Suppose I have the following HTML:
<div class="container">
<span class="remove">remove</span>
</div>
and jquery:
$(".container").delegate(".remove", "click", function() {
alert('yes');
});
This works, but now I have HTML like:
<div class"container">
<div>
<span class="remove">remove</span>
</div>
</div>
or even
<div class"container">
<div>
<div>
<span class="remove">remove</span>
</div>
</div>
</div>
So how can I make the jquery work in this case, so that if finds the .remove element in a hierarchy of elements and make the click event work?
NOTE: I'm adding the elements dynamically (using clone function)!
This will still work. As long as the element you're delegating the event to is an ancestor of the element, the element will receive the event.
An exception to this is where an element which is a more direct ancestor stops the propagation of the event using event.stopPropagation(), event.stopImmediatePropagation(), or by returning false in it's handler;
$(".container").delegate(".remove", "click", function() {
// This will never get executed, as the first handler cancels the propagation.
alert('yes');
});
$(".container > div").delegate(".remove", "click", function(e) {
// This handler get the event first as it is closer to the source of the event.
e.stopPropagation();
});
FYI, if you're using jQuery > 1.7, you should consider using the new on() method as on() was implemented to disperse the confusion over live(), delegate() and bind(). It is expected that these older methods will be depreciated in 1.8. The following is equivilent for what you're currently using;
$(".container").on("click", ".remove", function(e) {
alert('yes');
});
Related
I have some elements that I add to the DOM after the page has been loaded. And I'd like to perform some actions when I click on them. I'm using the delegation with jQuery but I don't know how to get the clicked element when I'm in the fonction ($(this) refers in this case to the parent)
<div id="parent">
<div class="child">
<div class="hidden"></div>
</div>
<div class="child">
<div class="hidden"></div>
</div>
</div>
<script>
$('#parent').click('.child', function(){
$(this).find('.child').toggleClass("hidden displayed")
});
</script>
Let's say I want to toggle the inner div from "hidden" to "displayed" when I click on the "child" div. Currently when I click on the first "child" div, the two "hidden" div will be toggled, and I want to be toggled only the one in the div I clicked.
Use e.target to find out which element the event originated on.
$('#parent').on('click', '.child', function(e){
$(e.target).toggleClass("hidden displayed")
});
I also fixed the code a bit - you need to use .on for delegated events. (Mentioned by Barmar.)
You need to use .on() to delegate events. As the documentation says:
When jQuery calls a handler, the this keyword is a reference to the element where the event is being delivered; for directly bound events this is the element where the event was attached and for delegated events this is an element matching selector.
So it should be:
$('#parent').on('click', '.child', function() {
$(this).toggleClass("hidden displayed");
};
Your use of .click('.child', function...) does not do delegation. It matches the function signature:
.click(eventData, handler)
described here. So it's just binding to the parent, not delegating to the child, that's why you get the wrong value in this.
I am simply trying to create a function that allows me to add a div with content using jQuery .prepend() and then when that div is clicked it is removed using .remove().
Prepend seems to work fine, but I am having trouble getting the .remove() to work on prepended divs. It works fine on divs that were already present.
Could anyone possibly help me with where I am going wrong?
Here is my code:
<script language="javascript" type="text/javascript">
$(document).ready(function(){
$(".DivAdd").click(function(){
var thisDivItem = $(this).attr('add');thisDivItemid = "DivItem"+$(this).attr('add');
$( ".DivItem" ).parent().prepend( "<div id='"+thisDivItemid+"' class='DivRemove')>"+thisDivItem+"</div> " );
});
$(".DivRemove").click(function(){ $( this ).remove() });
});
</script>
<div class="DivAdd" add="321">Div Add</div>
<div class="DivItems">
<div class="DivItem DivRemove" id="DivItem1">1</div>
<div class="DivItem DivRemove" id="DivItem2">2</div>
<div class="DivItem DivRemove" id="DivItem3">3</div>
<div class="DivItem DivRemove" id="DivItem4">4</div>
<div class="DivItem DivRemove" id="DivItem5">5</div>
<div class="DivItem DivRemove" id="DivItem6">6</div>
<div class="DivItem DivRemove" id="DivItem7">7</div>
</div>
Try using:
$(".DivItems").on('click', '.DivRemove', function(){ $( this ).remove() });
As others have mentioned, .on() refers to the concept of event delegation.
You need to delegate your event handlers. This is because the element is dynamically added, and not in the DOM at the time jQuery was initialized. So it's pretty much like jQuery doesn't know about this object. You can delegate the event handler on something that will exist at the time of page load. For example, if your appended div were inside of a wrapping div:
$('.wrapper').on('click',".DivRemove", function() {
//do stuff
});
This is the principle of delegate():
Attach a handler to one or more events for all elements that match the selector, now or in the future, based on a specific set of root
elements.
$('.DivItems').delegate('.DivRemove', 'click', function(){
$( this ).remove() });
});
Or you can use on() for this, which is preferred:
$('.DivItems').on('click', '.DivRemove', function(){
$( this ).remove() });
});
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time. By
picking an element that is guaranteed to be present at the time the
delegated event handler is attached, you can use delegated events to
avoid the need to frequently attach and remove event handlers.
This will do what you need. This will bind to existing and dynamic elements added to the dom as well.
$(document).on('click','.DivRemove',function(){ $( this ).remove() });
I want to stop func() from being executed when I click the inner div.
this is the html:
<div onclick="func()">
<div id="inner-div">
I'm an inner div
</div>
</div>
This code will stop propagation:
$('#inner-div').bind("click", function(e){
e.stopPropagation();
})
This code fails to stop propagation:
$(document).on("click", '#inner-div' , function(e){
e.stopPropagation();
})
How do I do it?
Stopping propagation from a delegated handler will not work, since the event has already propagated up the document tree in order for that delegated handler to run.
This is mentioned in the documentation:
Since the .live() method handles events once they have propagated to
the top of the document, it is not possible to stop propagation of
live events.
on() and delegate() behave the same as live() in this context, only with an ancestor element instead of the document itself. In all cases, it is too late to stop the event from propagating from the target element.
And to solve it you could do:
<div onclick="func(event, this)">
<div id="inner-div">
I'm an inner div
</div>
</div>
JS
function func(e, that) {
if (e.target === that) {
// do stuff
}
}
FIDDLE
or use jQuery and remove the inline handler (as the question is tagged jQuery)
<div id="parent">
<div id="inner-div">
I'm an inner div
</div>
</div>
JS
$(document).on('click', '#parent', function(e) {
if (e.target === this) {
// do stuff
}
});
FIDDLE
The below code is not binding the event to an appended element (using .insertBefore()).
From my understanding, .on() is supposed to work like .live(). Is this not the case?
<div class="todoColumn">
<div class="projectHeader">
<div class="title">Example</div>Subtitle
</div>
<div class="todo">
<div class="checkbox"><span class="check pictos">3</span></div>
<div class="text">another test</div>
<div class="floatfix"></div>
</div>
<div class="todo">
<div class="checkbox"><span class="check pictos">3</span></div>
<div class="text">another test</div>
<div class="floatfix"></div>
</div>
</div>
$('.todoColumn .todo .checkbox').on('mouseenter', function() {
$(this).find('.check').css('visibility','visible');
});
It depends upon where you put the selector. Putting it in the first jQuery object does not have any .live() behavior. It binds static event handlers.
Specifying a comment parent object in the jQuery object and putting the selector in the arguments to .on() gives you live behavior:
$(document.body).on('mouseenter', '.todoColumn .todo .checkbox', function() {
$(this).find('.check').css('visibility','visible');
});
It will work even more efficiently (and better than .live()) if you pick a common parent that is closer to the actual objects than document.body. A problem with .live() was too many event handlers (all with selectors to check) on the document object. .on() works more like .delegate() and allows you to put the event handler on a common parent that is closer to the actual objects.
EDIT: Now that you've included your HTML, more efficient code would be this with a common parent selector in the jQuery object and the simplest possible selector in the .on() arguments:
$('.todoColumn').on('mouseenter', '.checkbox', function() {
$(this).find('.check').css('visibility','visible');
});
To use it the way you want, you need to place the selector in the function and bind the event to the document:
$(document).on("mouseenter", ".column .todo .checkbox", function(){
// do stuff
});
And, like jfriend00 suggested, it's more efficient to bind the event to the closest parent:
$(".column .todo").on("mouseenter", ".checkbox", function(){
// do stuff
});
Here's the problem html:
<ul id="update-list">
<li class="update" onclick="window.location('some_url')">
<h2> some header </h2>
<p> some paragraph </p>
<div>
<a class="popup-link">
<span> Show Popup </span>
<span> + </span>
</a>
</div>
</li>
// this repeats n times
//...
</ul>
When I click on .popup-link link, it should open the lightbox popup only (which it does) but the inline onclick on li also fires. The thing is that the li tags are all part of some partial which is fetched via ajax on different pages. So I use jQuery's delegate to bind the events as follows:
$('#update-list').delegate('.popup-link', 'click', function(e){
// e.target is <span> while e.currentTarget is .popup-link
e.stopPropagation();
//console.log(e.isPropagationStopped()); this shows 'true' in console
$.popup(); // launch popup
e.preventDefault(); // or return false
});
This doesn't seem to work and the inline onclick fires anyway. I've tried with live() as well but no success. Is there something I am missing here?
AFAIK you cannot reliably prevent an inline event handler from firing by stopping the bubbling within an attached event handler.
Furthermore, using live() or .delegate() you cannot use preventDefault() nor stopPropagation(). You need to return false to prevent the bubble phase and the default behavior.
Anyway, as I already mention you can't prevent the inline event handler to fire with that.
So either, create it completely unobtrusive (which is what I highly recommend) or remove that inline click handler in code.
Example:
$('#update-list').delegate('.popup-link', 'click', function(e){
$.popup(); // launch popup
return false;
}).delegate('.update', 'click', function(){
window.location('some_url');
})
// the rest of this is unnecessary if you can just omit the onclick attribute
.find('.update')
.removeAttr('onclick');
Ref.: .delegate()
$('#update-list').delegate('.popup-link', 'click', function(e){
e.stopImmediatePropagation();
e.preventDefault();
// do something...
});
Can you try this?
$('#update-list').delegate('.popup-link', 'click', function(e){
// e.target is <span> while e.currentTarget is .popup-link
e.stopPropagation();
e.preventDefault(); // or return false
// open popup in a timeout so that this function can return false
window.setTimeout(function() {$.popup();}, 20);
// for IE
e.cancelBubble = true;
return false;
});
You can try this as .delegate() has been superseded by the .on() method.
It will work fine