JQuery events are annoying me. The thing is that I very often use
javascript (after ajax requests, etc.) to draw up new elements
(buttons, divs, etc.). I've got a list of elements
which you can press on an edit button so you can manipulate the one
linked to the selected edit button.
Now if someone submits a form to make a new element like the ones who
existed before, and I submit it with ajax and then I append or prepend
the new element into the list. After that the new edit button for the
new element isn't linked to JQuery's event system since the DOM hasn't
been reloaded after the edit button was made. If I call the same
javascript file with the events in it, then the edit button works but
then when people click other edit buttons the event happens twice for
them since they're bound twice. I've also used .bind() but that only
binds (I think) the same event twice as before. I don't remember at
the moment how I tested it. I haven't tested .one() but I would rather
not use it since some events must be called more than once.
I just wanted to ask you guys what approach you use when dealing with
the events?
P.S. I'm binding the JQuery event to the class attribute that all the elements have. If I was going to bind this to each element based on ID, then this wouldn't be a problem because then I would just use .bind(). By writing this I suddenly though of using .unbind() and then .bind() to link the elements to the events system. What do you think of that? Would you do it in another way?
Thanks in advance.
Kristinn.
You're looking to use $.fn.live:
$('a').live('click', function(e) {
e.preventDefault();
alert('im attached even if the DOM has been updated!');
});
http://docs.jquery.com/Events/live
Your question is a bit general, but I have a feeling that what you're looking for is jquery live
http://www.thewebsqueeze.com/tips-and-tricks/tip-for-jquery-live-event.html
http://simpable.com/code/jquery-live-events/
http://www.thefutureoftheweb.com/blog/jquery-live-events
http://kylefox.ca/blog/2009/feb/09/live-event-binding-jquery-13/
Related
I have a bunch of jQuery functions that use the .on event because I want to prevent reapplying the event to the same element.
However some people created plugins (e.g. Owl Carousel) and I don't know how to prevent this event from reapplying.
Currently I am using the plugin as following:
HTML:
<div class="init-owl"></div>
JS:
$('.init-owl').owlCarrousel();
$('.init-owl').removeClass('init-owl');
Whenever a second element gets loaded in the page using e.g. AJAX, I want to only apply the event to the newly added element.
Question: What I dont understand is how the event stays stuck to the DOM?
To better grip what is happening, I was wondering how an event in general gets connected to the DOM?
Is there a better way to prevent events applying to the same DOM elements?
If I wish to write my own plugins, I would need to know how javascript works, right?
Question: What I dont understand is how the event stays stuck to the DOM?
Once an event is bound to an object, it gets removed when the object gets garbaged collected. So if a DOM element is really gone and there are no references to it, then the event will get swept up as well.
To better grip what is happening, I was wondering how an event in general gets connected to the DOM?
I'm not sure how far you want to dive into this. Maybe it would help if you stop thinking about the DOM and events and look more at just regular events bound to objects. Basically an object does something, or something is done to it and some underlying code (in the browser's code in this case) triggers an event on that object. The implementations between browsers may differ, but basically you will have a key or string (the event name) that maps to a collection of functions. When you add an event listener, you add another function to this collection. Then when something triggers that event, it iterates through the functions and executes them. That's a real basic explanation, but I hope it makes things a little more clear.
Is there a better way to prevent events applying to the same DOM elements?
Make sure you don't add the events again by writing better code. I don't believe you can dive down into an element and look to see if it has events bound to it. You can however change your jQuery selector to only target newly added elements. If you have to, mark the elements that you have added events to with a class or something. Then you could target your elements by doing $('.init-owl:not(.already-bound)'). There is a better solution to your problem, I can assure you, but we might need more context and code to see a better way to help you.
EDIT:
You can look into jQuery's off() function to remove events. That may help you too.
I have some dynamic content that I am loading in via jQuery .load(). I have been trying to utilize the .on method, since the .live method is deprecated to bind elements to the page before they actually exist. I was able to achieve that but in this particular case the method I am invoking is firing twice. Any ideas and or solutions would be greatly appreciated.
I have tried unbinding('click') before binding and that seem to work but it causes the item I am trying to click on to fire on the second click. I also tried event.stopPropagation with no luck. Below is the code I currently have in place. I am currently utilizing the setTimeout approach until I can find a suitable solution.
$('#content').unbind('click').on('click', '.alternate-options', function(event){
//setTimeout(function(){
$('.alternate-option').each(function (index) {
$(this).bind('click', function (event) {
event.preventDefault();
$('.alternate-options li').each(function () {
$(this).removeClass('current');
});
$(this).addClass('current');
});
});
//},100);
});
Joel's answer is correct, but I thought it would be useful to explain it a little bit for others that might come across this in the future.
The deprecation of live() caused a lot of confusion for folks, but the replacement recommended technique really is a lot better once you understand it well.
First, the basics. If you're dynamically adding DOM elements to the page, you have to choose an approach to handling events on them.
One approach is to manually add event handlers to each element as they are dynamically added, typically you'd either add a data-* attribute to indicate the unique attribute of the dynamically added element, or you'd stick an object onto the DOM element itself that can be later retrieved in the event.
The old jquery approach let you simplify this process by using the live() API. In this case, you'd have a selector, and the DOM would be monitored for changes to that selector. This was pretty simple from the developer's perspective but had some major drawbacks. For more information this SO post describes some of the issues well: What's wrong with the jQuery live method?
The newer, and better approach is to use the on() method and look at a parent container DOM element, with an optional selector as a filter.
The reason why this works well is because it uses the normal DOM event bubbling behavior. When you trigger a bubbleable event (like click) on an element, that event will naturally "bubble" up the DOM, with a chance to catch it on each parent element all the way up the scene graph.
In the case of dynamic elements, this works really well. If you have a div, for example, that you're listening for a click event on, that event will be caught for any click events that were triggered by children, no mater when they were added to the DOM.
It would be a little annoying, however, to have to do a bunch of "if" statements inside of that click handler to determine if the element that was clicked was the one you're interested in. This is why the smart folks on the jquery team added the optional filter argument to the on() function. It let's you filter out events that were triggered by elements you don't care about.
This is why Joel's simple example works: you don't need to worry about directly adding event listeners to child elements or anything like that, you are just listening to the events on the container and filtering on the specific elements you care about.
This should do what you want it to:
$("#content").on("click", ".alternate-option li", function () {
$(".alternate-option li.current").removeClass("current");
$(this).addClass("current");
});
No need to rebind events or anything.
Here's a fiddle illustrating this binding with the dynamic content (both adding new list item or completely replacing the entire list):
http://jsfiddle.net/2jKCL/
I have a click event created by plugin and after I load $.ajax and only replaced(update) the area where contain the click event, the event will lost.
<span ref='B'><span ref='A'></span></span>
//click event is on A, but I replaced the html inside of B to <span ref='A'></span>;
its update, so the replaced html are the same.
I read many related problems, I found the solution are
.live() // will not work
.delegate(), // work
.on(), // work
The solution is I should bind the event on B instead of A like B.on('click', A, function(){})....
However, my structure is very hard to change, I rather want to find a solution that can prevent lose events while replacing or alternative.
Please advice, thank you very much.
Solved by using detach()
Since event bindings exist within the context of DOM elements, when you start removing or replacing DOM elements, you need some mechanism to re-establish the event bindings.
A good approach is to use delegation via a parent element (as you have stated - B.on('click', A, function(){}).... ).
The other alternative is to always re-establish the bindings at the point at which the DOM elements in question are removed or replaced (so in your scenario, it sounds like this would have to be in the Ajax callback/completion handler) however this is generally a poorer approach and is much less elegant than the delegation method.
If you are concerned about applying the on event to a certain element on the page. Then I would recommend just binding the event to the document.
$(document).on('click', '#id-of-a', function(){
});
Read this blog post for more information concerning this.
I found a solution. This is possible with using .detach()! tested.
I am new in AJAX. I have searched a lot on Internet but only got basic AJAX steps. Now I am writing codes using AJAX but a common problem I am facing continuously.
When I place return text in the particular id of HTML page, Javascript effects do not work. CSS works fine but Javascript effects like table sorting, jQuery effects or any other effect does not work. I know I am missing some concept here. But didn't get any effective answer.
Please suggest me what should I do? And what is the concept behind this...
The new HTML you're adding to the DOM (page) didn't exist when your jquery ran the first time and bound events to elements on the page. You're probably using $("something").click(...) or .bind("click", ...). Instead of these use the delegate function from jquery. Delegate is generally more flexible and faster than live. For instance you can not stopPropagation in a 'live' binding.
Jquery Delegate
Why Delegate is better than Live
Here is another SO answer that explains the benefits of delegate
What's most likely happening is that your events are getting unbound because you update the DOM with new elements. The easiest solution is to use the live method to bind to events : http://api.jquery.com/live/
Or you can simply rebind the events to the elements after insertion to the DOM just as easily.
EDIT
As user kasdega points out, another alternative is to use delegate : http://api.jquery.com/delegate/ Delegate works by using the bound root elements as the context to rebind events to DOM elements that may appear in the future.
What happens in jQuery when you remove() an element and append() it elsewhere?
It appears that the events are unhooked - as if you were just inserting fresh html (which I guess is what happening). But its also possible my code has a bug in it - so I just wanted to verify this behavior before I continue.
If this is the case - are there any easy ways to rehookup the events to just that portion of HTML, or a different way to move the element without losing the event in the first place.
The jQuery detach() function is the same as remove() but preserves the event handlers in the object that it returns. If you have to remove the item and place it somewhere else with everything you can just use this.
var objectWithEvents = $('#old').detach();
$('#new').append(objectWithEvents);
Check the API docs here: http://api.jquery.com/detach/
Yes, jQuery's approach with remove() is to unbind everything bound with jQuery's own bind (to prevent memory leaks).
However, if you just want to move something in the DOM, you don't have to remove() it first. Just append to your heart's content, the event bindings will stick around :)
For example, paste this into your firebug on this page:
$('li.wmd-button:eq(2)').click(function(){ alert('still here!') }).appendTo(document.body)
And now scroll down to the bottom of this page and click on the little globy icon now buried under the SO footer. You will get the alert. All because I took care to not remove it first.
use jQuery1.3.1 live() to bind events and you won't need to worry about this..
Update: live events are deprecated now, but you can get the same effect from $(document).on().