Bootstrap: dropdown menu in a .table-responsive

Created on 11 Oct 2013  路  47Comments  路  Source: twbs/bootstrap

If you have an Dropdown (e.g. from a Button) inside a table-respnsive object than the dropdown is not display correct on small devices (or small browser Windows).

here the example:

<div class="table-responsive">
    <table class="table table-striped">
        <thead>
            <tr>
                <th>Name</th>
                <th style="width: 1px;"></th>
            </tr>
        </thead>
        <tbody>
            <tr>
            <td>Name 1</td>
            <td>
                <div class="btn-group">
                        <button class="btn btn-primary dropdown-toggle" href="#" data-toggle="dropdown">
                        <span class='glyphicon glyphicon-plus'></span> <span class="caret"></span>
                        </button>
                        <ul class="dropdown-menu stay-open pull-right" role="menu" style="padding: 15px; min-width: 300px;">
                        <li>First menu item</li>    
                        <li>Second menu item</li>   
                        </ul>
                </div>
            </td>
            </tr>
            <tr>
            <td>Name 2</td>
            <td>
                <div class="btn-group">
                        <button class="btn btn-primary dropdown-toggle" href="#" data-toggle="dropdown">
                        <span class='glyphicon glyphicon-plus'></span> <span class="caret"></span>
                        </button>
                        <ul class="dropdown-menu stay-open pull-right" role="menu" style="padding: 15px; min-width: 300px;">
                        <li>First menu item</li>    
                        <li>Second menu item</li>   
                        </ul>
                </div>
            </td>
            </tr>
            </tbody>
    </table>
</div>

If you have a big screen, all looks good.
If you make the screen very small than the dropdown is not displayed correctly:
image

By the way:
Tested on Chrome 29 and Bootstrap 3

confirmed css

Most helpful comment

@leocaseiro for me the following gave a useable result:

@media (max-width: 767px) {
    .table-responsive .dropdown-menu {
        position: static !important;
    }
}
@media (min-width: 768px) {
    .table-responsive {
        overflow: visible;
    }
}

It includes the dropdown inside the table for mobile devices and makes is simply overflow on desktop devices

All 47 comments

Also have same issue. Not yet figured easy way to fix it

It is possible by overflow-x: visible; and overflow-y: visible;.
See also http://jsbin.com/eKUBAXE/1/edit

However, I don't know how work these definitions doing.

// in navbar.less 

// Responsive tables
//
// Wrap your tables in `.table-responsive` and we'll make them mobile friendly
// by enabling horizontal scrolling. Only applies <768px. Everything above that
// will display normally.

@media (max-width: @screen-xs-max) {
  .table-responsive {
    width: 100%;
    margin-bottom: (@line-height-computed * 0.75);
    overflow-y: hidden;
    overflow-x: scroll;

I have the same problem. This is not a solution because the table is not scrollable on small devices (or small browser windows) and .table-responsive loses all interest...

@nvrch You're right. I'm going to think about scrollable ways on this issue.

Well, I gave it some thought, but the example here is a small table that you wouldn't even need the wrapper on. So see if this works, it did for me.

First you add a class to the table "table-drop"

 <table class="table table-drop table-striped">

Then add some css after the table-responsive classes:

    .table-responsive.res-drop {height:500px!important;}

Inside your media query where the .table-responsive is

500px is the guestimated height of the drop downs.

Then you add some jQuery:

  $(document).ready(function() {

   $(".table-drop .dropdown-toggle").click(function () {
        $(this).parents(".table-responsive").toggleClass("res-drop");
   });

 });

If the user does not use the toggle to close the menu and just clicks off, and then clicks the other one, it won't toggle. I don't know a work around except to turn off the ability to click anywhere to close a menu. Perhaps it can be isolated for just not turning off inside a table-responsive div or create a new dropdown script (fork it for tables).

I usually avoid doing things that are not in the docs with examples, I would try it first then roll my own solution based on the situation. I would never consider using a table with a dropdown on a mobile device. I would use a php class to create content for desktop and if it were a touch device at a certain size that needs a different view, create a list item or some other mark up so that this stuff can be avoided. What the table-responsive does is just wrap it in an overflow div. I've been doing that for about two years to address tables on responsive sites, and anything that is added dynamically to a table with a wrapper on it, will behave like this, you won't be able to see the stuff without scrolling and since the dropdown menu closes when tap off, on a touch device this is not a good plan.

There are other options for responsive tables:

http://css-tricks.com/responsive-data-table-roundup/

In a real situation, if the table at all smaller sizes from the 767px and below point is taller than the height of your dropdowns, then only on the last several rows (depending on the height) would you need this, but it won't mess up stuff it you do it on all, except that the height will toggle while the user is messing with the dropdown content.

I think positioning via JavaScript is the good solution. Specify container 'body' like tooltips plugin to position the element in the body at another place in the DOM.

_nvrch_ agree, the only one way to solve this is to pull dropdown out of current position and stick to the body with absolute positioning.

The difference between this and a tooltip is that one is generated content and the other is not. For the time being, we won't be supporting dropdowns within responsive tables (or anything else with an overflow on it).

This simple line of jQuery did the trick for me:

$('.table-responsive tbody tr').slice(-2).find('.dropdown').addClass('dropup');

I'm 100% sure that this is not a real solution but solves the usability problem quickly.
Hope this helps somebody. ;)

Could you please someone check if the solution using position:static works?
https://github.com/YaleSTC/reservations/issues/724#issuecomment-94633338

@leocaseiro for me the following gave a useable result:

@media (max-width: 767px) {
    .table-responsive .dropdown-menu {
        position: static !important;
    }
}
@media (min-width: 768px) {
    .table-responsive {
        overflow: visible;
    }
}

It includes the dropdown inside the table for mobile devices and makes is simply overflow on desktop devices

Nice one @kaystrobach. In this case is completely responsive and mobile-first :D

2015-05-26_06-39-24
2015-05-26_06-39-12

Here is my solution, when .dropdown menu exceed the height of .table-responsive, I set it to .dropup :P

This way works if your drop down menu height is more than table.

https://gist.github.com/wellwind/8ca3a4d1568f18619574

$(document).ready(function(){
    $('.dropdown-menu').parent().on('show.bs.dropdown', function () {
        var parentResponsiveTable = $(this).parents('.table-responsive');
        var parentTarget = $(parentResponsiveTable).first().hasClass('table-responsive') ? $(parentResponsiveTable) : $(window);
        var parentTop = $(parentResponsiveTable).first().hasClass('table-responsive') ? $(parentResponsiveTable).offset().top : 0;
        var parentLeft = $(parentResponsiveTable).first().hasClass('table-responsive') ? $(parentResponsiveTable).offset().left : 0;

        var dropdownMenu = $(this).children('.dropdown-menu').first();

        if (!$(this).hasClass('dropdown') && !$(this).hasClass('dropup')) {
            $(this).addClass('dropdown');
        }
        $(this).attr('olddrop', $(this).hasClass('dropup') ? 'dropup' : 'dropdown');
        $(this).children('.dropdown-menu').each(function () {
            $(this).attr('olddrop-pull', $(this).hasClass('dropdown-menu-right') ? 'dropdown-menu-right' : '');
        });

        if ($(this).hasClass('dropdown')) {
            if ($(this).offset().top + $(this).height() + $(dropdownMenu).height() + 10 >= parentTop + $(parentTarget).height()) {
                $(this).removeClass('dropdown');
                $(this).addClass('dropup');
            }
        } else if ($(this).hasClass('dropup')) {
            if ($(this).offset().top - $(dropdownMenu).height() - 10 <= parentTop) {
                $(this).removeClass('dropup');
                $(this).addClass('dropdown');
            }
        }

        if ($(this).offset().left + $(dropdownMenu).width() >= parentLeft + $(parentTarget).width()) {
            $(this).children('.dropdown-menu').addClass('dropdown-menu-right');
        }
    });

    $('.dropdown-menu').parent().on('hide.bs.dropdown', function () {
        if ($(this).attr('olddrop') != '') {
            $(this).removeClass('dropup dropdown');
            $(this).addClass($(this).attr('olddrop'));
            $(this).attr('olddrop', '');
        }

        $(this).children('.dropdown-menu').each(function () {
            $(this).removeClass('dropdown-menu-right');
            $(this).addClass($(this).attr('olddrop-pull'));
        });
    });
});

mhmm sadly not reliable, fails, if you have just one row in the table, but 10 in them menu :( which can occur, if you filter data sets

imho the best option would be to move the dropdown directly into the body and then simply use absolute positioning ... this way we will not have problems with overlays :(

move the dropdown directly into the body and then simply use absolute positioning

however, doing that will severely mess up focus order (for keyboard users - yes, even on small screen devices like phones/tablets, there are users with paired bluetooth keyboard - and assistive technology users) unless you add extra code to explicitly set/unset the focus to the correct place

mhmm right ...

Still believe the best solution would be only with CSS:

@media (max-width: 767px) {
  .table-responsive .dropdown-menu,
  .table-responsive .dropdown-toggle {
        position: static !important;
  }
}

@media (min-width: 768px) {
    .table-responsive {
        overflow: visible;
    }
}

position:static for dropdown-menu, datepicker, timepicker (parent OR parents) inside .table-responsive

Im currently running into the same exact issue. Weird thing is it only seems to happen on safari on iphones. Mobile chrome and mobile firefox is fine.

This happens on larger devices as well. We ran into this today on Chrome _desktop_; if the drop-down menu is close enough to the bottom edge of the table (i.e.in the last row), it will popup cropped, especially if it has many links in it:

table-responsive dropdown

We solved this issue here at work by applying a .dropup class to the dropdown when the dropdown is close to the bottom of a table.

@jgw96 That workaround fixes the last row problem, but it will not help if the menu is taller than the table. For example, a table with a single row containing a very long dropdown menu. In this case neither dropdown nor dropup will work, as there is just not enough space.

@leocaseiro seems to have the best workaround for now for table-responsive.
If only there is an auto drop-up implemented. If there is not enough height for table, we should set either:

  1. .table-responsive {
    overflow: visible;
    } or,
  2. make the fixed the height of dropdown menu and make it scroll

I show it on the left side:

@media only screen and (min-width: 562px) {
.table-responsive .dropdown-menu {
    left: -160px !important;
    top: -120% !important;
}
}

my 2垄 quick global fix:

    // drop down in responsive table
    (function () {
        $('.table-responsive').on('shown.bs.dropdown', function (e) {
            var $table = $(this),
                $menu = $(e.target).find('.dropdown-menu'),
                tableOffsetHeight = $table.offset().top + $table.height(),
                menuOffsetHeight = $menu.offset().top + $menu.outerHeight(true);

            if (menuOffsetHeight > tableOffsetHeight)
                $table.css("padding-bottom", menuOffsetHeight - tableOffsetHeight);
        });

        $('.table-responsive').on('hide.bs.dropdown', function () {
            $(this).css("padding-bottom", 0);
        })
    })();

Explications:
When a dropdown-menu inside a '.table-responsive' is shown, it calculate the height of the table and expand it (with padding) to match the height required to display the menu. The menu can be any size.

In my case, this is not the table that has the '.table-responsive' class, it's a wrapping div:

<div class="table-responsive" style="overflow:auto;">
    <table class="table table-hover table-bordered table-condensed server-sort">

So the $table var in the script is actually a div! (just to be clear... or not) :)

Note: I wrap it in a function so my IDE can collapse function ;) but it's not mandatory!

@baltazarqc Slow clap, my good sir. That worked. But, as my body background color was grey, and the div of my table was white, it's not aesthetically pleasing to see the div go up and down. Nevertheless, I give my thanks.

I think, http://stackoverflow.com/a/29763143/5397119 is the best solution for this issue.

@sergio-ivanuzzo, for me that was not the best solution, because it meant I had to scroll to see the menu... but hey, if you're happy, I am too! :+1:

@baltazarqc make sure you test on a screen smaller than 767px.
This solution don't need scroll to see your menu:
screen shot 2016-02-11 at 9 27 44 am

Similar to other solutions ... but keeps default bootstrap behavior except only when dropdown is open. This way you never alter the appearance of your table when dropdown menu is closed or open ... and still maintain horizontal scrolling in mobile and accessibility.

.table-dropdown-visible {
    overflow: visible !important;
}
$(document).ready(function() {
    var milliseconds = 100;
    $('.table .dropdown').on('show.bs.dropdown', function () {
        $(this).closest('.table-responsive').addClass('table-dropdown-visible');
    }).on('hidden.bs.dropdown', function () {
        var $that = $(this);
        setTimeout(function() { // safari fix
            $that.closest('.table-responsive').removeClass('table-dropdown-visible');
        }, milliseconds);
    });
});

Hi,
Quick solutions just use bootstrap-select by silviomoreto:
https://silviomoreto.github.io/bootstrap-select/examples/#container

This you can set container to body.

This is what worked for me (combination of several solutions above including @kaystrobach and @julianmontagna )

`
@media (max-width: 767px) {
.table-responsive .btn-group {
margin-left: 1px;
}
}

@media (min-width: 768px) {
.table-responsive {
overflow: visible;
}
}
`

^ the margin doesn't really do anything and can be replaced with a CSS property that would not change the look and feel drastically. Make it anything you want if you have margins set for your drowndown.

`
function btnDropUpDown(){
if( $('.table-responsive .btn-group').css('marginLeft') == '1px' ){
$('.table-responsive .btn-group').slice(-3).not('.dropup').addClass('dropup');
return;
}

$('.table-responsive .btn-group.dropup').removeClass('dropup');

}
`

$(window).resize(btnDropUpDown);

btnDropUpDown();

^ Get "n" number of btnGroup items at the bottom and change them to a drop up. If the user is resizing, then change them back to dropdowns.

This has room for improvement.

@baltazarqc you have the best solution so far. Thanks a lot!

I just improved it a little bit...

jQuery.fn.hasHScrollBar = function(){
  return this.get(0).scrollWidth > this.innerWidth();
}

$('.table-responsive .dropdown').on('show.bs.dropdown', function (e) {
  var $table = $(this).closest('.table-responsive');
  if(!$table.hasHScrollBar()){
    $('.table-responsive').css("overflow", "visible");
  }
});

$('.table-responsive .dropdown').on('shown.bs.dropdown', function (e) {
  var $table = $(this).closest('.table-responsive');

  if($table.hasHScrollBar()){
    var $menu = $(this).find('.dropdown-menu'),
    tableOffsetHeight = $table.offset().top + $table.height(),
    menuOffsetHeight = $menu.offset().top + $menu.outerHeight(true);

    if (menuOffsetHeight > tableOffsetHeight)
      $table.css("padding-bottom", menuOffsetHeight - tableOffsetHeight + 15);
  }

});

$('.table-responsive .dropdown').on('hide.bs.dropdown', function () {
  $(this).closest('.table-responsive').css({"padding-bottom":"", "overflow":""});
})

If we haven't a horizontal scroll bar, we do not need to padding bottom, so overflow visible seems to be a best solution.

Nice @llins! I love how you modified this! ;)

@media (min-width: 768px) {
    .table-responsive {
        overflow: visible;
    }
}

This is not good if the table may need to scroll even on bigger devices

@baltazarqc & @llins, great work! Some more improvments:

$('.table-responsive').on('shown.bs.dropdown', function (e) {
    var t = $(this), 
        m = $(e.target).find('.dropdown-menu'),
        tb = t.offset().top + t.height(),
        mb = m.offset().top + m.outerHeight(true),
        d = 20; // Space for shadow + scrollbar.   
    if (t[0].scrollWidth > t.innerWidth()) {
        if (mb + d > tb) {
            t.css('padding-bottom', ((mb + d) - tb)); 
        }
    } else {
        t.css('overflow', 'visible');
    }
}).on('hidden.bs.dropdown', function () {
    $(this).css({'padding-bottom': '', 'overflow': ''});
});

@simon21587 It works perfectly

Another solution working on small screens: https://www.npmjs.com/package/bootstrap-responsive-table-dropdown

@simon21587 working like a charm, thank you so much. Bets solution until we get a proper fix (in BS4 ?)

This is still an issue in the Bootstrap 4.0.0 release. Is there any official comment on whether this is going to be fixed? I found lot of "closed" cases, but no official fix/solution.

There is an official solution in the dropdown documentation, you can specify the "boundary" option as: viewport or window for work inside table with overflow

try this

`
$('.table-responsive').on('show.bs.dropdown', function (e) {
$(e.relatedTarget).next('div[aria-labelledby="dropdownMenuButton"]').appendTo("body");
});

$('body').on('hide.bs.dropdown', function (e) {
$(this).find('div[aria-labelledby="dropdownMenuButton"]').appendTo($(e.relatedTarget).parent());
});
`

as @claudioalmeiida mentioned, for me adding data-boundary="viewport" to the button that toggles the dropdown (the one with the class "dropdown-toggle"), solved it. See https://getbootstrap.com/docs/4.1/components/dropdowns/#options

my 2垄 quick global fix:

    // drop down in responsive table
    (function () {
        $('.table-responsive').on('shown.bs.dropdown', function (e) {
            var $table = $(this),
                $menu = $(e.target).find('.dropdown-menu'),
                tableOffsetHeight = $table.offset().top + $table.height(),
                menuOffsetHeight = $menu.offset().top + $menu.outerHeight(true);

            if (menuOffsetHeight > tableOffsetHeight)
                $table.css("padding-bottom", menuOffsetHeight - tableOffsetHeight);
        });

        $('.table-responsive').on('hide.bs.dropdown', function () {
            $(this).css("padding-bottom", 0);
        })
    })();

Explications:
When a dropdown-menu inside a '.table-responsive' is shown, it calculate the height of the table and expand it (with padding) to match the height required to display the menu. The menu can be any size.

In my case, this is not the table that has the '.table-responsive' class, it's a wrapping div:

<div class="table-responsive" style="overflow:auto;">
    <table class="table table-hover table-bordered table-condensed server-sort">

So the $table var in the script is actually a div! (just to be clear... or not) :)

Note: I wrap it in a function so my IDE can collapse function ;) but it's not mandatory!

works for me thanks

Was this page helpful?
0 / 5 - 0 ratings

Related issues

leomao10 picture leomao10  路  3Comments

vinorodrigues picture vinorodrigues  路  3Comments

ziyi2 picture ziyi2  路  3Comments

devfrey picture devfrey  路  3Comments

ghost picture ghost  路  3Comments