Bootstrap dropdown menu hidden behind other elements - javascript

I have a problem similar to this . But the solutions there does not work for my issue. And that discussion has been closed for further suggestions.
Unfortunately I can't show my code exactly as it is on jsfiddle. However the issue is something like this. When trying to expand the dropdown inside first panel section, the dropdown hidden behind other elements.
I've spent hours trying different suggestions concerning "stacking context", "position", and "z-index".
I would really appreciate if anyone could point me to any resources that could help.
<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"> <span data-bind="label">Select One</span> <span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu">
<li>Item 1
</li>
<li>Another item
</li>
<li>This is a longer item that will not fit properly
</li>
</ul>
</div>

You have two options. You can make the container's overflow expand to fit the content by setting 'overflow: auto' to the parent's css.
FIDDLE DEMO
.panel-body { /* parent container of the menu */
overflow:auto;
}
Or you could do something functionally with JavaScript to handle the menu placement. This option is a little bit 'hacky' and would require further improvements. You would need to improve it to handle multiple menus open at once on the same page, and you would need to improve how it selects the menu. You might also want to add scroll support to move the menu with its target element unless that's not an issue. Here's the fiddle:
FIDDLE DEMO
(function() {
// hold onto the drop down menu
var dropdownMenu;
// and when you show it, move it to the body
$(window).on('show.bs.dropdown', function(e) {
// grab the menu
dropdownMenu = $(e.target).find('.dropdown-menu');
// detach it and append it to the body
$('body').append(dropdownMenu.detach());
// grab the new offset position
var eOffset = $(e.target).offset();
// make sure to place it where it would normally go (this could be improved)
dropdownMenu.css({
'display': 'block',
'top': eOffset.top + $(e.target).outerHeight(),
'left': eOffset.left
});
});
// and when you hide it, reattach the drop down, and hide it normally
$(window).on('hide.bs.dropdown', function(e) {
$(e.target).append(dropdownMenu.detach());
dropdownMenu.hide();
});
})();
There is probably a better way to do this, but the concept is to move the menu from the confines of the parent element to the body. When you're relying on the html data-attributes to setup the menu, you'll have trouble doing this, but that is where the second solution I reference becomes useful.
Sadly I would have suggested looking into the 'Via Javascript' options that bootstrap provides, but apparently they don't have the option to set the target append element. It would have been nice to have an option to append the menu on to whichever element you want like most of their other items: Bootstrap Docs
EDIT
As #Andrea Pizzirani mentioned, you could try removing the css for the menu absolute positioning, but I wouldn't recommend it! That will mess up all other instances of the menu unless you add other css to restrict scope of the change. Also, if you want the menu positioned under the button, you'll have to redo CSS that bootstrap already had in place.
Still, if it works, and you're not worried about messing up all other menus, it may be the solution you were looking for. Heres a fiddle demonstrating the solution:
FIDDLE DEMO
dropdown-menu {}
EDIT
I finally found where I originally found this solution. Gotta give credit where credit is due!

Try to remove position: absolute; from:
dropdown-menu {}
and place the menu under the button by css.

Modification of Jonathan's answer above, which was a huge help, but I modified it to place the menu back at it's source row, which allows custom jQuery to function properly (because the menu still exists at its initial parent element hierarchy. My use case was a jQuery DataTable with frozen columns. We had a bootstrap 4 "Action" dropdown-menu embedded in a cell of that table. The menu was appearing behind the hidden columns. This function allowed it be copied and pasted above the fixed columns where users can actually interact with it.
function BringActionMenuToForeground() {
var dropdownMenu;
/// When you show the menu, remove it from its current row and place it right back. This puts it on top of the DOM.
$(window).on('show.bs.dropdown', function (e) {
/// grab the menu
dropdownMenu = $(e.target).find('.dropdown-menu');
/// get the row where the menu appers.
var menuRow = $(dropdownMenu).closest("tr");
/// detach it and append it right back to the row from which it was taken.
$(menuRow).append(dropdownMenu.detach());
/// grab the new offset position
var eOffset = $(e.target).offset();
/// make sure to place it where it would normally go (this could be improved)
dropdownMenu.css({
'display': 'block',
'top': eOffset.top + $(e.target).outerHeight(),
'left': eOffset.left
});
});
/// and when you hide it, reattach the drop down, and hide it normally
$(window).on('hide.bs.dropdown', function (e) {
$(e.target).append(dropdownMenu.detach());
dropdownMenu.hide();
});
}

Add an additional class on your HTML and use the below codepen reference code.
check here: https://codepen.io/qpqinc/pen/yLyPVMJ
<div class="dropdown">
<button class="btn btn-default dropdown-toggle" type="button" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
Dropdown
<span class="caret"></span>
</button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
<li>Item 4</li>
<li>Item 5</li>
<li>Item 6</li>
<li>Item 7</li>
<li>Item 8</li>
<li>Item 9</li>
<li>Item 10</li>
</ul>
</div>
$(function() {
//add BT DD show event
$(".dropdown").on("show.bs.dropdown", function() {
var $btnDropDown = $(this).find(".dropdown-toggle");
var $listHolder = $(this).find(".dropdown-menu");
//reset position property for DD container
$(this).css("position", "static");
$listHolder.css({
"top": ($btnDropDown.offset().top + $btnDropDown.outerHeight(true)) + "px",
"left": $btnDropDown.offset().left + "px"
});
$listHolder.data("open", true);
});
//add BT DD hide event
$(".dropdown").on("hidden.bs.dropdown", function() {
var $listHolder = $(this).find(".dropdown-menu");
$listHolder.data("open", false);
});
//add on scroll for table holder
$(".ak-holder").scroll(function() {
var $ddHolder = $(this).find(".dropdown")
var $btnDropDown = $(this).find(".dropdown-toggle");
var $listHolder = $(this).find(".dropdown-menu");
if ($listHolder.data("open")) {
$listHolder.css({
"top": ($btnDropDown.offset().top + $btnDropDown.outerHeight(true)) + "px",
"left": $btnDropDown.offset().left + "px"
});
$ddHolder.toggleClass("open", ($btnDropDown.offset().left > $(this).offset().left))
}
})
});

You need to add only:
.panel.panel-default{
overflow: inherit !important;
}
JSFIDDLE DEMO

Related

Hide() dropdown contents makes it disappear

I'm new to Javascript so this may be a stupid question, but I'm at a loss. I've been trying to get my dropdown to hide/close when the right list item is selected. I'm using the hover dropdown provided by W3Schools. I've copied the CSS they've provided and my HTML and JS look like this:
<div class="dropdown" id = "menu3">
<button class="dropbtn" id = "menu3btn">Menu 3</button>
<div class="dropdown-content" id = "menu3c">
<ul>
<li><a id="item30">Cheese</a></li>
<li><a id="item31">Pepperoni</a></li>
<li><a id="item32">Sausage</a></li>
</ul>
</div>
</div>
And JS:
$(document).ready(function(){
$('li').click(function(){
if($(this).text() == "Sausage"){
$(this).parent().parent().hide();
}
}
}
The current Javascript is closing the dropdown if the user clicks "Sausage", which is what I want. But when I hover over the menu again, the dropdown doesn't open/show. I've tried changing
$(this).parent().parent().hide();
to
$(this).parent().hide();
and the results are the same.
I've also tried to toggle the dropbtn by replacing the parent().hide() code with:
var btnName = "#"+$(this).parent().parent().parent().attr("id")+"btn";
$(btnName).toggle();
This removed the button and dropdown menu from the screen entirely.
I've also tried forcing the hover to show hidden dropdown-content by adding this method to my JS:
$(".dropdown").hover(function() {
if($(this).attr("id") == "menu3"){
$("#menu3c").show();
}
}
This didn't do anything either (the hidden content stayed hidden).
Any help would be much appreciated.
Instead of $(this).parent().parent().hide();, you can use this to hide the parent
$(this).closest('div').hide();
To show the menu again,
$('#menu3btn').on('click', function() {
$('#menu3c').toggle();
});

How do I get this jQuery script to work

I have a bit of jQuery code I am trying to get to work here. I basically have a vertical nav menu with two items that have jQuery dropdown menus that pop out to the side. However, when you hover over them both quickly they are displayed overlapping each other. So what I am trying to accomplish is that if one is hovered on and pops out, if the other one is already displayed it will slideUp and be hidden. The dropdown trucks already existed I am adding on the engines one. I will keep working on it but if anyone can help out I would appreciate it.
/* Dropdown Menu Trucks*/
$("#menu-main-menu .show-dropdown").hover(function(){
$("#dropdown").slideDown('fast');
});
$("#dropdown").mouseenter(function(){
$("#dropdown").show();
});
$("#dropdown").mouseleave(function(){
$("#dropdown").slideUp('fast');
});
// ----------------------------
/* Dropdown Menu Engines */
$("#menu-main-menu .show-dropdown2").hover(function(){
$(".dropdown-engines").slideDown('fast');
});
$(".dropdown-engines").mouseenter(function(){
$(".dropdown-engines").show();
});
$(".dropdown-engines").mouseleave(function(){
$(".dropdown-engines").slideUp('fast');
});
if ($('#dropdown').is(':visible')) {
$(".dropdown-engines").slideUp();
}
else if ($('.dropdown-engines').is(':visible')) {
$("#dropdown").slideUp();
}
edit: let me approach this another way that seems easier to accomplish..
$("#menu-main-menu .dropdown2").hover(function(){ // hover over .dropdown2
// show #dropdown-engine
$("#dropdown-engine").slideDown('fast');
});
$("#dropdown-engine").mouseenter(function(){ // mouse enter #dropdown-engine
// show #dropdown-engine
$("#dropdown-engine").show();
});
$(".dropdown2" || "#dropdown-engine").mouseleave(function(){
$("#dropdown-engine").slideUp('fast'); // mouse leave #dropdown-engine
// *or .dropdown2 slideup
}); // but only if not hovering on either
The question isn't completely clear to me, but I think this is want.
I noticed that you used id's that are not present in your jsfiddle and are using classes and id's mixed. Try to be consistent and think DRY, by using data-target and the classes like in the example, the code is cleaner and shorter.
$(function(){
$(".dropdown").hide();//hide all dropdowns on start
$(".show-dropdown").mouseenter(function(){
//on mouse enter of a .show-dropdown, we slideup all .dropdowns
$(".dropdown").slideUp("fast");
//then we get the ID of the dropdown we want to show through the data-target attribute, and slide it down.
$("#"+$(this).attr("data-target")).slideDown("fast");
});
});
<ul>
<li class="show-dropdown" data-target="dropdown-trucks"><!--data target is the ID of dropdown you want to show-->
Trucks
</li>
<li class="show-dropdown" data-target="dropdown-engines">
Engines
</li>
</ul>
<ul id="dropdown-trucks" class="dropdown">
<li>Truck 1</li>
<li>Truck 2</li>
</ul>
<ul id="dropdown-engines" class="dropdown">
<li>Detroit Series 60 Engine</li>
<li>Cummins N14 Engine</li>
</ul>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
I ended up just using hide(); on hover to hide the element I didn't want to see. I was severely overthinking this. Thanks to anyone who answered.

HTML5 storage for ul list?

I'm having trouble figuring out how to use session storage for list items. This doesn't seem to be working and because of the styling, I can't use the basic select/option:
<div class="btn-group">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown"><span data-bind="label">Day</span><span class="caret"></span>
</button>
<ul class="dropdown-menu" role="menu" name="country">
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
var item = window.localStorage.getItem('country');
$('ul[name=country]').val(item);
$('ul[name=country]').change(function() {
window.localStorage.setItem('country', $(this).val());
});
fiddle
It is a basic click() event. You should not use change():
$('ul[name=country] li').click(function() {
window.localStorage.setItem('country', $(this).text());
});
Check in the console, using localStorage.getItem("country").
Fiddle: https://jsfiddle.net/5z0nkdjb/
You're trying to use val in a ul tag, and it does not have value property.
Also, you're trying to use a change event on it, and it does not have that event either.
First, you must understand what you're doing there. You've probably copied this Dropdown Picker from anywhere else, and you don't understand what it's doing.
It's simulating a select behavior by using a label inside a button, and anytime a li inside the ul tag is clicked, your code changes the label text to the li's one.
So, firstly, when your page is loaded, you must see if anything is already in your localStorage, and if it is, you must change the label's text to the value saved in the localStorage.
var item = localStorage.getItem('country');
if (item) $('span[data-bind="label"]').text(item);
After that, inside your li's click event, you must save the clicked li's text into the localStorage for persisting purposes later.
// Dropdown picker
$('.dropdown-menu').on('click', 'li', function(e) {
var $target = $(e.currentTarget);
/* the rest of your code */
localStorage.setItem('country', $target.text());
});
I've updated your fiddle with the code working.

Make only bullets clickable

I'm working on a menu that is designed using an unordered list, with each list element containing a link to another page. However, I would also like to be able to click on each bullet point to open more subcategories that will also link to other pages.
Essentially, I would like to be able to click once on a link and have it go to the correct page, but I would also like to click on the bullet point and have it expand into the subcategories. I've been researching how to separate the bullet from the content of the li, but it doesn't seem to be working in my case, likely because my li contains a lot of subcategories. Here's an example:
<li id="m2l1" class="child">
Y
<ul id="u11">
<li class="gchild">Y.1</li>
<li class="gchild">Y.2</li>
<li class="gchild">Y.3</li>
<li class="gchild">Y.4</li>
<li class="gchild">Y.5</li>
<li class="gchild">Y.6</li>
<li class="gchild">Y.7</li>
<li class="gchild">Y.8</li>
<li class="gchild">Y.9</li>
</ul>
</li>
Does anyone have any suggestions as to how to separate the bullet from the text in my case?
Here's the link: http://jsfiddle.net/stamblerre/XYp48/17/
Thank you!!!
Your HTML is invalid. You can't have div inside your ul. Moreover, you can greatly simplify your code by moving separate logic for each li into one generic handler.
Something like this:
Demo: http://jsfiddle.net/abhitalks/XYp48/18/
CSS:
ul {
display: none;
overflow: hidden;
}
ul:first-child {
display: block;
}
JS:
$("li").on("click", function () {
$(this).children("ul").slideToggle();
return false;
});
Edit:
I deliberately left out checking of a because clicking the a would navigate to the respective pages (as mentioned in your question), so expand/collapse wouldn't matter.
However, as per your comment if you really want to remove a altogether from the handler, then you can use the event target to handle li without a. Something like this:
Demo 2: http://jsfiddle.net/abhitalks/XYp48/22/
JS:
$("li").on("click", function (e) {
var $t = $(e.target); // get the event target as a jQuery object
if (!$t.is('a')) { // check if the target is not an anchor
$(this).children("ul").slideToggle();
return false;
}
// otherwise if target is anchor, then do nothing
});
Change your list still to hide bullets, then modify your html to :
<li class="gchild">•Y.1</li>
Should do the trick.
<li class="gchild">Y.1</li>
One way that worked for me: remove the bullet with li { list-style-type: none; } then add your own bullets with the character • (alt-8 on a mac). Add that character inside a elements like so:
• X
with the label now outside of the element.
Hope this works for you!

jQuery - Set var - Multiple Ul's - Target same li child for every ul

Simple question I hope.
I want to use jQuery to identify identical children in a similar list based setting (the equivalent horizontally.)
Pretend I have this:
<ul class="features">
<li class="category">Hardware Repair</li>
***background on hover for both***
<li class="category">Network Troubleshooting</li>
</ul>
<ul class="features">
<li class="category">Hardware Repair</li>
***background on hover for both***
<li class="category"><a href="services_something_further.html">Network
</ul>
And I want the background to change on ALL children of either list item 1 or 2 (horizontal positioning).
Please don't mention tables. Very good reason I'm not using them.
Here is my code thus far for simplicity:
<script>
(function() {
$('ul.features li').on('hover', function() {
var $eqTd = <! How to target every equivalent child across both ul's >
var $blah = <! Whatever else I need >
<! I Can Figure This Part Out. Need to think about defining the items before I declare a function... >
});
})();
</script>
Wondering at this point. Is there a CSS method? I have a live example so you can see how I want to change BG horizontally here:
http://www.sinsysonline.com/repair/price.html
(* Just tried to simplify the code. I can figure out the styling and whatnot.)
NEED TO DEFINE:
Horizontal Li elements
To Change:
BG Color:
Any help is appreciated!
Not clearly sure what you need. But if you want identical children based on its position in another container with the same class you can try this way using index() and nth-child. Also on(hover) is not valid it is a combination of mouseenter and mouseleave events.
See this example to select identical elements from other container with the same class.
$(function(){
$('ul.features li').on('mouseenter mouseleave', function () {
$('ul.features > li:nth-child(' + ($(this).index() + 1) + ' )').toggleClass('active');
});
});
Fiddle
if you want to select all other but not the hovered one you can do this
$('ul.features > li:nth-child(' + ($(this).index() + 1) + ' )').not(this).toggleClass('active');
you can use CSS3 :nth-child() Selector :
see then link below
Style Odd and Even

Categories

Resources