防止 select2 向上翻转下拉列表

Prevent select2 from flipping the dropdown upward

本文关键字:翻转 下拉列表 select2 防止      更新时间:2023-09-26

根据标题,有没有办法强制 select2 始终创建下拉列表而不是下拉列表?

似乎还有一些javascript在下拉菜单上方滚动时导致翻转,添加新的CSS类"select2-drop-above",或两者兼而有之。

编辑:我应该指定我正在通过select2导轨拉入库。我希望有一种方法可以解决这个问题,而不是在我自己身上拉取整个 select2 库并直接编辑 select2.js 文件。

由于修改源代码不是一种选择,并且向select2:open事件添加钩子不是很优雅,尤其是当您在同一页面中有多个 select2 实例时,我为 Select2 插件编写了一个小扩展。

我的实现受到插件存储库 (https://github.com/select2/select2/pull/4618) 中尚未合并的 PR 的启发。

基本上,以下代码覆盖了处理下拉列表定位的原始插件函数,并添加了一个新选项(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


更新 十二月 30, 2019

弄最新的 Select2 版本 (v4.0.12):https://jsfiddle.net/g4maj9ox/

您可以编辑select2.js


它说的地方

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

只需将其更改为

enoughRoomBelow = true,
enoughRoomAbove = false,

你可以通过覆盖CSS来实现,如下所示:

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

我曾经为此找到一个更简单/更快的解决方案:

        $("select").select2({
            // Options
        }).on('select2:open',function(){
            $('.select2-dropdown--above').attr('id','fix');
            $('#fix').removeClass('select2-dropdown--above');
            $('#fix').addClass('select2-dropdown--below');
        });

这很简单,您只需在打开事件(select2:open)中将上面的.select2-下拉列表更改为下面的.select2-dropdown

只能在事件下工作,并且可能还有许多其他方法可以执行它,只是在打开选择时更改类。

附言。如果您尝试使用 jquery 填充您的选择,它将不起作用。

正如你提到的,修改插件不是首选。我遇到了类似的问题,找不到使用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 的正上方,以便下拉列表不会翻转。

更新自 [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);
            }
        });

看起来在 CSS 中强制自动制作底部值也可以解决问题。我使用了以下代码,它对我很好用。

.my_select_picker_trigger.dropup .dropdown-menu {
    bottom: auto !important;
}