What's the best way to event handle dynamically created HTML? [duplicate] - javascript

This question already has answers here:
Adding event listeners to dynamically added elements using jQuery [duplicate]
(4 answers)
Closed 8 years ago.
It's very easy to event-handle when dealing with items the document has from the get go:
$(document).ready(function() {
$('.element-in-question').on("event", function (event) {
//do what you need to do during the event
});
});
My problem is how would I best deal with dynamic elements. For example, let's say I dynamically load notifications, some of which are friend requests during an AJAX request. Would I create the event-handler in the success callback, or would I do it somewhere else?
The way I would currently go about it:
$(document).ready(function() {
$.ajax({
url: '/friendships/requests',
type: 'GET',
success: function(responseData) {
//dynamically create your elements (with classes accepted and rejected)
$('.accepted, .rejected').on("click", function(event) {
//do what is needed in this event
});
}
});
});
Is this the idiomatic way to go about it, or is there another way I probably should be going about it?

use jquery's "on" merhod to bind event handler to parent element (which will not change) and pass a selector of the element you want to listen to:
$('.parent').on('event', '.child', handlerFunction);

If you dynamically create an element, such as a 'button', that was not on the page before, handle it like this:
$(function() {
(function() {
$('body').append('<button id="newButton">Hello World!</button>');
})();
$('body').on('click','#newButton',function() {
console.log($(this).html()); //"Hello World!"
});
});

I think this is the (partly) right approach. You cannot and should not apply eventhandlers to objects that might or might not be available, even if possible.
If the situation would involve 10000 different eventhandlers, they should be only available when present in dom. When removed the eventhandler should be removed as well.
The way you do it is rudimentary but correct.
2 other thoughts. If you bind the listener in the ajax callback you might add to the "stack" of events, since they are not replaced. Not a good thing. If the ajax query will happend more than once, do not add it again, if not removed first.
Another aproach might be to just add them to all pages, if this is a small page/application and first check that the element exist. Like so:
if ($('#id').size() > 0) {
// bind events for #id here
}

Related

Extend jQuery's .on() method with custom events

There are numerous questions on SO about custom jQuery events. Almost all of them only have answers suggesting the use of $.fn.extend().
Here is my script using $.fn.extend() to detect when an element has stopped scrolling:
$.fn.extend({
scrollStopped: function (callback) {
var $this = $(this)
$this.scroll(function () {
clearTimeout($this.data('scrollTimeout'))
$this.data('scrollTimeout', setTimeout(callback.bind(this), 150))
}.bind(this))
return this
}
})
$('element').scrollStopped(function () {
alert('Scrolling has stopped')
})
Like the title of this question suggests, what I want, however, is not an event that has to be added with .scrollStopped(), but one that can be added with the .on() method, like so:
$('window').on('scrollStopped', function () {
alert('Window has stopped scrolling')
})
$('div').on('scrollStopped', function () {
alert('Div has stopped scrolling')
})
I've been scanning the jQuery source trying to find a way to do this. After copy-pasting a bunch of snippets for an hour or two, I decided to look for an answer on SO, which was surprisingly hard to find. This answer helped me a bit, but (like I said in the comments of that answer, one hour ago) the events that trigger the custom event there are hardcoded to the body and then sent to the elements to which the .on() method is bound.
There are two problems with the approach:
The event logic still isn't part of jQuery's internal structure. It's simply part of an event triggered by the body (not a deal-breaker per say)
Scroll events (like the one I'm trying to use) don't bubble, meaning that converting the events from keydown to scroll won't work
I've simplified the Fiddle from the answer here.
I know I could use a native event and set useCapture to true to capture all scroll events on the page, but seeing there are no answers anywhere on SO that explain how to do what I want, I think it's worthwhile to take a look at this.
The only reason I want to do this, is because I want to be able to write things like:
$(window).on('beforeunload scrollStopped', function () { savePageScroll() })
Instead of:
$(window)
.on('beforeunload', function () { savePageScroll() })
.scrollStopped( function () { savePageScroll() })`

jquery bind event with ajax content [duplicate]

This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 7 years ago.
We have an external js file loaded on our static html page with a jQueryUI dialog opening a url with jQuery.load() from a button click. Inside the ajax content returned is a handful of input elements, all number type. We need to bind the keydown and change events to the number inputs, however it's not binding since the .bind() is happening before the elements are in the DOM from the ajax result. We know about .on() but having an awfully hard time wiring it up. I know we're missing something simple; any suggestions?
Basic Fiddle recreation: http://jsfiddle.net/jbwebtech/wvufLket/
There's nothing too hard to grasp that it's not already explained pretty decently inside the jQuery .on() documentation: http://api.jquery.com/on/#direct-and-delegated-events
http://learn.jquery.com/events/event-delegation/
$(staticElementParent).on("event1 event2", dynamicElement, function(){ /*stuff*/ });
In your specific case:
fiddle demo
var ajax_content = '<input type="number"><br><input type="number"><br><input type="number"><br><input type="number">';
$(function () {
console.log('jquery ready');
$("body").on('input', 'input[type=number]', function (event) {
console.log('on() input successful');
});
// "AJAX" ;)
$('button').click(function(){
$('#ajax_container').html(ajax_content);
})
});
As always, read the Docs
Can't you just put your bind the callback of the .load() function? Or if you can't(im not sure), you can change a variable in the callback and check if it's loaded in the bind.
Or you can put the bind in the file that loaded with jquery(at the end of it).
Sorry, i can't comment yet.

How to avoid multiple loading of jQuery functions?

I am using following code on my page which I am loading in ajax.
$(document).ready(function() {
$('#button_id').click(function() {
//Do Something
});
});
Now When I click on the button action happens multiple times. I know that its happening because I am loading the ajax page multiple times.
Please help me solve this.
You can use .off() to remove existing listeners:
$(function() {
$('#button_id').off('click').click(function() {
//Do Something
});
});
If I am wrong about your implementation I apologize. Your problem may exist because the binding is created on first page load and then on subsequent ajax loads with new scripts being inserted and creating duplicate bindings. You should prevent any bindings from being generated on ajax loads to prevent duplicate bindings unless you are good with cleanup.
If the button you are clicking on exists in the ajax loaded area then you should use delegation to ensure that the click handlers still work.
For example:
$( "body" ).on( "click", "#button_id", function() {
//do something
});
This will add a binding to the body element, but more specifically to the id #button_id. A click event on the button will propagate and bubble up to the body element (or whatever parent element you choose).
This makes it so that dynamic elements can be inserted in the DOM and only one event handler is needed to listen for it.
No need for .on() or .off() calls for individual ajax loads. This allows your bindings to be much cleaner.
Of course, if your button is not likely to exist on the page all the time then it would not be a good idea to keep extra bindings. Only create these types of binding if they are always needed to prevent optimization issues.
A cleaner solution would be to remove that code from the ajax loaded HTML and use one single event handler in the master page
I guess your problem is the event is firing many times.
To fire only once try this:
$(document).ready(function() {
$('#button_id').on("click",function(e) {
e.preventDefault(); // This prevents the default non-js action (very used for anchors without links or hashes)
e.stopPropagation(); // Prevent the bubling of the event and spread more times
//Do Something
});
});
If doesn't work with e.stopPropagation(); try with e.stopInmediatePropagation();
Adding documentation for the last method I suggested. It could solve your problem.
http://api.jquery.com/event.stopimmediatepropagation/

Javascript Delegation Best Practice [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 9 years ago.
Improve this question
I am developing/have developed a few systems now, I have come across a querk in my programming, I seem to delegation all my JQuery functions to the document like so:
$(document).on('click', '.modal-editor', function () {
//code
});
$(document).on('click', '.another-class', function () {
//code
});
$(document).on('click', '#another-id', function () {
//code
});
Is there anything wrong with this?
UPDATE:
Ok so in essence there is nothing wrong with this until:
The applications reaches a scale where delegation needs to be more localised to prevent slowing down UI. "Every event that takes place needs to run every selector against every element between the e.target and the document"
Delegation like this will increase the chances of unexpected behaviour like propagation if nested functions are used.
Referring back to my question (best practice), best practice would be to delegate the event to the closest static element as possible to attempt to avoid the above.
"Is there anything wrong with this?"
Yes. Every event that takes place needs to run every selector against every element between the e.target and the document. That's unnecessarily expensive.
Basically like this simplified pseudo code:
// start on the e.target
var node = e.target;
// For the target and all its ancestors until the bound element...
while (node && node !== this) {
// ...test the current node against every click selector it was given
for (var i = 0; i < click_selectors.length; i++) {
// If a selector matches...
if ($(node).is(selector)) {
// ...run the handler for that selector
}
}
node = node.parentNode;
}
Also, if your code or some other code you loaded binds a handler between the e.target and the document, and calls e.stopPropagation(), the event will never reach the document, and so yours will not fire.
It's much better to keep your delegation as close to the intended element(s) as possible. And use it primarily for dynamically loaded elements because of the extra overhead it adds, especially in the case of events that fire rapidly.
Yes. In itself there isn't, nto really, but delegating all events from the document (root of the DOM) does meant that all click events, including those you're not interested in will be handled at least partially:
$(document).on('click', '$another-id', function(){});
is a particularly bad idea, in that respect: you're after a single element, but if I click anywhere in the body, jQ will do something like:
if (event.target.id == 'another-id')
{
return callback.call(event.target, $(event));//where callback is the anonymous function you passed to on
}
return event;
So all click events result in a function call. This can slow your UI down.
By no means should you stop delegating events, but bind the listeners wisely. If all the DOM elements you want to use are contained within the #container div, for example, then bind your listeners there. If you want to handle navigation events, bind the listener to the node that contains all the navigation elements your after
Add to that that, if you cancel an event, but failed to stopPropagation, the event will still end up invoking all your other listeners that might be queued. Returnign false can also cause trouble, seeing as, in jQ, return false is translated to e.preventDefault(); e.stopPropagation(). So be careful when dealing with nested elements, if you want to handle a click on links in your page, but also on elements like <a class='navigation'>, both handlers might be called, depending on the selectors used:
$(document).on('click', 'a', function(){});//is called for all links
$(document).on('click', 'a.navigation', function(){});//is called for navigation
Which will be invoked first? Which would you want to use in a given situation? There's room for error here, that shouldn't be there:
$('#navContainer').on('click', 'a.navigation', function(){});//is called for all links
At least makes things a tad safer, clearer and lighter, too.
If you want to delegate an event, using an ID selector, and the element already exists in the DOM, don't delegate:
$('#another-id').on('click', function(){});
is shorter, and will likely even be faster anyway
Delegation is great when you want a particular event to apply to multiple elements, without each element having its own event handler. In the instance above this is likely for the first two methods.
Where there will only ever be one element with that matches the criteria in theory it might have a performance implication, depending on the size of your document, due to every event having to be tested against the handler filter to see if it matches.
Also, if every method is added as a delegate, and you are frequently loading and uploading sections of the page those events are going to hang around longer than the elements on the page that they belong to.
You can delegate your handlers to something other than the document element, such as a surrounding div or something instead perhaps in this kind of scenario. Or use event namespacing to make sure events aren't being added multiple times.
There is nothing wrong with your code,
$(document) is not necessary to be place always in event delegation, that selector refers to the closest parent element to which your elements are added dynamically:
Syntax:
$(closest parent selector).on('event','target selector', function(){
});
You can also use document.body
$(document.body).on('event','target selecor',function(){
});
Side Note: For existing DOM elements you can use normal click event.
$('selector').click(function(){
});
OR
$('selector').on('click',function(){
});
For more information refer to .on() API documentation.

How can I make jQuery work even on objects not yet created? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Binding dynamically created elements in jQuery
Is there a way I can change the following code:
$('#city')
.focus(function () {
$('option[value="99"]', this).remove();
store.setItem($(this).attr('id'), $(this).val());
})
.change(function () {
store.setItem($(this).attr('id'), $(this).val());
$(this).attr("title", $("option:selected", this).attr("title"));
$('#detailData').html("");
});
So that it works for selects even if they have not yet been created as long as they have the class "update-title". for example:
<select class="update-title">
I saw some implementation using live but someone said it was not good to use. Also is there much of an overhead doing this. Would it be better for me to add the code after I am sure the selects have been created with document.ready() ?
Have a look at on. This is a replacement for live which would do this for you.
You need on:
$(document).on('change', '.update-title', function(event){
// your business here
});
This way the document listens for any events triggered on .update-title elements, wether they were there at document load or not.
Best way is to use delegation - so you bind the event handler to a parent element that exists at the time the dom is loaded - It will in turn listen for events on the given elements and handle them
ex with jQuery 1.7+
$('body').on('click','.update-title', function(){ // <-- all click events will now bubble
// up to body - and it will listen for events from .update-title
// code here
});
It is best practice to bind the event handler to the closest parent at dom load as you can see the overhead in binding to the body/document - all click events will bubble up - wheareas if you bind to a parent element only those inside that element will bubble up.
Yes,you sure thtat the selects have been created with document.ready(), if the html is not loaded in ajax

Categories