Prevent select2 from flipping the dropdown upward

前端 未结 6 1723
难免孤独
难免孤独 2020-12-13 09:55

As per title, is there a way to force select2 to always create a dropdown instead of a drop-up?

There also appears to be some javascript that is either causing the f

相关标签:
6条回答
  • 2020-12-13 10:11

    I used to find an easier/faster solution for that:

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

    It's simple, you just change the .select2-dropdown--above to .select2-dropdown--below in the opening event (select2:open).

    It will only works under the event, and there could be many other ways to perform it just changing classes when the select is opened.

    ps. It won't work if you try to populate your select using jquery.

    0 讨论(0)
  • 2020-12-13 10:14

    Since modifying the source code is not an option and adding a hook to the select2:open event is not very elegant, especially when you have multiple select2 instances in the same page, I have written a small extension for the Select2 plugin.

    My implementation is inspired by a PR from the plugin's repository (https://github.com/select2/select2/pull/4618) that is not yet merged.

    Basically, the following code overrides the original plugin function that handles the dropdown positioning and adds a new option (dropdownPosition) to force the dropdown positioning above/below.

    The new dropdownPosition option can take the following values: - below - the dropdown is always displayed at the bottom of the input; - above - the dropdown is always displayed at the top of the input; - auto (default) - it uses the old behavior.

    Just insert the following code after select2.js file:

    (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);
    

    The initialize the plugin with as follows:

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

    Fiddle here: https://jsfiddle.net/byxj73ov/

    Github repository: https://github.com/andreivictor/select2-dropdownPosition


    UPDATE December 30, 2019

    Fiddle with the latest Select2 Version (v4.0.12): https://jsfiddle.net/g4maj9ox/

    0 讨论(0)
  • 2020-12-13 10:25

    Updated from [shanabus] answer

    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);
                }
            });
    
    0 讨论(0)
  • 2020-12-13 10:26

    Modifying the plugin is not preferred as you mention. I had a similar issue and couldn't find an way to use select2 options to force the dropdown to stay below. The solution I ended up with is the following:

    $("#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);
            }
        });
    

    This code determines if there is enough room to place the dropdown at the bottom and if not, creates it by adding margin-bottom to some element on the page. It then scrolls to just above the select2 so that the dropdown won't flip.

    0 讨论(0)
  • 2020-12-13 10:29

    You can just edit select2.js


    Where it says

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

    just change it to

    enoughRoomBelow = true,
    enoughRoomAbove = false,
    
    0 讨论(0)
  • 2020-12-13 10:34

    You can do it by overwriting CSS like so:

    .select-dropdown {
      position: static;
    }
    .select-dropdown .select-dropdown--above {
          margin-top: 336px;
    }
    
    0 讨论(0)
提交回复
热议问题