Запретить select2 перевернуть всплывающее окно вверх

Как и в заголовке, существует ли способ заставить select2 всегда создавать выпадающее меню вместо выпадающего?

Также появляется некоторый javascript, который вызывает флип при прокрутке над выпадающим списком, добавляя новый класс CSS "select2-drop-above" или оба.

EDIT: я должен был указать, что я вытаскиваю библиотеку через select2-rails. Я надеюсь, что есть способ обойти это, что не включает в себя вытягивание всей select2 lib в себя и прямое редактирование файла select2.js.

Ответы

Ответ 1

Изменение плагина не является предпочтительным, как вы упомянули. У меня была аналогичная проблема, и я не смог найти способ использовать параметры select2, чтобы выровнять вниз. Решение, в результате которого я закончил, следующее:

$("#mySelect2").select2({ ...options... })
    .on('select2-open', function() {

        // however much room you determine you need to prevent jumping
        var requireHeight = 600;
        var viewportBottom = $(window).scrollTop() + $(window).height();

        // figure out if we need to make changes
        if (viewportBottom < requireHeight) 
        {           
            // determine how much padding we should add (via marginBottom)
            var marginBottom = requireHeight - viewportBottom;

            // adding padding so we can scroll down
            $(".aLwrElmntOrCntntWrppr").css("marginBottom", marginBottom + "px");

            // animate to just above the select2, now with plenty of room below
            $('html, body').animate({
                scrollTop: $("#mySelect2").offset().top - 10
            }, 1000);
        }
    });

Этот код определяет, есть ли достаточно места для размещения выпадающего списка внизу, а если нет, создается его, добавляя край поля к некоторому элементу на странице. Затем он прокручивается чуть выше select2, так что выпадающее меню не будет переворачиваться.

Ответ 2

Вы можете просто отредактировать select2.js


Где он говорит

enoughRoomBelow = dropTop + dropHeight <= viewportBottom,
enoughRoomAbove = (offset.top - dropHeight) >= this.body().scrollTop(),

просто измените его на

enoughRoomBelow = true,
enoughRoomAbove = false,

Ответ 3

Поскольку изменение исходного кода не является опцией, а добавление перехватчика к select2:open не очень элегантно, особенно если у вас есть несколько экземпляров select2 на одной странице, я написал небольшое расширение для плагина Select2.

Моя реализация вдохновлена пиаром из репозитория плагинов (https://github.com/select2/select2/pull/4618), который еще не объединен.

По сути, следующий код переопределяет исходную функцию плагина, которая обрабатывает позиционирование раскрывающегося списка, и добавляет новую опцию (dropdownPosition), чтобы принудительно установить раскрывающееся позиционирование выше/ниже.

Новая опция dropdownPosition может принимать следующие значения: - below - раскрывающийся список всегда отображается внизу ввода; - above - раскрывающийся список всегда отображается в верхней части ввода; - auto (по умолчанию) - используется старое поведение.

Просто вставьте следующий код после файла select2.js:

(function($) {

  var Defaults = $.fn.select2.amd.require('select2/defaults');

  $.extend(Defaults.defaults, {
    dropdownPosition: 'auto'
  });

  var AttachBody = $.fn.select2.amd.require('select2/dropdown/attachBody');

  var _positionDropdown = AttachBody.prototype._positionDropdown;

  AttachBody.prototype._positionDropdown = function() {

    var $window = $(window);

    var isCurrentlyAbove = this.$dropdown.hasClass('select2-dropdown--above');
    var isCurrentlyBelow = this.$dropdown.hasClass('select2-dropdown--below');

    var newDirection = null;

    var offset = this.$container.offset();

    offset.bottom = offset.top + this.$container.outerHeight(false);

    var container = {
        height: this.$container.outerHeight(false)
    };

    container.top = offset.top;
    container.bottom = offset.top + container.height;

    var dropdown = {
      height: this.$dropdown.outerHeight(false)
    };

    var viewport = {
      top: $window.scrollTop(),
      bottom: $window.scrollTop() + $window.height()
    };

    var enoughRoomAbove = viewport.top < (offset.top - dropdown.height);
    var enoughRoomBelow = viewport.bottom > (offset.bottom + dropdown.height);

    var css = {
      left: offset.left,
      top: container.bottom
    };

    // Determine what the parent element is to use for calciulating the offset
    var $offsetParent = this.$dropdownParent;

    // For statically positoned elements, we need to get the element
    // that is determining the offset
    if ($offsetParent.css('position') === 'static') {
      $offsetParent = $offsetParent.offsetParent();
    }

    var parentOffset = $offsetParent.offset();

    css.top -= parentOffset.top
    css.left -= parentOffset.left;

    var dropdownPositionOption = this.options.get('dropdownPosition');

    if (dropdownPositionOption === 'above' || dropdownPositionOption === 'below') {
      newDirection = dropdownPositionOption;
    } else {

      if (!isCurrentlyAbove && !isCurrentlyBelow) {
        newDirection = 'below';
      }

      if (!enoughRoomBelow && enoughRoomAbove && !isCurrentlyAbove) {
        newDirection = 'above';
      } else if (!enoughRoomAbove && enoughRoomBelow && isCurrentlyAbove) {
        newDirection = 'below';
      }

    }

    if (newDirection == 'above' ||
    (isCurrentlyAbove && newDirection !== 'below')) {
        css.top = container.top - parentOffset.top - dropdown.height;
    }

    if (newDirection != null) {
      this.$dropdown
        .removeClass('select2-dropdown--below select2-dropdown--above')
        .addClass('select2-dropdown--' + newDirection);
      this.$container
        .removeClass('select2-container--below select2-container--above')
        .addClass('select2-container--' + newDirection);
    }

    this.$dropdownContainer.css(css);

  };

})(window.jQuery);

Инициализируйте плагин следующим образом:

$(document).ready(function() {
  $(".select-el").select2({
    dropdownPosition: 'below'
  });
});

Скрипка здесь: https://jsfiddle.net/byxj73ov/

Репозиторий Github: https://github.com/andreivictor/select2-dropdownPosition

Ответ 4

Вы можете сделать это, перезаписав CSS так:

.select-dropdown {
  position: static;
}
.select-dropdown .select-dropdown--above {
      margin-top: 336px;
}

Ответ 5

Я использовал для этого более легкое/быстрое решение:

        $("select").select2({

            // Options

        }).on('select2:open',function(){

            $('.select2-dropdown--above').attr('id','fix');
            $('#fix').removeClass('select2-dropdown--above');
            $('#fix').addClass('select2-dropdown--below');

        });

Просто, вы просто измените .select2-dropdown - выше на .select2-dropdown - ниже в открывшемся событии (Select2: открыт).

Он будет работать только под событием, и может быть много других способов выполнить его, просто меняя классы, когда открывается выбор.

пс. Он не будет работать, если вы попытаетесь заполнить свой выбор, используя jquery.

Ответ 6

Обновлено из ответа [shanabus]

jQuery("#line_item").select2({
            formatResult: Invoice.formatLineItem
        })
        .on('select2-open', function() {
            // however much room you determine you need to prevent jumping
            var requireHeight = $("#select2-drop").height()+10;
            var viewportBottom = $(window).height() - $("#line_item").offset().top;

            // figure out if we need to make changes
            if (viewportBottom < requireHeight)
            {
                // determine how much padding we should add (via marginBottom)
                var marginBottom = requireHeight - viewportBottom;

                // adding padding so we can scroll down
                $(".aLwrElmntOrCntntWrppr").css("marginBottom", marginBottom + "px");

                // animate to just above the select2, now with plenty of room below
                $('html, body').animate({
                    scrollTop: $("#select2-drop").offset().top - 10
                }, 1000);
            }
        });

Ответ 7

.select2-dropdown--above { bottom: auto !important; }