Newly added elements $.each and events - javascript

I’ve read many posts already on the $.each and newly added elements + event attachment. Many of the current Questions regarding this topic on StackOverflow don’t seem to work for me. $.on() is normally recommended since it allows us to append new elements and still maintain a single event listener + handler.
In my current code:
1.$(‘input[type="checkbox"]’).on(“change”, function(e){});
//I do a logical if-statement, if(this.checked) else
//With-in the if-statement I run $.each, however, once I have appended new element in this context a new li to the ul, it stops working.
Out of the curiosity has anyone encountered something like this before, and if YES, how have you folks solved this?
Some StackOverflow posts I have already seen:
jQuery $(element).each function doesn't work on newly added elements
jquery: dynamically appending li items to ul then adding click, but click runs through each li
Event binding on dynamically created elements?

Currently what you are using is called a "direct" binding which will only attach to element that exist on the page at the time your code makes the event binding call.
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time.
As you are creating elements.
You need to use Event Delegation. You have to use .on() using delegated-events approach.
General Syntax
$(document).on(event, selector, eventHandler);
Ideally you should replace document with closest static container.
Example
$(document).on('change', 'input[type="checkbox"]', function(){
//Your code
});

Related

How to use JQuery.find() when DOM is dynamic

I have a cascading menu with the following flow;
click on an item from menu-1
creates and updates menu-2 li elements
click on an item from menu-2
creates and updates menu-3 li elements
etc..
```
$firstMenu = $('.prime-menu');
$secondtMenu = $('.second-menu');
$thirdMenu = $('.third-menu');
```
As i'm traversing through different elems. within each menu, using find() comes as a blessing, the issue is that the script loads when no menu other than the first menu is created so $secondtMenu.find('.item-year').click(function (clickEvent) {}) is 0 length.
What are my options in JQuery to make my find() functions work on elements that are not loaded yet in the DOM?
I thought of creating an event listener, but I think there are more terse approaches than that.
You should use delegates when dealing with dynamic HTML. For instance, use an outer element like document or body to "start" your finds.
$(document).find(".prime-menu");
EDIT: Find and event delegation
The solution was to use find with event delegation. Example event.
$(document).find(".prime-menu").on('mouseenter', '.track-table tbody tr', function(){ });
You state that when you click on an item from menu-1 it creates and updates menu-2 li elements. In this function is where you should do your event binding. The DOMElement will exist in js before being added to the dom, and that is where your bindings should be set.
If you need help share this code with us I'm sure myself or someone will be able to help you sort it out.
Bind the click handler to the menu parent, not the actual menu items.
Something like this might work...
$("#menuparent").on("click",".item-year",function(event) {
var clicked_element = event.currentTarget;
});
Doing it this way, even if the element with class .item-year is added to the dom after the click event is bound, it will still register the click.

Have newly added DOM elements use function

I have the following set in my JS:
$('.selector').selectpicker();
When new DOM elements are added to the page, the above method doesn't work on the new DOM elements. I know that, in other cases, I can do the following such that newly added DOM elements work:
$(document).on("click", ".class-here", function() {
});
But how can a method like the first changed to work with new DOM elements (rather than calling that same method again)?
The answer will depend on the function you're calling (here selectpicker).
If you're talking about the bootstrap function, you would do:
$('.selector').selectpicker("refresh");
After having changed the DOM.
You might use the level 3 event for DOM node creation, like .on("DOMNodeInserted",(selector),(function)) to execute your function whenever an element fitting the selector is inserted. See How to catch creation of DOM elements and manipulate them with jQuery
Your problem is regarding binding new element in DOM, Prior version of jquery use bind and unbind method for new element in dom.
But If you use jQuery 1.3+ then you can write
$('selector').live('event',function (){ //do some action });
In above jquery, You didn't need to bind/unbind element on DOM.
But latest version of jQuery 1.7+, You can directly use .on() method which you mentioned above, It mean that you didn't care to bind, unbind on 'DOM change'.
On() is simple that you can write common callback for multiple events on particular selector.
I hope that this details is useful to you and you got your answer. If you use 'on()' then you not need to bind element in DOM.

jQuery :last-child not updated as additional children are added?

I have a set of inputs where I would like to add another input when the last input currently displayed receives focus. The form starts out with 2 inputs.
$('.answer_fields input:last-child').focus(function(event) {
var nextfield = $('.answer_fields input').length + 1;
$('.answer_fields').append('<input class="form-control answer_field" placeholder="Answer option #' + nextfield +'" id="answer' + nextfield +'">');
});
As it currently stands, additional fields are only appended when the 2nd input (the original "last-child") receives focus.
Checking the source seems to show that the inputs are getting added to the DOM as expected. What am I missing?
The jQuery selector is evaluated ONCE at the time you run the code to install the .focus() event handler. It isn't adjusted live as things change. You can switch to event delegation if you want that type of behavior.
A similar event handler using event delegation that will be evaluated live would look like this:
$(some common parent selector).on("focus", ".answer_fields input:last-child", function() {
// code here
});
Working demo: http://jsfiddle.net/jfriend00/ZZLPJ/
First off, your original code did not work because you were attaching event handlers to specific elements that existed at the time you ran your first code. Which events you had event handlers attached to did not change as your structure changed in the future so new elements would not have event handlers on them and the :last-child portion of your selector would not be re-evaluated.
Event delegation works because certain events that occur on a given element will bubble up the DOM hierarchy through each parent. You can attach a single event handler to a common parent and when that parent is notified that a given event occurred, it can then dynamically evaluate the selector you specified vs. the child item that originated the event and if it's a match, fire your event handler. This gives you the "real-time" selector evaluation that you want in your case.
To give you an idea, these are what I call "static" event handlers:
$(".test:last-child").focus(fn);
$(".test:last-child").on("focus", fn);
This a delegated (and thus dynamic) event handlers:
$("#parent").on("focus", ".test:last-child", fn);
For more info on this topic, see these other answers:
jQuery .live() vs .on() method for adding a click event after loading dynamic html
Does jQuery.on() work for elements that are added after the event handler is created?
Should all jquery events be bound to $(document)?
Just checked to be sure and the focus event does bubble event delegation will work with it.
This is a common question for people starting to use jQuery. It turns out that the named eventListeners (i.e. $.fn.click, $.fn.focus, etc.) attach an event listener to each matching DOM element when the page is first loaded. These listeners are defined on the matching element themselves, meaning that each one is unique to the element it is attached to.
You will hear this method compared to what is commonly known as event delegation. Event delegation involves attaching an even listener to a shared parent element that will check the event.target attribute to see if it matches your criterion (in your case, .answer_fields input:last-child). This differs from attaching a unique listener to each DOM element you wish to target because it allows you to add new nodes to the DOM dynamically, which will then be handled by the parent node's listener like any other node.
To accomplish event delegation using jQuery, use the .on() function on a common ancestor (I'll use document as an example) and listen for all matching elements as the second argument of the function call:
$(document).on('focus', '.answer_fields input:last-child', function(event) {
// your code here
});
Not only will this solve your problem with dynamically created DOM elements, but event delegation will greatly improve the performance of your page by reducing the total number of event listeners attached to elements in the DOM.
For more information about event delegation, I'd encourage you to check out this tutorial and this SO question

Removing a dynamically generated element

I have implemented a user-generated keyword list for a project I'm working on, using jQueryUI autocomplete to suggest existing keywords.
On selecting the autocomplete suggestion, the returned string is added to the html of a div, as a child div.
I would like to add a removal function whereby the user can remove the child div if erroneously entered.
I've tried multiple suggested answers from Stackoverflow and elsewhere, but can't seem to get it working.
I've created a fiddle containing the pertinent elements.
The most logical solution to me was:
$('.keyword-entry').click(function(e){
var id = $(this).closest('div').prop('id');
$('#'+id).remove();
}
Though it would appear this doesn't work.
Whilst a solution to the problem would be very much appreciated to save my dwindling supply of coffee from running out this evening, I would also appreciate a rundown as to why I'm going wrong.
Thanks in advance.
Event delegation.
It's basically that you're attempting to attach an event to an DOM element that doesn't exist in the DOM at the time of load. Rewrite the .click() handler too:
$(document).on('click', '.trashYes', function () {
$(this).remove();
});
Fiddle: http://jsfiddle.net/6bBU4/
What it's doing is that, it's attaching the .click() event to the document (The top most DOM element) will travel down to find any new .trashYes, thus successfully executing the .remove(). This doesn't have to be bound to the document but to any DOM element within the document as well at load.
No need to get the id and then try and find it again, just do this...
$('<div id="'+id+'" class="keyword-entry" style="z-index:0">'+ui.item.value+' <--I want to remove this</div>')
.appendTo($('#keyword-list'))
.click(function(e){
$(this).remove();
});
when adding the keyword entry

jQuery on() method - which way to use is better for performance?

Is it better to attach the on() event to the document or a closer parent?
Note: Initially this question had another aspect and a different topic. It became obsolete really quickly (typo in the source code)
The best key for performance using jQuery is to use an id as the initial identifier. For example:
$('#my_id').on('click', 'tag.my_class', function () {
...
});
This allows jQuery to go straight to the container, and then begin trawling from there.
if you bind the "on" event to the closest parent will produce exactly what are you looking for, click function will works fine even if it is appended to document, but in future if you append any elements with class "clickable" will also get binded. so its always good practice to append the "on" event to closest parent rather than whole document.
if you want more specific you can use
$("ul.media-grid").on('click', 'li.clickable', function () {
alert("works")
});
as it will get the ul with the class "media-grid" and appends the event to the li's with class "clickable"

Categories

Resources