jQuery Applying Action To Element Being Injected - javascript

I have some elements of the page being injected by a library after the page has been loaded. These elements have onClick actions, which is controlled by jQuery. Since these items are rendered after the page has been loaded, would jQuery have an issue targeting these items to apply the onClick action?

This is a typical case for using delegated events where you bind the handler to the parent element with a specific selector passed as an argument and jQuery will trigger the handler only when the matching selector is the one triggering the event.
Read more on jQuery documentation under Direct and delegated events -> http://api.jquery.com/on/
.on( events [, selector] [, data], handler(eventObject) )
events - One or more space-separated event types and optional
namespaces, such as "click" or "keydown.myPlugin".
selector A selector string to filter the descendants of the selected
elements that trigger the event. If the selector is null or omitted,
the event is always triggered when it reaches the selected element.
data Data to be passed to the handler in event.data when an event is
triggered.
handler(eventObject) A function to execute when the event is
triggered. The value false is also allowed as a shorthand for a
function that simply does return false.
The above is to target the dynamic elements that could be added later to DOM.
Note:
Incase of delegated events, You should always bind it to closest static container that would be available when you bind the handler. More info: Should all jquery events be bound to $(document)?
However, it is always better to bind directly to the elements which can be achieved by binding the handler after the elements are inserted to DOM.

Use the form:
$("#myid").on("click",".myTargetElement", function(){
// do my click stuff here
});
Sample markup:
<div id="myid">
<div class="myTargetElement">Click Me</div>
<div class="myTargetElement">Click Me</div>
</div>
NOTE: this form binds to the document not the parent element, forcing document traversal and thus not recommended optimally:
$(".myTargetElement").on("click", function(){
// do my click stuff here
});
Example: clones the first element in the markup above and makes them blue when clicked:
$("#myid").on("click",".myTargetElement", function(){
$(this).css('color','blue');
});
$('.myTargetElement').eq(0).clone().text("New").appendTo('#myid');

Related

Single click on dynamically added element

Most people know how to bind "click' event to dynamically added element with
$('#main').on('click','.link',function(){ //some code here });
Where .link is a dynamically added element. But how the code above should look like when i want to fire function only on first click? Yes, i know about .one(), but the question is to merge .one() with .on().
jQuery docs for show .one() and .on() are the same as of 1.7:
.one( events [, selector ] [, data ], handler )
.on( events [, selector ] [, data ], handler )
If you just want it to fire on the first click, you can unbind the listener after the execution.
$('#main').on('click', '.link', function() {
$('#main').off('click', '.link');
// do whatever you need here
});
.one()
Description: Attach a handler to an event for the elements. The handler is executed at most once per element per event type.
$('#main').one('click','.link',function(){
//some code here
});
I dont think it is possible straight-away to create delegated on() which will be fired at most once( i.e one()) because of the way delegated handler is wired up.
on(): When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
Thus the event will always be fired on descendent, even you detach the handler for the current element .link using .off() method inside the handler body.
However you can still achieve this by maintaining a list of DOM elements as a closure and match the list before doing actual code work.
var elements = [];//list of elements which is responded to events at once
$('#main').on('click', '.link', function() {
if(matched){//match $(this)/ uniqueIdentifier in 'elements' array
// prevent default and stopPropagation
return;
}
elements.push($(this));//or push something like 'id' to uniquely identify current element
//..codes
});

jQuery : How to get an element by id which has been created in jQuery after DOM ready?

I'm a in the wonderful world of jQuery and today i faced a problem.
I have a list of app that user can select. To select an app they have to click on a button (id="add_app_#{app_id}"). When someone add an app i create a row on a table that list all selected apps. In this row (created in jQuery) there is a button (id="remove_app_#{app_id}") to remove the app from the table.
My issue is that i don't know how to get my remove_app button's click event (certainly because it's added to the DOM after the DOM is ready).
Though, i used .on() jQuery function...
Here is my code:
jQuery(function() {
$('[id^=add_app_]').click(function() {
var app_id, version_id;
version_id = 0;
app_id = this.getAttribute('data-app_id');
$("#app_versions_" + app_id + " option:selected").each(function() {
version_id = $(this).val();
});
hide_app_from_selector(app_id);
display_app_in_table(app_id, version_id);
});
$('[id^=remove_app_]').on('click', function() {
// I NEVER GET HERE !
var app_id;
app_id = this.getAttribute('data-app_id');
remove_app_from_table(app_id);
display_app_in_selector(app_id);
});
});
Simple answer: You use Event delegation.
You can achieve that by passing a selector as second argument to jQuery's .on().
$( document.body ).on( 'click', '[id^=add_app_]', function() {
});
Instead of document.body you should use the closest shared parent possible to prevent unnecessary event bubbling.
You can use the on() method to assign an event handler to an ancestor of a dynamically created element which exists when the method was first called. What you're doing by simply using $('[id^=remove_app_]').on(...) is assigning the event handler to that element which doesn't exist when called.
To rectify this, find an ancestor of your [id^=remove_app_] element which exists when you first assign the event handler and assign on() to that:
$('body').on('click', '[id^=remove_app_]', function() { ... });
With this particular code, you delegate the event handler to body (which presumably exists prior to calling your on() function) rather than assigning it directly to your dynamically created element. From jQuery's on() documentation:
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
To make your code as performant as possible, you'll want to assign the delegation to the closest ancestor which isn't dynamically created, for instance:
<div id="non-dynamic-container">
...
<elem id="remove_app_123"></elem>
</div>
$('#non-dynamic-container').on('click', '[id^=remove_app_]', function() { ... });

The added class doesn't trigger its jQuery function

It's an audio player: the idea is that the play button turns into a pause button (and viceversa) when clicked.
Thing is that the .pause event doesn't trigger the following function:
$('.pause').click(function(){
player.pause();
$(this).addClass('play');
$(this).removeClass('pause');
});
The css shows that the pause class is set, but the function doesn't work. Is there a way to make it work? (would be great to know why it didn't work)
jsFiddle
Use a delegated event binding to bind a handler that will be selector-aware without requiring rebinding on events.
For the purposes of your demo, the selector would be along the lines of:
$('.player_controls').on('click', '.pause', function () {...});
Delegate event bindings attach the listener to a parent element that checks to see if the event fired was fired on an element that matches the provided selector.
jQuery docs
When a selector is provided, the event handler is referred to as delegated. The handler is not called when the event occurs directly on the bound element, but only for descendants (inner elements) that match the selector. jQuery bubbles the event from the event target up to the element where the handler is attached (i.e., innermost to outermost element) and runs the handler for any elements along that path matching the selector.
Event handlers are bound only to the currently selected elements; they must exist on the page at the time your code makes the call to .on(). To ensure the elements are present and can be selected, perform event binding inside a document ready handler for elements that are in the HTML markup on the page. If new HTML is being injected into the page, select the elements and attach event handlers after the new HTML is placed into the page. Or, use delegated events to attach an event handler, as described next.
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 element could be the container element of a view in a Model-View-Controller design, for example, or document if the event handler wants to monitor all bubbling events in the document. The document element is available in the head of the document before loading any other HTML, so it is safe to attach events there without waiting for the document to be ready.
You can use event delegation for this. The issue is that binding directly (without delegation) binds to whichever elements exist at the time click is called.
$(".player_controls").on("click", ".pause", function(){
/* ... */
});
Instead of using $('.pause').click(function(){...}) you would need to start using the $.on method to start listening for objects which are still not in the DOM.
e.g
$(".pause").parent().on("click",".pause", function(event){
player.pause();
$(this).addClass('play');
$(this).removeClass('pause');
});

Use jquery inside or outside document ready

Below two scenario give me the same behavior. But What is the difference technically? (I put the below code in the last section of script tags in the body.)
$(document).ready(function() {
$('.collapse').collapse({toggle: false});
$(document).on('click', '#expandAllLessons', function() {
$('div.accordion-body').collapse('show');
});
$(document).on('click', '#collapseAllLessons', function() {
$('div.accordion-body.collapse').collapse('hide');
});
});
or
$(document).ready(function() {
$('.collapse').collapse({toggle: false});
});
$(document).on('click', '#expandAllLessons', function() {
$('div.accordion-body').collapse('show');
});
$(document).on('click', '#collapseAllLessons', function() {
$('div.accordion-body.collapse').collapse('hide');
});
Thanks.
More or less, it's doing the same thing.
With the use of .on() with a child selector, you're using event delegation to bind any future events to any elements that match that selector. document is the very tippy top of the DOM tree (and available upon script execution), so your event delegation works.
.ready() waits until the DOM has assembled, so you can, more reliably, directly bind events using methods like .click(), .hover(), etc.
So your first example is just waiting for the DOM to assemble, then delegating the event. The second example is just delegating the event immediately upon script execution.
From jQuery's documentation regarding .on():
Direct and delegated events
The majority of browser events bubble, or
propagate, from the deepest, innermost element (the event target) in
the document where they occur all the way up to the body and the
document element. In Internet Explorer 8 and lower, a few events such
as change and submit do not natively bubble but jQuery patches these
to bubble and create consistent cross-browser behavior.
If selector is omitted or is null, the event handler is referred to as
direct or directly-bound. The handler is called every time an event
occurs on the selected elements, whether it occurs directly on the
element or bubbles from a descendant (inner) element.
When a selector is provided, the event handler is referred to as
delegated. The handler is not called when the event occurs directly on
the bound element, but only for descendants (inner elements) that
match the selector. jQuery bubbles the event from the event target up
to the element where the handler is attached (i.e., innermost to
outermost element) and runs the handler for any elements along that
path matching the selector.
Whenever you do a function, regardless of whether it's $(document).ready(function(){}); or something else, all the contents inside that function can only read stuff that's at its level or above it (unless you're using return functions).
The top paragraph means that all your code won't be executed until it's loaded, but it also means that it's nested code. Nested code means certain variables and functions won't be readable from outside. Example:
function bob(){
function sandy(){
function joe(){
alert("I can access anything written by sandy, bob or ray!");
}
}
}
function ray(){
alert("I can't see anything from anybody but bob!");
}
Look at the comments first. After jQuery 1.7 on can delegate events as well:
"The .on() method attaches event handlers to the currently selected
set of elements in the jQuery object. As of jQuery 1.7, the .on()
method provides all functionality required for attaching event
handlers. For help in converting from older jQuery event methods, see
.bind(), .delegate(), and .live()."
So before jQuery 1.7 this is the correct answer:
First is better because, document ready event is triggered when HTML document is fully loaded to DOM. And then you're sure you have all elements in place, and you can bind events to them.
But if you bind event before loading '#expandAllLessons' element to DOM, then it will simply not work, as jQuery selector will not find any elements, and will not bind this event anywhere.
After 1.7 both will work almost in the same way. Almost, because in first case, when you trigger event before document will be ready, it will not be executed. In second example it will be executed, because it was attached when script was loaded.

jQuery on() event only working with $(document)

I'm using jQuery's .on() event handler and it's only working when I use $(document).
This works:
$(function() {
$(document).on("click", ".search .remove", function(e) {
console.log("clicked");
});
});
This does not work:
$(function() {
$(".search .remove").on("click", function(e) {
console.log("clicked");
});
});
Nothing happens on that second one...no errors or anything. It just doesn't fire.
You are using two different syntaxes of .on which have two very different outcomes.
Your first is:
$(context).on("event","targetselector",handler)
This binds the event to context, and any events of type event that gets to the context that has an e.target that can be selected with targetselector will trigger the handler with e.target as the context. this is commonly known as event delegation.
Your second syntax is
$(targetselector).on("event",handler)
In this case, the event is bound directly to the elements currently on the page that match targetselector, not future elements. This is essentially the same as the old .bind.
Your second example doesn't work because your elements are created dynamically. When using .on() with dynamically inserted elements, you have to bind it via an element that isn't inserted dynamically, i.e. one that exists on the page at load time.
You can continue to use document as an ancestor element but in terms of performance you might want to find an element closer in the DOM to ".search .remove".
From the jQuery docs on .on():
Event handlers are bound only to the currently selected elements; they
must exist on the page at the time your code makes the call to .on().
To ensure the elements are present and can be selected, perform event
binding inside a document ready handler for elements that are in the
HTML markup on the page. If new HTML is being injected into the page,
select the elements and attach event handlers after the new HTML is
placed into the page. Or, use delegated events to attach an event
handler, as described next.
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
element could be the container element of a view in a
Model-View-Controller design, for example, or document if the event
handler wants to monitor all bubbling events in the document. The
document element is available in the head of the document before
loading any other HTML, so it is safe to attach events there without
waiting for the document to be ready.
Your first method is the on() equivalent for the deprecated method live(). Probably your elements get inserted dynamically after the page loading has finished.
You could rewrite your code like following and it should work:
$(function() {
$(".search").on("click", ".remove", function(e) {
console.log("clicked");
});
});

Categories