【问题标题】:Prevent select2 from flipping the dropdown upward防止 select2 向上翻转下拉列表
【发布时间】:2013-11-27 19:32:53
【问题描述】:

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

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

编辑:我应该指定我通过 select2-rails 拉入库。我希望有一种方法可以解决这个问题,不涉及将整个 select2 库拉入我自己并直接编辑 select2.js 文件。

【问题讨论】:

    标签: javascript jquery css jquery-select2 select2-rails


    【解决方案1】:

    由于修改源代码不是一个选项并且给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


    更新 2019 年 12 月 30 日

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

    【讨论】:

    • 谢谢!正在使用 select2 4.0 版!
    • @KaranSharma,我用 select2 版本 4.0.7 创建了一个小提琴:jsfiddle.net/51sxfa0L,它似乎运行良好。您能否提供更多详细信息或提供您遇到的错误的最小可重现示例?
    • 啊,我错了,我添加了两个 select2 库。一个来自我现在发现的 vendor.min.js,另一个来自 cdn 链接。我再次尝试了 jsfiddle 并发现了与之相关的问题。感谢您的建议:) 我留下了希望。
    • 非常感谢,这是解决此问题的唯一干净解决方案。这确实应该是 select2 的一部分...
    • 谢谢!正在使用 select2 4.0 版!
    【解决方案2】:

    你可以编辑select2.js


    哪里写的

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

    改成

    enoughRoomBelow = true,
    enoughRoomAbove = false,
    

    【讨论】:

    • 但这意味着当您将来要升级 select2 时,请记住这一点。所以要小心这个。
    • 你不应该修改源脚本,否则你会遇到问题。
    • 不幸的是,这是为我修复它的唯一方法(使用 ajax 查询和需要关闭选择)。已创建 PR 来解决此问题,但尚未合并:github.com/select2/select2/pull/4560
    • 对我不起作用 :( 反之亦然,但不是这个。可能是什么原因?
    • 解决方案有效,但我强烈同意@cmfolio。相反,有人应该使用enoughRoomAbove: true/false 作为插件配置选项提出拉取请求。让我抓住机会:)。谢谢。
    【解决方案3】:

    你可以像这样覆盖 CSS:

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

    【讨论】:

    • 用 4.0 试试这个,完美运行,最简单的解决方案!
    【解决方案4】:

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

            $("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--above改为.select2-dropdown--below (选择2:打开)。

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

    ps。如果您尝试使用 jquery 填充您的选择,它将无法正常工作。

    【讨论】:

    • 不起作用,因为 select2 添加了一些具有最高值的内联 css
    • 不知道如何在相关元素上使用内联顶部值
    • 我在尝试做这样的事情时摔断了手指,我不确定它是如何工作的,没有理论。我通过尝试实现了它。
    • 我试过你的不是理论。也许看看最新版本。它在打开时添加一个顶部内联值;所以仅仅切换课程是不够的
    【解决方案5】:

    正如您所说,修改插件不是首选。我遇到了类似的问题,找不到使用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);
            }
        });
    

    此代码确定是否有足够的空间将下拉菜单放置在底部,如果没有,则通过向页面上的某些元素添加 margin-bottom 来创建它。然后它会滚动到 select2 的正上方,这样下拉菜单就不会翻转。

    【讨论】:

    • 我不确定你的意思,它在 Chrome 中对我来说没问题
    • 我用的版本是4.0,这个还是3。反正我已经修好了。 jsfiddle.net/brunodd/jEADR/2009
    【解决方案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);
                }
            });
    

    【讨论】:

    • 什么是 $(".aLwrElmntOrCntntWrppr")
    • aLwrElmntOrCntntWrppr 是一个可怕、可怕的名字。
    • @Jaskey 我猜意思是“较低的元素或内容包装器”
    【解决方案7】:

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

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

    【讨论】:

      猜你喜欢
      • 2020-06-18
      • 2015-08-17
      • 2013-01-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-02
      • 1970-01-01
      • 2015-10-19
      相关资源
      最近更新 更多