最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Replacing Bootstrap Dropdown with Dropup (Different activity on two near identical implementations) - Stack Overflo

programmeradmin2浏览0评论

I'm working on a project over at github pages, which I replace a bootstrap .dropdown with .dropup if the div's overflow-y: scroll will cause the dropdown menu to be cutoff / overflow. You can see the function working properly at this jsfiddle. Notice if you click on the ellipsis icon to the right on the top rows, it will drop down, if you click on the icon on the bottom rows, it will drop up.

Now, my actual implementation (github page), the code is exactly the same (below), but it wants to replace all .dropdown classes with .dropup when opened, including the top-most row which gets cut off, seen in the photo below. I've been struggling with this for a week and can't quite figure it out. I've tried a few different things that I thought fixed it but ended up just being a hack and didn't work on mobile, or replaced some but not all etc.

Here is the Javascript / jQuery I'm using, which can be seen in the jsfiddle and my github source here.

$(document).on("shown.bs.dropdown", ".dropdown", function () {
  // calculate the required sizes, spaces
  var $ul = $(this).children(".dropdown-menu");
  var $button = $(this).children(".song-menu");
  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()) - $('#playlist').scrollTop();
  // how much space is left at the bottom
  var spaceDown = $('#playlist').scrollTop() + $('#playlist').height() - ((ulOffset.top + 10) + $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");
});

Edit: To clear up any confusion, here's an example of the behavior without my added .dropup function. jsfiddle Notice when you click the last menu item, it opens the menu but requires scrolling. I specifically want to remove the .dropdown class and add .dropup in this case, so no scrolling is required.

I'm working on a project over at github pages, which I replace a bootstrap .dropdown with .dropup if the div's overflow-y: scroll will cause the dropdown menu to be cutoff / overflow. You can see the function working properly at this jsfiddle. Notice if you click on the ellipsis icon to the right on the top rows, it will drop down, if you click on the icon on the bottom rows, it will drop up.

Now, my actual implementation (github page), the code is exactly the same (below), but it wants to replace all .dropdown classes with .dropup when opened, including the top-most row which gets cut off, seen in the photo below. I've been struggling with this for a week and can't quite figure it out. I've tried a few different things that I thought fixed it but ended up just being a hack and didn't work on mobile, or replaced some but not all etc.

Here is the Javascript / jQuery I'm using, which can be seen in the jsfiddle and my github source here.

$(document).on("shown.bs.dropdown", ".dropdown", function () {
  // calculate the required sizes, spaces
  var $ul = $(this).children(".dropdown-menu");
  var $button = $(this).children(".song-menu");
  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()) - $('#playlist').scrollTop();
  // how much space is left at the bottom
  var spaceDown = $('#playlist').scrollTop() + $('#playlist').height() - ((ulOffset.top + 10) + $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");
});

Edit: To clear up any confusion, here's an example of the behavior without my added .dropup function. jsfiddle Notice when you click the last menu item, it opens the menu but requires scrolling. I specifically want to remove the .dropdown class and add .dropup in this case, so no scrolling is required.

Share Improve this question edited Sep 3, 2017 at 11:44 Cœur 38.8k25 gold badges206 silver badges279 bronze badges asked Oct 19, 2016 at 4:33 dcclassicsdcclassics 8961 gold badge12 silver badges39 bronze badges 3
  • Maybe make the .dropup class !important – user1752532 Commented Oct 19, 2016 at 4:35
  • @gerdi I don't think that will fix the issue I'm having. Specifically because as the image shows it is only adding the dropup class. – dcclassics Commented Oct 19, 2016 at 12:53
  • try change z-index of element – user6079755 Commented Oct 21, 2016 at 19:08
Add a ment  | 

4 Answers 4

Reset to default 4

It took some basic math, but I managed to figure out what you desired to do. This code changes the bootstrap classes between dropup and dropdown depending on the room available for a normal dropdown.

I calculated this by detracting the height of the button, dropdownmenu and how far the button was scrolled down in the scrollContainer from the height of the scrollContainer. I got the value how much the div was scrolled down by using the buttons offset and detracting the offset from the scrollContainer.

Here is my jQuery (I selected the .playlist class because this was attached to your scrollContainer, but you should replace it by an id or select it by other means):

$(".dropdown, .dropup").click(function(){
 var dropdownClassCheck = $(this).hasClass('dropdown');
 var buttonOffset = $(this).offset().top;
 var scrollboxOffset = $('.playlist').offset().top;
 var buttonHeight = $(this).height();
 var scrollBoxHeight = $('.playlist').height();
 var dropDownButtonHeight = $(this).children('ul').height();
 dropdownSpaceCheck = scrollBoxHeight>buttonOffset-scrollboxOffset+buttonHeight+dropDownButtonHeight; 
 if(dropdownClassCheck && !dropdownSpaceCheck){
  $(this).removeClass('dropdown').addClass('dropup');
 }
 else if(!dropdownClassCheck && dropdownSpaceCheck){
        $(this).removeClass('dropup').addClass('dropdown');
 }
});

A working JSFiddle

Let me know if there are parts of the code that could be improved/done easier or if there are any problems with my solution.

I have not thoroughly checked, but .scrollTop() is probably why the code fails when bined with other elements in the DOM, so here is a solution without it:

function checkHeights(){
  // LOOP through each dropdown
  $('.dropdown,.dropup').each(function(index,element){
    var $dropDown = $(element),
        $dropDownMenu = $dropDown.find('.dropdown-menu'),
        dropDownTop = $dropDown.offset().top,
        visibleHeight = $dropDown.height(),
        hiddenHeight = $dropDownMenu.height(),
        ddTop = dropDownTop - hiddenHeight,
        ddBottom = dropDownTop + visibleHeight + hiddenHeight;
    
    // LOOP through all parents
    $dropDown.parents().each(function(ix,el){
      var $el = $(el);
    
      // CHECK if any of them have overflow property set
      if( $el.css('overflow') !== 'visible' ){
        var limitTop = $el.offset().top,
            limitBottom = limitTop + $el.height();
      
        // CHECK if parent is better fit when dropped upside
        if( limitBottom < ddBottom && ( ddTop - limitTop ) > ( limitBottom - ddBottom ) )
          $dropDown.removeClass('dropdown').addClass('dropup');
        else
          $dropDown.removeClass('dropup').addClass('dropdown');
      
        // BREAK LOOP
        return false;
      }
    });
  });
}

$(document).ready(function() {
  checkHeights();
  $('.playlist').scroll(checkHeights);
});

JS Fiddle here.

This one does not require any class or id given to it except for dropdown,dropdown-menu, and dropup (all of which are Bootstrap defaults) and would work fine even if there are multiple playlists on page.


UPDATE

The code is modified and wrapped in a function in order to allow being called when scroll event fires.

I think that the problem it's that you have a big header, and the jsFiddle don't. So ulOffset.top it's always big, and spaceDown is always negative

Replace parent div.dropdown with div.dropup.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论