I have this code that for now creates a Alert() with the value of that list item.
Now when i click a list item that has another list item as parent. it will alert() them both.
$("#nav li").click(function() {
$LiValue = this.value;
alert($LiValue);
});
Example. here is the HTML
<li value="1">Home</li>
<li value="2">Information
<ul class="subs">
<li value="3">History</li>
<li value="4">Present</li>
<li value="5">Future</li>
</ul>
</li>
Now when i click on list item "Information" it will return with value 2
When i click on list item "Present" it will return value 4 and then 2.
How can i only return the list item i click on and not the parent?
--------->>>> SOLVED!! (can't accept answer yet)
Thank you all for helping me. i will accept the answer as soon as i can. thank you!
Events in JavaScript naturally bubble up the DOM tree, from child elements to their ancestors. You can stop this behavior by stopping the event propagation.
$("#nav li").click(function(e) {
e.stopPropagation();
$LiValue = this.value;
alert($LiValue);
});
The fix to your problem is stopPropagation(). The jQuery documentation tells you that this function "Prevents the event from bubbling up the DOM tree, preventing any parent handlers from being notified of the event."
Basically, the event will not be fired for any of the parent elements. In order to use this method, handle the first parameter that your click function provides you. Then you call the method inside the function. Your method should look like this
$("#nav li").click(function(e) {
e.stopPropagation()
$LiValue = this.value;
alert($LiValue);
});
$("#nav li").click(function(e) {
$LiValue = this.value;
alert($LiValue);
return false;
});
or try to add return false.
Explanation : The difference is that return false; takes things a bit further in that it also prevents that event from propagating (or "bubbling up") the DOM. The you-may-not-know-this bit is that whenever an event happens on an element, that event is triggered on every single parent element as well.
So in other words:
function() {
return false;
}
// IS EQUAL TO
function(e) {
e.preventDefault();
e.stopPropagation();
}
see this link for further explanation
Related
I'm trying to disable a li click event after it has clicked the first time. Essentially to stop the array data being doubled. The click is working fine for each time. My current method doesn't appear to be working. I also need to disable the other li's from being clicked once the first one has :)
Thanks
JS code is:
$('#eventType ul li').click(function(e) {
e.preventDefault();
var value = $(this).attr('value');
answers.push(value);
// Below isn't working
$(this).click(function() {
return false;
});
console.log(answers);
});
you need to use one:
$('#eventType ul li').one('click',function(){
//your code here
});
this event will be fired only once
UPDATE
you can do that using $.off()
$('#eventType ul li').one('click',function(){
//your code here
$('#eventType ul li').off('click');
});
jQuery is just JavaScript so you can easily add behaviors that you want
// basic jQuery plugin boilerplate
$.fn.once = function once(eventType, f) {
// this = the selected elements
return this.each(idx, elem) {
// create reference to jQuery-wrapped elem
var $elem = $(elem);
// add event listener for eventType
$elem.on(eventType, function(event) {
// call the event handler
return f(event);
// remove the event handler
$elem.off(eventType, f);
});
});
};
Usage would look like this
$('#eventType ul li').once('click', function(event) {
console.log("you will only see this once");
});
However, this is obviously a common need so it exists in jQuery already. It's called $.one. As APIs grow, you may not know about the existence of such procedures. This answer exists to show you that you can use your brain to program the things that you want or that might be missing from a particular library. This lessens your dependence on the creator's of the lib to introduce the functionality you need.
EDIT
In a comment, you ask if the event handler can be disabled for all other LI elements after the first LI is clicked. The trouble here is that jQuery uses implicit iteration, which means that when you call $('li').on('click', ...), jQuery will bind an onclick event handler for each LI.
A better solution to this problem would be to use jQuery's event delegation
// only fire event handler for the first LI clicked
$('ul').one('click', 'li', function(event) {
console.log($(this).text());
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
This will delegate the event listener to the children LI, but once one of the LI is clicked, the event handler will be removed (because we delegated using the $.one procedure).
Try clicking one LI, you will see a message in the console. When you click the second LI, nothing will happen because the event handler was removed.
var used = false;
$('#eventType ul li').click(function(e) {
if (used == false) {
used = true;
e.preventDefault();
var value = $(this).attr('value');
answers.push(value);
console.log(answers);
}
});
the way you did it was just adding another on click handler, not removing or overriding the old ond.
You can use CSS classes; add the class 'disabled' to elements you don't need, and avoid adding elements that have the classe 'disabled'.
https://plnkr.co/edit/6aloNPETHGxfiP5oYZ9f?p=preview
$('ul li').click(function(e) {
if(!$(this).hasClass('disabled')) {
var value = $(this).text();
answers.push(value);
$('li').addClass('disabled');
}
console.log(answers);
});
I've been trying to get a simple vertical navigation to display a list item's children on click.
However when you then click any of those nested, children list items you are in essence 'clicking' the parent list item again. Which triggers my click function to remove '.active' and closes the parent list item right before the user is redirected to their chosen page. This looks bad and is totally annoying.
Any advice on how to get a click function to not affect the children list items?
$('#sidebar > li.parent').click(function(){
if($(this).hasClass('active')){
$(this).removeClass('active');
}else{
$(this).addClass('active');
}
});
HTML
<aside id="tertiary" class="tertiary">
<ul id="sidebar" class="sidebar">
<li class="active">Portal</li>
<li>Start Here</li>
<li>Cards</li>
<li class="parent">Programs
<ul>
<li>LOS</li>
<li>Safety</li>
<li>Retirement</li>
<li>Wellness</li>
<li>Investors</li>
</ul>
</li>
<li>Marketplace</li>
<li>Reporting</li>
</ul>
</aside>
You would need to stop propagation in the children... not the parent
http://jsfiddle.net/TrueBlueAussie/g6a496Lf/
$('#sidebar > li.parent').click(function (e) {
e.preventDefault();
$(this).toggleClass('active');
}).find("ul li").click(function(e){
e.stopPropagation();
});
The e.preventDefault() is just there to stop the link clicking on the parent in the example. e.stopPropagation() stops the child click bubbling up the DOM.
I chained the two handlers together, so that you did not need a separate selector like this:
$('#sidebar > li.parent ul li").click(function(e){
e.stopPropagation();
});
You could add a click handler for the child elements that uses stopPropagationto prevent the click event bubbling to parent elements:
$('#sidebar > li li').click(function(e){
e.stopPropagation();
});
Further reading: MDN docs for Event.
Before you stop propagation of events, take some time to spin up on why you shouldn't do so.
From css-tricks.com, "The Dangers of Stopping Event Propagation", very briefly:
"Modifying a single, fleeting event might seem harmless at first, but it comes with risks. When you alter the behavior that people expect and that other code depends on, you're going to have bugs. It's just a matter of time."
"A much better solution is to have a single event handler whose logic is fully encapsulated and whose sole responsibility is to determine whether or not the menu should be hidden for the given event."
With that being said, you could do something like
$('#sidebar').on('click', function( e ) {
var $target = $(e.target).closest('li');
if ( $target.hasClass('parent') ) {
$target.toggleClass('active');
}
});
Working example:
http://jsfiddle.net/g6a496Lf/1/
Okay so a simple thing that I don't know how to search for, wasn't able to get an answer anywhere...
Basically, I'm using XML to populate a list. What happens is when clicking on the first tier li, it adds all the different suburbs to the child ul. This generates then the structure below:
<li class="province-active" style="background-image: url("img/minus-icon-storefinder.png");">
<p>Gauteng</p>
<ul class="province-sub-menu">
<li class="province-sub-nav-item" data-suburb="Strijdom Park">Strijdom Park,Randburg</li>
<li class="province-sub-nav-item" data-suburb="Edenvale">Edenvale,Eastrand</li>
<li class="province-sub-nav-item" data-suburb="Strubens Valley">Strubens Valley,Roodepoort</li>
<li class="province-sub-nav-item" data-suburb="Centurion">Centurion,Pretoria</li>
</ul>
</li>
Okay the problem is, using the JQuery below, it first triggers the click event for "province-sub-nav-item" and then triggers the click event for "province-active" (the parent li). This is the JS I'm using for the children li.
$(document).on("click", ".province-sub-nav-item", function(e){
e.stopPropagation();
$shop = $(this).attr("data-suburb");
loadShop($shop);
});
And this is the function for the parent li.
$(".province-active").click(function(e) {
$li = $(this);
$province = $li.children("p").text();
$(".store-locations").css("width", 375);
getMap($li, $province, 15);
});
So basically, I just need to not fire the click event for "province-active" without firing the click event for "province-active" and stopPropagation(); isn't working.
Thanks guys.
It is because you have used used event delegation and bounded the handler to teh document object.
So when event bubbles up the province-active elements handler will be triggered before the document objects handler is triggered so your stop propagation does not have any effect.
Demo: Fiddle - see the order of logging in the console
One possible solution is to use event delegation for the province-active element also
$(document).on("click", ".province-active", function (e) {
var $li = $(this);
var $province = $li.children("p").text();
$(".store-locations").css("width", 375);
getMap($li, $province, 15);
});
Demo: Fiddle - see the order of logging in the console
Alternatively this works too:
$(".province-sub-menu").on("click", ".province-sub-nav-item", function(e){
e.stopPropagation();
$shop = $(this).attr("data-suburb");
loadShop($shop);
});
I can't figure out how to remove an event listener from an element on my page. This event is causing issues between two scripts and it would be great to get rid of it!
Here's how it's added;
if (CT_I_EnableExitTracking) {
if (document.addEventListener) //handle DOM 2 (Mozilla 6)
document.addEventListener('click', CT_ProcClick, false);
else
document.attachEvent('onclick', CT_ProcClick);
}
..and it needs removing from this markup (from the flexslider 2 plugin)..
<ul class="flex-direction-nav">
<li><a class="flex-prev" href="#">Previous</a></li>
<li><a class="flex-next" href="#">Next</a></li>
</ul>
The links inside this div are the issue. How can I target them? I've tried various ideas but I just don't seem to get the syntax right (too much time with jQuery and no real jS knowledge I think!!)
var prev = document.getElementsByClassName("flex-prev");
prev.removeEventListener('click', CT_ProcClick, false);
Thanks!
You're assigning event listener to document object while you try to remove event listener from flex-prev.
In addition, document.getElementsByClassName("flex-prev") returns a NodeList. In this case you should either loop that through:
[].forEach.call(prev, function(obj, i) { ... });
or reference to single instance with:
document.getElementsByClassName("flex-prev")[0].
But in your case, you should make a check on the event listener's function whether the event.target has class "flex-prev". Like this:
function CT_ProcClick(evt) {
var target = evt.target || evt.srcElement;
if (!!~target.className.indexOf("flex-prev")) {
return false;
}
...
}
I have two list items that, when clicked, should change classes from '.off' to '.on'. Only one element should be '.on' at a time so when one is already turned on and the other is clicked both elements should change classes from '.off' to '.on' and vice versa. If a list item with a class of '.on' is clicked it should change classes to '.off'
The problem I am having is when a list item with class '.on' is clicked it still runs the click function as if it had a class of '.off'
My html:
<ul>
<li>ABOUT</li>
<li>SUBMIT</li>
</ul>
My javascript (running on jQuery 1.7.1)
$('.off').click(function(event) {
event.preventDefault();
$(".on").addClass("off").removeClass("on");
$(this).addClass("on").removeClass("off");
});
$('.on').click(function(event) {
event.preventDefault();
$(this).addClass("off").removeClass("on");
});
Does anyone know what is going on here? Is there something wrong in my code or have I encountered some sort of bug here?
http://jsfiddle.net/ZC3CW/6/
The selectors you're using to bind the event using click() are used to select the elements to add the event handler to. The selector is not considered when the handler is run.
You should be looking for something more like this:
$('li').click(function(event) {
event.preventDefault();
if ($(this).hasClass('off')) {
$(".on").addClass("off").removeClass("on");
$(this).addClass("on").removeClass("off");
} else { // $(this).hasClass('on');
$(this).addClass("off").removeClass("on");
}
});
You might want to make the li selector more explicit by adding a class/id to the ul or li's.
To confuse things further, you could also do this (if you're using jQuery > 1.7);
$(document).on('click', '.off', function(event) {
event.preventDefault();
$(".on").addClass("off").removeClass("on");
$(this).addClass("on").removeClass("off");
});
$(document).on('click', '.on', function(event) {
event.preventDefault();
$(this).addClass("off").removeClass("on");
});
This is because the .on() function works by attaching the event handler to the selected elements (document), and will only execute the handler (the function) on the event specified (click) if the element that the event originated from matches the selector .off at the time the event fired, not at binding time.
I would suggest adding a click handle to a different selector, this should work for you...
$("ul li a").click(function(e){
e.preventDefault();
if($(this).hasClass("off")){
$("ul li a").addClass("off").removeClass("on");
$(this).addClass("on").removeClass("off");
}
else{
$(this).addClass("off").removeClass("on");
}
});
The problem is that jQuery handlers get attached at page load and remain the same regardless of changing their classes. Use live('click', handler) on('click', handler) instead of click().
Edit: just noticed that .live() is deprecated in jQuery 1.7.
The problem as I see it is that your "on" class is not in play at the time of the click event, so your $('.on').click method is never being called.
Try re-assigning your events after changing classes (example follows) :
var assignClicks = function () {
$('.off').click(function(event) {
event.preventDefault();
$(".on").addClass("off").removeClass("on");
$(this).addClass("on").removeClass("off");
assignClicks();
});
$('.on').click(function(event) {
event.preventDefault();
$(this).addClass("off").removeClass("on");
assignClicks();
});
};
assignClicks();
Hope this helps,
Pete
The click is bound to the element not the class.
Maybe you can attach the events to the elements and detect/toggle the elements classes:
$('li').click(function(event) {
event.preventDefault();
if( $(this).hasClass('on') ) {
$(this).removeClass('on');
}
else {
$(this).addClass('on');
$(this).siblings().removeClass('on');
}
});