I have a Bootstrap v3 Panel with a panel-heading and panel-body. I want a user to be able to click on the panel-heading and it will collapse/expand the panel-body. I am using Bootstrap's Collapse javascript to do this. Here is my markup:
<div class="panel panel-default group-panel">
<div class="panel-heading" role="button" data-toggle="collapse" data-target="#panel-body-foobar">
Action Button
</div>
<div class="panel-body collapse in" id="panel-body-foobar">
Some content here.
</div>
</div>
This code works fine, clicking on the panel heading will collapse/expand the body. However, notice that I also have a button inside the panel heading. When I click that button, it is collapsing/expanding the panel body. See this jsfiddle if you don't know what I mean.
How can I configure this so that only clicking directly on the panel heading, not any child elements, will trigger the collapsing/expanding?
I believe the solution is to define my own click handler (rather than using the one Bootstrap sets up automaitcally) which ignores clicks on children:
$('.panel-heading[data-toggle^="collapse"]').click(function(){
var target = $(this).attr('data-target');
$(target).collapse('toggle');
}).children().click(function(e) {
e.stopPropagation();
});
Thanks to this answer for helping me figure out how to have a jQuery click handler ignore the clicks on children. Note that due to use of e.stopPropagation();, this does not disable any click handlers on the children themselves.
Related
I have been working on getting a popup menu on a button.
There are 7 buttons with this menu, on the page in different containers. So far you can click the button and the menu opens.
Each menu opens with its own version of this, which works but not efficient:
$('.country-btn-portugal').click(()=>{
$(".dropdowna").toggleClass('active');
});
$('.country-btn-uk').click(()=>{
$(".dropdowna").toggleClass('active');
});
....etc... x7, one for each button menu.
I have tried to close the menu if an item is clicked but doesnt function with:
//close if menu <a> is clicked
$('#mclose').click(()=>{
$('.dropdown').removeClass('active');
});
And using the following to close the menu if an item that is not this element is clicked (does not work):
$(document).mouseup(function (e)
{
var container = $("#oclick");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
container.hide();
}
});
which i was hoping would also fix the issue when 1 menu is open and your next click is another menu, so you have both open.
The menu buttons will be serving separate divs (or card like boxes and are not siblings next to eachother. Hence finding it hard to compact the code. had to give each menu its own click functions.
it is a mess sorry. would be nice to see where im going wrong here.
fiddle --> https://jsfiddle.net/s4nk1zev/118/
html structure for one "card" with one "menu button".
<div class="country_card">
<span class="cc-t goth upperC">Portugal </span> <span class="cc-t goth upperC blued">Visa</span>
<div class="cc-txt">
text in here
</div>
<div class="cc-btn">
<button class="tablabel country-btn-portugal" id="portimg"></button>
<div id="mcontainer" class="dropdowna">
<a id="mclose" class="mclose" href="#home">Overview</a>
<a id="mclose" href="#about">Application Process</a>
<a id="mclose" href="#contact">Investment Options</a>
</div>
</div>
</div>
Well, this is how I would have done that.
Here is the updated fiddle
And this script would do enough
//open menu
$('.tablabel').click(function(event){
$('.tablabel').not(this).next().removeClass("active")
$(this).next().toggleClass("active")
});
//close if menu clicked
$(".dpd").click(function(e){
$(this).toggleClass("active")
})
//close if anything but menu clicked
What it does is, just listen for any click on the button and add active class to the next of it. Removing active class from all the active elements if there is any.
Secondly, you can use a class (as I've added one dpd) on the menue items to detect a click on them to close the open menu.
One more thing. Identifiers must be unique for each element you use. I've also updated them to be unique
Hope that helps
SInce your button and menu tags appear to always be siblings, if you add a common class to all your dropdowns, you can get a list of all of them more easily. Like this:
<div id="mcontainer" class="dropdown dropdowna">
Also as a suggestion, it's really not a very good idea to have duplicate ids in your document. It's against the html standard, and it can cause strange issues with getting tags by id.
Once you have a common class on all your dropdowns, you can do something like this to close all others, and toggle the one related to the button you're clicking.
function fnClick(e){
var $dd = $(this).next();
$('.dropdown').not($dd).removeClass('active');
$dd.toggleClass('active');
}
//open menu
$('.country-btn-portugal').click(fnClick);
$('.country-btn-uk').click(fnClick);
here's an update of your fiddle to demonstrate:
https://jsfiddle.net/s4nk1zev/143/
You can try using promise().done(), and reviewing the html class. This is a fiddle for you: https://jsfiddle.net/zxoLmf71/
using a promise on an element let you wait for the code to finish execution before start the new one. this is the code:
//open menu
const buttons = $('.country-btn');
const dropDownMenus = $('.dropdownmenu');
const dropDownItems = $('.dropDownItem')
buttons.click(function() {
dropDownMenus.removeClass('active');
$(this).next('div').promise().done(function() {
$(this).toggleClass('active');
});
});
//close if menu clicked
dropDownItems.click(function() {
dropDownMenus.removeClass('active');
});
Hope it helps
I got problem with my accordion: I want when the panel opens jQuery adds a class expanded to the target(element) which I clicked and when the other panel opens, it removes that class and add it to the other one which opens.
The default bootstrap functionality when I open the accordion panel it removes collapsed class of the a tag and when I close it, it adds it(collapsed class) again, I used this to solve my problem(which I explained it in paragraph above) but I couldn't.
here is the HTML code.
<div class="panel-group" id="accordion">
<div class="panel" >
<div class="panel-heading">
<a data-toggle="collapse" data-parent="#accordion" href="#html">HTML</a>
</div>
<ul id="html" class="panel-collapse collapse">
<li><a class="a_nav" href="#">Tags</a></li>
</ul>
</div>
<div class="panel">
<div class="panel-heading">
<a data-toggle="collapse" data-parent="#accordion" href="#css">CSS</a>
</div>
<ul id="css" class="panel-collapse collapse">
<li><a class="a_nav" href="#">Bootstrap</a></li>
</ul>
</div>
</div>
and here is the jQuery code
$(document).ready(function(){
//add class to panel heading of sidebar for expanding
$("div.panel-heading").click(function (event) {
$target = $(event.target);
$target.addClass("expanded");
if($("div.panel-heading a").hasClass("collapsed")) {
$(this).removeClass("expanded");
//this line is for testing if the if condition even works
$(this).text("ok");
}
});
});
You are trying to get the event target and apply or remove the class to the event target every time.
As some users have said before me, maybe you should let bootstrap handle the accordion functionality.
But I will give a quick try to answer your problem.
You are using:
$("div.panel-heading").click(function (event) { ... });
so you want to execute a function when an div with class panel-heading is clicked.
Inside the function you do:
$target = $(event.target);
To get the target element where the click event was fired.
Now looking at your HTML I can see that .panel-heading divs contain a link as a child.
So event.target, will be the link itself if you click on the link or the .panel-heading div if you click on the div, so depending on where you happen to click, the class is going to be added to/removed from different elements.
This is something to watch. Maybe instead of event.target, you can use $(this) in your event handling function.
Below that, your logic is wrong because let's assume that you addClass("expanded") to the correct element, then this:
$("div.panel-heading a").hasClass("collapsed");
Will look at a collection of elements so even if the first link doesn't have the class, the second link may have it, in which case the if statement will evaluate to true and the class you added before, will be removed from the element on which the event handler is called on.
To fix the issue, I would propose something like (if you don't want to let bootstrap do the work):
$(".panel-heading").click(function () {
var $target = $(this);
//close all panels
$(".panel-heading").removeClass("expanded");
$(".panel-heading a").addClass("collapsed");
//open clicked one
$target.addClass("expanded");
$("a", $target).removeClass("collapsed");
});
For better performance, I would cache the collections of elements used in the event handler in variables so that they don't get recalculated every time a click happens.
I think this should solve your issue:
$('.panel-heading').click(function (event) {
$(this).toggleClass('expanded');
$('.panel-heading a').toggleClass('collapsed');
});
See the docs for toggleClass().
I have a bootstrap collapse panel which is loaded with a lot of data from an ajax call.
When the data is loaded in the collapse panel is populated with ~ 2000 dom elements, mainly checkboxes and spans.
I've measured the time for the call and the creation of the dom elements, and it doesn't seem to be a problem. It takes less than a second to load and create the elements using jQuery.
However, when I click to expand collpased panel it causes the site to lag, and it takes a good while for the panel to expand.
Is there any solution to this, or an alternative to the bootstrap collapse panels? Could you expand it and show the elements incrementally as you scroll?
I don't seem to have any problem at all with collapse panels that contain less elements.
<div id="collapse_function" class="panel-collapse collapse">
<div class="panel-body">
<div class="panel-group" id="inner_accordion">
<!-- Checkboxes and spans are loaded here dynamically -->
</div>
</div>
Edit: It seems to be the case that even with disabled animations, the site lags.
you should use the :
$('#collapse_function').on('show.bs.collapse', function() { alert('we Go SHOW'); });
$('#collapse_function').on('hide.bs.collapse', function() { alert('we Go Hide'); });
$('#collapse_function').on('shown.bs.collapse', function() { alert('we ARE SHOWN'); });
$('#collapse_function').on('hidden.bs.collapse', function() { alert('we ARE HIDDEN'); });
Well, it seems like I was appending too many dom elements to the collapse panel.
I solved it by lazily loading the elements when scrolling down in the panel.
I'm stuck with my dropdown menu.
I want it to open when I click the Nav-Button "clicker". Then it shall close when I click "clicker" again.
I want it to show/hide only when clicking the Image. It shall stay open when I click inside the hidden div.
But that doesnt work. I dont know why. Tried so many things. Any idea? (Total noob here).
HTML:
(Click on "#clicker" Image = Toggle Dropdown; "#dropdown-inside" = hidden div;
<div class="header_content">
<div class="navbar">
<div id="dropdown-menu">
<img id="clicker" src="http://porschedvd.de/bluptest/typo3/fileadmin/stromer/template/pix/menu_btn.jpg" alt="">
<ul id="dropdown-inside">
JQuery:
$j('#clicker').click(function() {
$j('#clicker').not(this).children('ul').slideUp("slow");
$j(this).children('ul').slideToggle("slow");
});
$j('#clicker').blur(function() {
$j('#dropdown-inside').hide('slow', function() {
});
});
Fiddle here: http://jsfiddle.net/377G6/2/
I have read your code and tried to run it.
Now you have 2 points to fix:
Your selector $j(this).children(ul) can't find the list.
The list ul is a sibling element of your #clicker, so you can use the method siblings('ul') or next() to select the list, but not children()
the event 'blur' can't be triggered now.
Generally, the event blur could only be triggered on input or textarea, so if you want to trigger the blur on <img> or <div>, you may set an attribute like tabindex="-1" to it.
As I tried, it runs successfully after the fix.
Here is the link: http://jsfiddle.net/edisonator/kfcB9/
Thanks,
Edison
I have this JavaScript code that enables a accordion menu to function, it works, kinda...
When I click the link, it indeed drops down as it should, but when I click it again, it comes back up, then goes down again, not hiding as I would like. The only way to hide the element just opened, is to click the next element on the menu, then the first one will close and the second will open.. Can this be modified to make the element that is opened, close and stay closed, instead of closing and just opening right away.
Code:
// JavaScript Document
$(document).ready(function() {
//ACCORDION BUTTON ACTION
$('div.accordionButton').click(function() {
$('div.accordionContent').slideUp('normal');
$(this).next().slideDown('normal');
});
//HIDE THE DIVS ON PAGE LOAD
$("div.accordionContent").hide();
});
<div id="wrapper">
<div class="accordionButton"><strong>Subject:</strong></div>
<div class="accordionContent">Text</div>
<div class="accordionButton"><strong>Subject:</strong></div>
<div class="accordionContent">Text</div>
<div class="accordionButton"><strong>Subject:</strong></div>
<div class="accordionContent">Text</div>
</div>
Change:
$('div.accordionContent').slideUp('normal');
$(this).next().slideDown('normal');
to be
$('div.accordionContent').not($(this).next()).slideUp('normal');
$(this).next().slideToggle();
It could be an event bubbling issue, but without full code, it's hard to say. You might try including 'return false' at the end of your click listener to stop bubbling and ensure that no default behaviors execute.