68

Using bootstrap, I have a dropdown menu(s) inside a <div> with overflow: hidden, which is needed to be like this. This caused the dropdowns to be clipped by the container.

My question is, how can I solve this clipping issue, e.g. change the container of all dropdowns inside my project to be body, with the lowest cost possible?

.bs-example {
  height: 80px;
  border: 1px solid black;
  overflow: hidden;
}
<div class="bs-example">
  <div class="dropdown">
    <a id="dLabel" data-target="#" href="http://example.com" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
        Dropdown trigger
        <span class="caret"></span>
      </a>

    <ul class="dropdown-menu" aria-labelledby="dLabel">
      <li>a</li>
      <li>b</li>
      <li>c</li>
      <li>d</li>
    </ul>
  </div>
</div>
Michael M.
  • 10,486
  • 9
  • 18
  • 34
AlBaraa Sh
  • 2,202
  • 3
  • 20
  • 29
  • 21
    I need the overflow style – AlBaraa Sh Aug 05 '15 at 11:35
  • 3
    Oh, yeah, just remove overflow and break the layout of whatever the menu's in as long as the menu's open. Not a solution at all. Why would the developers of this menu leave the menu item nested inside the menu when it's supposed to be on top of everything? Solutions that try to use z-index and fixed are thwarted by any parent element transforms too. HTML is still a decade behind Flash in 2016. Sad. So many broken sites full of hacks. Chase's site doesn't even open in Firefox. So much for standards. – Triynko Jul 06 '16 at 22:10
  • A poor fix that applies to certain cases: In my case I have a carousel with several articles. Each article has a "share" button at the bottom right, which opens a dropup menu. As with your example, if the article has not enough height, the menu gets clipped because of the carousel's overflow hidden. The idea is to set min-height to the article equal to the height of the menu and position the button at the bottom right. This min-height thing can be done with javascript if the menus are not all of equal height. Again: this poor fix only applies to certain cases but maybe the idea helps. – Quiquetas Dec 21 '16 at 19:40
  • Still an issue in 2019. Just tried to like my own comment, haha. I'm using a standard "select" element, and with really long text for an option, it extends far beyond the right edge of the desktop, rendering the vertical scroll bars inaccessible in Chrome. This should not be possible for such a standard control, and might be a bug in Windows or Chrome. – Triynko Nov 19 '18 at 17:48
  • 1
    @Triynko We are not in 2019 yet. : > – Andriod Dec 07 '18 at 05:21
  • 1
    Now we are @Andriod :) – ThCC Jan 07 '19 at 14:29

22 Answers22

56

if anyone interested in a workaround for this, bootstrap dropdown has a show.bs.dropdown event you may use to move the dropdown element outside the overflow:hidden container.

$('.dropdown').on('show.bs.dropdown', function() {
  $('body').append($('.dropdown').css({
    position: 'absolute',
    left: $('.dropdown').offset().left,
    top: $('.dropdown').offset().top
  }).detach());
});

there is also a hidden.bs.dropdown event if you prefer to move the element back to where it belongs once the dropdown is closed:

$('.dropdown').on('hidden.bs.dropdown', function() {
  $('.bs-example').append($('.dropdown').css({
    position: false,
    left: false,
    top: false
  }).detach());
});

Here is a working example:

http://jsfiddle.net/qozt42oo/32/

Varol
  • 1,798
  • 1
  • 22
  • 30
  • If you are nested with your dropdown, and have no inline css, on hide you can simply say something like: $('.dropdown').removeAttr('style') – Paul Allsopp Oct 11 '17 at 19:45
  • I'm trying to figure out if there's a way to do this, while preventing a weird flash in my dropdown button if i have different styling for hover on my dropdown button. See http://jsfiddle.net/5mprhb14/. when you click on the dropdown if flashes yellow briefly. Not sure if there's a workaround in this case. That just seems like there's a brief moment that it takes to trigger the hover state for the component after being added to the DOM. – Sir Neuman Aug 10 '18 at 16:11
  • This worked for me, although in my case I had multiple dropdowns on the same page, so needed to capture the event for only the specific event throwing dropdown. – coler-j Dec 06 '18 at 04:10
  • I faced an issue when the dropdown is inside a scrollable content, scrolling will not maintain dropdown element position – Ahmed Ismail Jun 27 '19 at 15:59
  • Instead of moving the node, I opted instead to use `position: fixed`. Then, you can just register the following handler: `let select = $('div.bootstrap-select'); select.on('show.bs.dropdown', function() { let pos = select.offset(); $('div.dropdown-menu').css({ left: pos.left, top: pos.top + select.outerHeight() })});` – Jeff G Feb 28 '20 at 22:01
  • @JeffG Now position fixed is not working under transformed elements. – Shashank Bhatt Sep 29 '22 at 07:23
16

Change the div property to overflow:visible;, or Set Position:absolute; on .dropdown class like

.bs-example .dropdown{position:absolute;}

See the Working Code: http://jsfiddle.net/guruWork/3x1f8ng5/

Guru
  • 621
  • 1
  • 6
  • 15
  • 7
    I need the `overflow:hidden` style. regarding absolute positioning, it won't work if there is an element between .bs-example and .dropdown which has a non-static position, like this: http://jsfiddle.net/vn6437kf/ – AlBaraa Sh Aug 05 '15 at 11:59
  • 1
    Remove `.bs-example>div{position:relative;}` . Actually it, because of relative position, dropdown class is setting it's position according to that div. and because it is child element of `.bs-example`, So `overflow:hidden` applies on `dropdown` class. If you want `position:relative;`, then you should set it on it's parent div. See this ex: http://jsfiddle.net/guruWork/3x1f8ng5/1/ – Guru Aug 06 '15 at 06:45
12

My solution was to use static positioning on the dropdown. I had the dropdown inside a bootstrap table with overflow hidden.

<div class="table" style="overflow: hidden;">
  <div class="dropdown" style="position: static;">
    <div class="dropdown-menu">...</div>
  </div>
</div>

It didn't work with your fiddle, however. So I'm not sure if my solution had something to do with bootstrap tables. Regardless, perhaps it will give someone something new to try.

Jacob Colvin
  • 2,625
  • 1
  • 17
  • 36
10

Using Bootstrap 5.2.1 we can configure popperjs to use a fixed position breaking out of the parents overflow: hiddden.

<div style="overflow: hidden">
  <div class="dropdown">
    <button class="dropdown-toggle"
            type="button"
            data-bs-toggle="dropdown"
            data-bs-popper-config='{"strategy":"fixed"}'>
      Dropdown
    </button>
    <ul class="dropdown-menu">
      <li>Item #1</li>
      <li>Item #2</li>
    </ul>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/js/bootstrap.bundle.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.1/dist/css/bootstrap.min.css" rel="stylesheet" />

<div class="row">
  <div class="col-6">
    <div style="overflow: hidden; width: 100px; height: 35px; border: 1px solid pink;">
      <div class="dropdown">
        <button class="btn btn-link dropdown-toggle" type="button" data-bs-toggle="dropdown" data-bs-popper-config='{"strategy":"fixed"}'>
      Working
    </button>
        <ul class="dropdown-menu">
          <li>Item #1</li>
          <li>Item #2</li>
        </ul>
      </div>
    </div>
  </div>
  <div class="col-6">
    <div style="overflow: hidden; width: 100px; height: 35px; border: 1px solid pink;">
      <div class="dropdown">
        <button class="btn btn-link dropdown-toggle" type="button" data-bs-toggle="dropdown">
      Cut off
    </button>
        <ul class="dropdown-menu">
          <li>Item #1</li>
          <li>Item #2</li>
        </ul>
      </div>
    </div>
  </div>

Update

Also from the Bootstrap 5.2.1 Docs the popperConfig can also be passed in via JS.

const element = document.querySelector(".dropdown-toggle")
const dropdown = bootstrap.Dropdown.getOrCreateInstance(element, {
  popperConfig: {
    strategy: "fixed"
  }
)
Justin Tanner
  • 14,062
  • 17
  • 82
  • 103
  • Hi, when I try to add via data-attribute this way, it gives error about the type that bootstrap DROPDOWN: Option "popperConfig" provided type "string" but expected type "(null|object|function)" but with javascript it works fine. Any idea about that? – Shashank Bhatt Sep 29 '22 at 06:05
  • @ShashankBhatt interesting what version of Bootstrap are you using? – Justin Tanner Sep 29 '22 at 07:34
  • Bootstrap version is 5.1 – Shashank Bhatt Sep 29 '22 at 07:39
  • But here what I am trying to do is to create template via js to pass as an option via backtick literals. – Shashank Bhatt Sep 29 '22 at 07:41
  • Nice, then use setting JS, I personally find the single quoted popper setting a bit weird, it feels like it shouldn't be in the HTML, I'll update my answer to say as much. – Justin Tanner Sep 30 '22 at 04:54
  • 1
    If your using `react-bootstrap` and the dropdown is misplaced you may need to add `renderOnMount` to the `Dropdown.Menu`. (https://github.com/react-bootstrap/react-bootstrap/issues/6203) – Peter Riesz May 09 '23 at 15:36
7

Ng Bootstrap >= 4.1.0 (Angular powered Bootstrap) solution:

For anyone struggling with the same issue, but using ng-bootstrap, here's a solution for you - use container="body" option in your ngbDropdown:

<div ngbDropdown container="body">
    <!-- Dropdown menu goes here -->
</div>
Yulian
  • 6,262
  • 10
  • 65
  • 92
5

All the answers mentioned here didn't work for me, so I found another workaround:

$('.dropdown').on('shown.bs.dropdown', function () {
    const dropdown = $(this);
    setTimeout(function () {
        dropdown.find('.dropdown-menu').css({'top': dropdown.offset().top - $(window).scrollTop(), 'left': dropdown.offset().left - $(window).scrollLeft(), 'position': 'fixed'});
    }, 1);
});

The timeout is necessary as the event handler is fired before bootstraps sets the CSS of the dropdown therefore overwriting it

Johannes
  • 377
  • 4
  • 17
4

You can also try this one by replacing yourDivId by the id of your div.

$('.dropdown').on('show.bs.dropdown', function() {
    $('#yourDivId').css('overflow-x', 'visible');
});

$('.dropdown').on('hidden.bs.dropdown', function() {
    $('#yourDivId').css('overflow-x', 'hidden');
});
3

I had some kind of scroll problem with most of solutions in this and other questions I've seen on SO. What worked for me was making the dropdown position inherit:

.dropdown {
  position: inherit;
}

And then set the dropdown-menu position using JavaScript:

$(document).on("show.bs.dropdown", function () {
  var dropdownToggle = $(this).find(".dropdown-toggle");
  var dropdownMenu = $(this).find(".dropdown-menu");
  dropdownMenu.css({
    "top": (dropdownToggle.position().top + dropdownToggle.outerHeight()) + "px",
    "left": dropdownToggle.position().left + "px"
  });
});

Here is a working example: http://jsfiddle.net/ck66ee9q/

Helder Pereira
  • 5,522
  • 2
  • 35
  • 52
2

I was facing the same issue and after searching for hours.. I created a event handler to catch the dropdown-toggle and get the positions and attach it dynamically. so that it doesnt get cut off.

Here is the simple code I used. Try it out if this helps.

$('.dropdown-toggle').click(function (){
     dropDownFixPosition($('button'),$('.dropdown-menu'));
 });
 function dropDownFixPosition(button,dropdown){
    var dropDownTop = button.offset().top + button.outerHeight();
    dropdown.css('top', dropDownTop + "px");
    dropdown.css('left', button.offset().left + "px");
  }
Chait
  • 511
  • 7
  • 15
2

I had a div element with a Bootstrap panel class containing DevExpress ComboBoxes contained in div elements with Bootstrap col-lg classes.

I had a problem of the panel background color or border color bleeding to the div above. To resolve that I added overflow:hidden; to the panel but that caused the ComboBox's dropdown to be cut off. I added style="position: inherit" to those div elements within the panel containing the ComboBoxes and that solved the problem.

<div class="col-sm-12" style="position: inherit">
<div class="col-sm-4" style="position: inherit">
 <dx:aspxComboBox></dx:aspxComboBox>
</div>
</div>

This solution I found here.

Dov Miller
  • 1,958
  • 5
  • 34
  • 46
  • Thanks it worked. And that's what I too noticed. This is especially happening with col-lg classes. Not anything else. As soon as there is a col-lg class used it the menu goes below the content. – Vishwas Aug 02 '21 at 18:04
2

All suggestions that have been made here have some kind of lacking things and I could not make them work properly. So I have made this one based on all suggestions and works great for me.

I have used another class here to make it different than regular dropdown elements that I might wanna have them run in the regular way.

$(document).on("show.bs.dropdown", ".dropdown.my-body-dropdown", function () {

    //original dropdown element
    var thisDropdown = $(this);

    //make a deep clone
    var thisDropdownCopy = thisDropdown.clone(true, true);

    //append to the body with a different class which is my-modified-dropdown here 
    //and open which will make dropdown menu show up
    $("body").append( thisDropdownCopy.addClass("my-modified-dropdown open").css({ "position": "absolute", "left": thisDropdown.offset().left, "top": thisDropdown.offset().top }).detach() );

    //hide the original dropdown element
    $(this).hide();

})
.on("hidden.bs.dropdown", ".dropdown.my-body-dropdown", function() {

    //remove modified dropdowns
    $(".dropdown.my-body-dropdown.my-modified-dropdown").remove();

    //show original dropdowns
    $(".dropdown.my-body-dropdown").show();

});
Mert Simsek
  • 700
  • 7
  • 12
0

I ended up using tippy.js insead of bootstrap dropdown, which solved this overflow:hidden parent problem, since dropdown can be located in body tag.

Giedrius
  • 128
  • 1
  • 8
0

You all were very helpful to me in getting this figured out. In my case, the accepted answer did not work because my dropdowns are created in code-behind during ajax as GenericHtmlControls and therefor, do not exist at page load to hook the events with the .dropdown selector. Additionally, my pages can have many dropdowns dynamically added that are affected by this, as well as any number of other dropdowns that are NOT affected.

I added a unique css class (editModeModuleMenu) to the affected div(s) containing the actual menu and previously marked with .dropdown-menu, changed the event selector to window and scoped the function to ONLY select and alter the single dropdown in focus.

$(window).on('show.bs.dropdown', function (e) {
var el = $(e.target).closest('.editModeModuleMenu');
if (el.length == 0) return;
$('body').append(el.css({
    position: 'absolute',
    left: el.offset().left,
    top: el.offset().top
}).detach());})

And here is the relevant code-behind:

private void attachSettingGear(Panel container)
    {
        var dropdown = new HtmlGenericControl("div");
        dropdown.Attributes["class"] = "dropdown";
        dropdown.Style["position"] = "absolute";
        dropdown.Style["top"] = "2px";
        dropdown.Style["right"] = "10px";
        

        var a = new HtmlGenericControl("a");
        a.Attributes["href"] = "#";
        a.Attributes["class"] = "dropdown-toggle";
        a.Attributes["data-bs-toggle"] = "dropdown";
        a.Style["color"] = "white";
        dropdown.Controls.Add(a);
        

        var gear = new HtmlGenericControl("span");
        gear.Attributes.Add("class", "navBarIcon bi-gear");
        
        a.Controls.Add(gear);

        container.Style["position"] = "relative";
        container.Controls.Add(dropdown);

        var menu = new HtmlGenericControl("div");
        menu.Attributes["class"] = "dropdown-menu editModeModuleMenu";
        dropdown.Controls.Add(menu);

        createMenuLink(menu, "Delete", "trash3", $"javascript:popupConfirm('Confirm Delete','Are you sure you want to delete this module?', 'Delete', 'Cancel', deletePageModule, null, {ModuleSettings.ID})");
    }

private void createMenuLink(HtmlGenericControl menu, string text, string icon, string href)
    {
        var ico = new HtmlGenericControl("span");
        ico.Attributes.Add("class", "navBarIcon bi-" + icon);

        var item = new HtmlGenericControl("a");
        item.Attributes["class"] = "dropdown-item";
        item.Attributes["href"] = href;
        item.InnerText = text;
        item.Controls.AddAt(0, ico);

        menu.Controls.Add(item);
    }

Thank you all for the excellent information. I hope my addition helps someone else struggling with this.

0

2022 Bootstrap 5 working example.

I think this is the correct code:

    $('.dropdown > button').off('click').click(function(clickEvent){
        clickEvent.preventDefault();
        clickEvent.stopPropagation();
    });
    $('.dropdown').on('show.bs.dropdown', function() {
        $('body').append($('.dropdown').css({
            position: 'absolute',
            left: $(this).offset().left,
            top: $(this).offset().top
        }).detach());
    });
    $('.dropdown').on('hidden.bs.dropdown', function() {
        $('.dropdown-wrap').append($(this).css({
            position: 'relative',
            left: '0',
            top: '0'
        }).detach());
    });
ErikTailor
  • 875
  • 1
  • 8
  • 11
0

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

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

If the height of the table is not important to you, it is possible to increase the min-height when the dropdown is toggled and reset it to auto after the dropdown hides:

$(".table-functions").on('show.bs.dropdown', function() {
  $('.table-scroll').css("min-height", "125px");
}).on('hide.bs.dropdown', function() {
  $('.table-scroll').css("min-height", "auto");
});
Michael M.
  • 10,486
  • 9
  • 18
  • 34
0

Bootstrap already provides a solution for this. No need for custom javascript or overriding css.

See the boundary option at https://getbootstrap.com/docs/4.0/components/dropdowns/#options

Setting this to viewport or window resolved the issue for me.

0

I needed this for a horizontal scrollable navbar with dropdowns. In this context the following solution works, but it should also apply to other situations.

function bringToFront(dropdown) {
    setTimeout(
        function () {dropdown.find('.dropdown-menu').css({ 'top': dropdown.offset().top + dropdown.outerHeight() - $(window).scrollTop(), 'left': dropdown.offset().left - $(window).scrollLeft(), 'position': 'fixed' })
    }, 1)
}

$('.navbar .dropdown').on('shown.bs.dropdown', function () { bringToFront($(this)); });
Maxi
  • 421
  • 4
  • 4
-1

I faced the same issue, with needing to have overflow:hidden set, but yet have the drop-down not be clipped.

However my situation involved the Bootstrap Carousel, so I decided a valid solution would make the overflow visible only when the dropdown is opened, and when it closes, the overflow can return to hidden. This solution might work for others, so I'm sharing it.

$('.dropdown-toggle').click(function() {
  var $container = $(this).parent('.carousel-inner');
  $container.css('overflow','visible');
  $(document).one('click', function() {
      $container.css('overflow','');
  });        
});

Note, this doesn't check for Esc key being used to close the dropdown, but in my case this wasn't an issue. My own version uses a class to apply the style which some devs might prefer instead of modifying inline CSS.

mikeapr4
  • 2,830
  • 16
  • 24
-2

Changing the container is possible using jQuery e.g. $(".dropdown").insertAfter(".bs-example");

See this code for example: https://jsfiddle.net/SolveItSimply/pwmyvsw6/

I don't recommend this though, as you will lose any of the usefulness of Bootstrap positioning and keeping the drop-down elements in order will be messy.

Instead, you could use overflow:visible along with the 'hidden' class to your advantage, hiding any element that overflows with JavaScript. Here's an example: http://jsfiddle.net/SolveItSimply/z14c210x/

You will need to check the contained elements when the div is first visible and whenever the height of the container changes, which I've tried to demonstrate in my example.

Chris Dobson
  • 140
  • 7
-4

I couldn't set the container to overflow:visible, but inserting a

<div class="clearfix" />

below the block containing the Dropdown solved the problem for me.

Andreas
  • 303
  • 1
  • 17
-8

Just change the overflow value to visible.

sanjeev shetty
  • 438
  • 4
  • 17
  • 5
    I need it to be `overflow:hidden` – AlBaraa Sh Aug 05 '15 at 11:35
  • with overflow hidden. It gives the result. what do you want to do actucally. do you want the div to stretched.? – sanjeev shetty Aug 05 '15 at 12:11
  • 1
    yes, main div will shrink, so I need `overflow:hidden` to prevent content to go out. but dropdown should appear fully. This is why I need to change the container of dropdown to be the `body`. – AlBaraa Sh Aug 05 '15 at 12:20
  • 3
    This isn't hard to understand. The dropdown is in a container that clips all its content and hides it, but the menu has a high z-index to put it above everything, but that doesn't help because the container still clips it. In other words, the menu shouldn't be in the container at all. Bootstrap's menu sucks, because it doesn't parent the menu to the document above everything, it just leaves it in whatever container it happens to be in. It's bad design. – Triynko Jul 06 '16 at 22:16
  • @Triynko putting the menu at the document root would result in other problems, such as following the content when scrolling and context specific styling. – Emile Bergeron Dec 04 '17 at 18:05