Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

dropdown menu in a .table-responsive #11037

Closed
SoftHai opened this issue Oct 11, 2013 · 56 comments
Closed

dropdown menu in a .table-responsive #11037

SoftHai opened this issue Oct 11, 2013 · 56 comments

Comments

@SoftHai
Copy link

SoftHai commented Oct 11, 2013

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

@pySilver
Copy link

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

@jeonghwan-kim
Copy link
Contributor

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;

@joulse
Copy link

joulse commented Oct 18, 2013

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...

@jeonghwan-kim
Copy link
Contributor

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

@carasmo
Copy link

carasmo commented Oct 20, 2013

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/

@carasmo
Copy link

carasmo commented Oct 20, 2013

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.

@joulse
Copy link

joulse commented Oct 21, 2013

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.

@pySilver
Copy link

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.

@mdo
Copy link
Member

mdo commented Nov 30, 2013

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).

@julpod
Copy link

julpod commented Aug 11, 2014

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

@leocaseiro
Copy link

Could you please someone check if the solution using position:static works?
YaleSTC/reservations#724 (comment)

@kaystrobach
Copy link

@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

@leocaseiro
Copy link

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

@kaystrobach
Copy link

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

@wellwind
Copy link

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'));
        });
    });
});

@kaystrobach
Copy link

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

@kaystrobach
Copy link

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 :(

@patrickhlauke
Copy link
Member

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

@kaystrobach
Copy link

mhmm right ...

@leocaseiro
Copy link

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;
    }
}

@kristjan-kure
Copy link

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

@jgw96
Copy link

jgw96 commented Jun 22, 2015

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.

@Sbudah
Copy link

Sbudah commented Oct 13, 2016

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.

@llins
Copy link

llins commented Nov 25, 2016

@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.

@baltazarqc
Copy link

baltazarqc commented Nov 25, 2016

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

@fmonts
Copy link

fmonts commented Jan 16, 2017

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

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

@simon21587
Copy link

@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': ''});
});

@gildonei
Copy link

@simon21587 It works perfectly

@UksusoFF
Copy link

UksusoFF commented Apr 6, 2017

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

@picks44
Copy link

picks44 commented Oct 27, 2017

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

@KasraF
Copy link

KasraF commented Jan 11, 2018

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.

@claudioalmeiida
Copy link

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

@willderazevedo
Copy link

willderazevedo commented Jun 4, 2018

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

@kickbk
Copy link

kickbk commented Aug 23, 2018

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

@AbdullahiAbdulkabir
Copy link

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

@vitormicillo
Copy link

For me, this is the best solution with bootstrap from version 3 to 5, thanks

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!

@aijazbinqasim
Copy link

aijazbinqasim commented Sep 28, 2022

For BS v5.1

    .table-responsive .dropdown {
        position: static !important;
    }
}

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

@tuthost
Copy link

tuthost commented Oct 3, 2022

For BS v5.1

    .table-responsive .dropdown {
        position: static !important;
    }
}

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

It looks like you have 1 extra curly brace in the example. But the example worked for me.

@ArsHinMQ
Copy link

None of the solutions worked for me, I have a big-ass table with like 12 big-ass columns, I need the oveflow-x: auto even in large devices.
also if I change the position of .dropdown, it expands the height of table's row and doesn't look good.

@GreathostRo
Copy link

After more than a few hours and various attempts I found the working version:

const dropdowns = document.querySelectorAll('.dropdown-toggle')
const dropdown = [...dropdowns].map((dropdownToggleEl) => new bootstrap.Dropdown(dropdownToggleEl, {
    popperConfig(defaultBsPopperConfig) {
        return { ...defaultBsPopperConfig, strategy: 'fixed' };
    }
}));

@timbogdanov
Copy link

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).

why? based on all the discussions on these, even in 2022, this should be a priority

@timbogdanov
Copy link

None of the solutions worked for me, I have a big-ass table with like 12 big-ass columns, I need the oveflow-x: auto even in large devices. also if I change the position of .dropdown, it expands the height of table's row and doesn't look good.

on the same boat as you on this.

@aynoncse
Copy link

I have solved the problem by changing the position of the dropdown menu from 'absolute' to 'fixed', and now it works.

If you are using jQuery then you can use this code or rewrite equivalent vanilla code.

$('.table-responsive').on('click', 'button[data-bs-toggle="dropdown"]', function (e) {
  const { top, left } = $(this).next(".dropdown-menu")[0].getBoundingClientRect();
  $(this).next(".dropdown-menu").css({
    position: "fixed",
    inset: "unset",
    transform: "unset",
    top: top + "px",
    left: left + "px",
  });
});

if ($('.table-responsive').length) {
  $(window).on('scroll', function (e) {
    $('.table-responsive .dropdown-menu').removeClass('show');
    $('.table-responsive button[data-bs-toggle="dropdown"]').removeClass('show');
  });
}

@NicoJuicy
Copy link

NicoJuicy commented May 28, 2024

Bootstrap 4. I'm adding a class "dropup" on the dropdown class when it's the last element of the last.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests