Can't toggle class to element - javascript

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

Related

Open menu on click close other menus

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

Why is event.stopPropagation also stopping my Bootstrap Collapse?

I have a list item (#planAdminMenuItem) that has an onclick attribute. This list item has an icon inside of it (.spinner) that will collapse #collapseExample. Whenever the .spinner is clicked, I want it to run bootstrap collapse only. I do not want it to run drawPlanAdmin function. I have tried adding event.stopPropagation to my toggleSpinnerLeftMenu function, but whenever I do that, it also stops the bootstrap collapse. The parent click is blocked, but so is bootstrap collapse.
THE PHP & HTML CODE
<ul>
<li id="planAdminMenuItem" onclick="plan.drawPlanAdmin();">
Book Plan
<span class="icn icn-chevron-down spinner" onclick="ui.toggleSpinnerLeftMenu(this,event);" data-toggle="collapse" data-target="#collapseExample" data-aria-expanded="true" aria-controls="collapseExample"></span>
</li>
<!-- the collapsable area -->
<li id="collapseExample" class="collapse in">
<ul>
<li onclick="plan.drawRunListAdmin();">
Run List View
</li>
<li onclick="plan.drawLadderAdmin();">
Ladder View
</li>
</ul>
</li>
</ul>
THE JS CODE
toggleSpinnerLeftMenu:function(el,event){
el = jQuery(el);
if(el.hasClass('icn-chevron-up')){
el.addClass('icn-chevron-down');
el.removeClass('icn-chevron-up');
}else if(el.hasClass('icn-chevron-down')){
el.addClass('icn-chevron-up');
el.removeClass('icn-chevron-down');
}
event.stopPropagation(); //why is this stopping the collapse also?
},
stopPropagation is doing exactly what it is meant to do.
If you want the parent element to be propagated by the click on the inner element then simply don't do event.stopPropagation at all.
Though for some reasons if you need to have that then my suggestion is: call the function like
toggleSpinnerLeftMenu:function(el,event){
el = jQuery(el);
if(el.hasClass('icn-chevron-up')){
el.addClass('icn-chevron-down');
el.removeClass('icn-chevron-up');
}else if(el.hasClass('icn-chevron-down')){
el.addClass('icn-chevron-up');
el.removeClass('icn-chevron-down');
}
plan.drawPlanAdmin(); // Call the function inside of the child element's click handler.
event.stopPropagation(); //why is this stopping the collapse also?
},
Update: Since you described the issue more clearly in the comment, which has a solution completely south of what I've written above, I am updating with the new content that may be able to help.
Instead of attaching two event handlers, one using an inline onClick attribute and another using Bootstrap's data-collapse, use one:
$(".spinner").on("click", function(event) { // tip: Give a better id or class name
$('#collapseExample').collapse({toggle: true});
ui.toggleSpinnerLeftMenu(this, event);
});
This is the general idea of doing this, you may still have to make some adjustments to your method calls to fit it in.

Bootstrap: Using panel heading to collapse panel body

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.

Dropdown menu not opening

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

Show menu when i click on button and hide that when i click anywhere on the page or button

I have small dropdown profile menu with logout button etc. I need to show the menu when I click on the button and hide it when i click anywhere on page or on the button as well.
<div id='menu'>
<ul>
<li class='has-sub'> <a class="testbutton" id="userButton" onclick="dropdown()" href="#">
<span id="buttonText">User name</span> <span id="triangleDown">▾</span>
</a>
<ul id="submenu">
<li class='has-sub'><a href='#'><span>Change password</span></a>
</li>
<li class='has-sub'><a href='logout.php?action=0'><span>Logout</span></a>
</li>
</ul>
</li>
</ul>
</div>
I used JavaScript. At this time menu is displayed on hidded only when I click on profile button. I also know how to start function using something like document.ready.
My not working code:
function dropdown() {
if ($('#submenu').css('visibility') == 'hidden') {
$('#submenu').css('visibility', 'visible');
} else {
$('#submenu').css('visibility', 'hidden');
}
};
$(document).click(function (event) {
if ($('#submenu').css('visibility') == 'visible') {
$('#submenu').css('visibility', 'hidden');
}
});
But when I combine this methods it does not works. So when I clicked on the button to open menu, nothing happened.
Sorry for my English :)
Thanks for help in advance.
This has partly to do with something called event propagation. Put simply, this means that click events will register not only on the clicked element, but also on any parent or ancestor elements of that element.
So if you click a DIV, the event will also be registered on the BODY, because the DIV is inside the BODY. Put abstractly, if a kitchen is the scene of a crime, then the apartment that houses that kitchen is also the scene of a crime. One is inside the other.
This is prevented by preventing propagation - in jQuery, by running the stopPropagation() method of the evt object that is automatically passed to your event handler.
In any case, your situation can be greatly simplified.
var menu = $('#menu'), but = $('#menu_button');
$(document).on('click', '*', function(evt) {
evt.stopPropagation(); //<-- stop the event propagating to ancestral elements
if ($(this).is(but)) //<-- on button click, toggle visibility of menu
menu.toggle();
else if (!$(this).closest(menu).length) //<-- on click outside, hide menu
menu.hide();
});
Assumption: I have assumed that the toggler button is targetable via the selector '#menu_button'. Update this as required. Also, the code should run inside a DOM-ready handler.
The code listens for clicks to any element. If it's registered on the button, the visible state of the menu is toggled. If it's to an element outside of the menu, the menu is hidden. (If, in the latter case, the menu is already hidden, this will have no effect.)
Here's a working JS Fiddle that demonstrates the approach.
Try this:
$(function() {
$('.test-button').click(function(event) {
event.stopPropagation();
$('#submenu').toggle();
});
$('body').click(function() {
var submenu = $('#submenu');
if(submenu.is(":visible")) {
submenu.hide();
}
})
});

Categories

Resources