bootstrap drop-down menu auto dropup according to screen position?

前端 未结 7 716
無奈伤痛
無奈伤痛 2021-02-02 10:33

I was wondering if any of you has what I am asking for ready, to save me from the trouble. What I am looking for is for a dropdown menu to have the dropup class automatically ad

相关标签:
7条回答
  • 2021-02-02 11:08

    I found the answer from Zoltán Tamási to be a good starting point but not enough when the dropdown should appear within a div with a vertical scrollbar. I needed the height of that div (the scrollHeight variable in the code below) to figure out if the dropdown will take to much space at the bottom.

    jquery(document).on("show.bs.dropdown", ".dropdown", function () {
      var $ul = jquery(this).children(".dropdown-menu");
      var menuHeight = $ul.height()
      var buttonHeight = jquery(this).children(".dropdown-toggle").height();
      var scrollHeight = $ul.parent().offsetParent().height();
      var menuAnchorTop = $ul.parent().position().top + buttonHeight;
    
      // how much space would be left at the top
      var spaceUp = (menuAnchorTop - buttonHeight - menuHeight);
      // how much space would be left at the bottom
      var spaceDown = scrollHeight - (menuAnchorTop + menuHeight);
      // Switch to dropup if more space there
      if (spaceDown < 0 && (spaceUp >= 0 || spaceUp > spaceDown))
        jquery(this).addClass("dropup");
    }).on("hidden.bs.dropdown", ".dropdown", function() {
      // always reset after close
      jquery(this).removeClass("dropup");
    });
    
    0 讨论(0)
  • 2021-02-02 11:22

    I had performance issues with the provided answer on large pages.

    I've "improved" it by using getBoundingClientRect(), and adding a new class to specific .btn-group's that contain dropdows., eg. btn-group-dropup.

    Your mileage may vary.

    (function() {
      // require menu height + margin, otherwise convert to drop-up
      var dropUpMarginBottom = 100;
    
      function dropUp() {
        var windowHeight = $(window).height();
        $(".btn-group-dropup").each(function() {
          var dropDownMenuHeight, 
              rect = this.getBoundingClientRect();
          // only toggle menu's that are visible on the current page
          if (rect.top > windowHeight) {
            return;
          }
          // if you know height of menu - set on parent, eg. `data-menu="100"`
          dropDownMenuHeight = $(this).data('menu');
          if (dropDownMenuHeight == null) {
            dropDownMenuHeight = $(this).children('.dropdown-menu').height();
          }
          $(this).toggleClass("dropup", ((windowHeight - rect.bottom) < (dropDownMenuHeight + dropUpMarginBottom)) && (rect.top > dropDownMenuHeight));
        });
      };
    
      // bind to load & scroll - but debounce scroll with `underscorejs`
      $(window).bind({
        "resize scroll touchstart touchmove mousewheel": _.debounce(dropUp, 100),
        "load": dropUp
      });
    
    }).call(this);
    
    0 讨论(0)
  • 2021-02-02 11:22

    Edit 07 Jan 2019: tidied up the variable names and optimized the code just a bit.

    dropup = function() {
      $(".dropdown-toggle").each(function(){ 
        offsetTop=$(this).offset().top+$(this).height()-$(window).scrollTop();           
        offsetBottom=$(window).height()-$(this).height()-$(this).offset().top+$(window).scrollTop();
        ulHeight=$(this).parents('.btn-group').find('ul').height();
    
        if ((offsetBottom < ulHeight) && (offsetTop > ulHeight)) {
          parent.addClass('dropup');
        } else {
          parent.removeClass('dropup');
        }
      });
    } 
    
    $(window).on('load resize scroll', dropup);
    
    0 讨论(0)
  • 2021-02-02 11:22

    I know this is an old question, but it's high up on google results so here's another simple solution that worked for me. Adjust the 150 in the if statement for however much space your dropdown will need.

    var dropUp = function() {
        var windowHeight = $(window).innerHeight();
        var pageScroll = $('body').scrollTop();
    
        $( ".your-dropdown" ).each( function() {
            var offset = $( this ).offset().top;
            var space = windowHeight - ( offset - pageScroll );
    
            if( space < 150 ) {
                $( this ).addClass( "dropup" );
            } else  {
                $( this ).removeClass( "dropup" );
            }
        });
    }
    
    $(window).load(dropUp);
    $(window).bind('resize scroll mousewheel', dropUp);
    
    0 讨论(0)
  • 2021-02-02 11:26

    I have checked the performance and browser support (chrome,Firefox,Edge,IE 10 and above).

    I have covered both vertical and horizontal auto positioning.

    Code:

    (function () {
      var dd_obj = {
        jq_win :jQuery(window),
        jq_doc :jQuery(document),
      }
      function selfCalc(ele) {
        var $this = jQuery(ele),
            $dd_menu = $this.children(".dropdown-menu"), 
            ddOffset = $this.offset(),
            ddMenu_posTop = dd_obj.jq_win.outerHeight() - (($this.outerHeight() + ddOffset.top + $dd_menu.outerHeight()) - dd_obj.jq_doc.scrollTop());
            ddMenu_posRight = dd_obj.jq_win.outerWidth() - (($this.outerWidth() + ddOffset.left + $dd_menu.outerWidth()) - dd_obj.jq_doc.scrollLeft());
    
        (ddMenu_posTop <= 0) ? $this.addClass("dropup") : $this.removeClass("dropup"); 
        (ddMenu_posRight <= 0) ? $this.find('.dropdown-menu').addClass("dropdown-menu-right") : $this.find('.dropdown-menu').removeClass("dropdown-menu-right");
      }
      jQuery('body').on("shown.bs.dropdown", ".dropdown", function () {
        var self = this;
        selfCalc(self)
        dd_obj.jq_win.on('resize.custDd scroll.custDd mousewheel.custDd',function() {
          selfCalc(self)
        })
      }).on("hidden.bs.dropdown", ".dropdown", function() {
          // there is no need to keep the window event after dropdown has closed, 
          // still if we keep the event then there is no use
          dd_obj.jq_win.off('resize.custDd scroll.custDd mousewheel.custDd')
      });
    })();
    
    0 讨论(0)
  • 2021-02-02 11:28

    A bit late, but because is quite different from others, here is my solution for bootstrap 3. It applies globally to all dropdowns on the page, also those created or loaded dynamically.

    Note that I had to use the shown.bs.dropdown event because the dimensions are ready only at that point.

    $(document).on("shown.bs.dropdown", ".dropdown", function () {
        // calculate the required sizes, spaces
        var $ul = $(this).children(".dropdown-menu");
        var $button = $(this).children(".dropdown-toggle");
        var ulOffset = $ul.offset();
        // how much space would be left on the top if the dropdown opened that direction
        var spaceUp = (ulOffset.top - $button.height() - $ul.height()) - $(window).scrollTop();
        // how much space is left at the bottom
        var spaceDown = $(window).scrollTop() + $(window).height() - (ulOffset.top + $ul.height());
        // switch to dropup only if there is no space at the bottom AND there is space at the top, or there isn't either but it would be still better fit
        if (spaceDown < 0 && (spaceUp >= 0 || spaceUp > spaceDown))
          $(this).addClass("dropup");
    }).on("hidden.bs.dropdown", ".dropdown", function() {
        // always reset after close
        $(this).removeClass("dropup");
    });
    

    One could easily improve it with @PeterWone's app height wizardry if needed, currently this was working well enough for me.

    UPDATE

    Here is a fiddle representing this solution: http://jsfiddle.net/3s2efe9u/

    0 讨论(0)
提交回复
热议问题