Why is jquery event delegation not working? - javascript

I have the following html:
<ul id="contain">
<li class="active_one"></li>
<li class="two"></li>
</ul>
And the following jquery:
$contain = $('#contain'); //going to use a lot
$contain.on('click','li.two', function(){
console.log('working');
//plus do other stuff
});
The above does not work, but When I change it to:
$('body').on('click','li.two', function(){
console.log('working');
//plus do other stuff
});
Then it works, but I know the best practice is to drill as close as possible to the parent element of what I am trying to work with, yet every time I try to do that, I am clearly doing something wrong because my parent level selectors are not working.

It means that #contain itself is not a static element, you should select closest static parent of the element. Otherwise jQuery doesn't select the element and delegation fails.
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().
However, in case that element is static, you are selecting the element too soon, you should wait for DOM to be ready.
$(document).ready(function(){
var $contain = $('#contain'); //going to use a lot
$contain.on('click','li.two', function(){
console.log('working');
//plus do other stuff
});
})

So, I will try to explain how jquery on(change) works,
if you have
$(document).on('parameter 1','parameter 2', function(){} )
essentially, jquery will check if event mentioned in parameter 1 is executed or not. Then it will check if parameter 2 is present,
if so, Then it will check if the event mentioned in parameter 1 was triggered in the element mentioned in parameter 2. hence you can prevent on(change) events whenever the document changes.
this will help you to bind dynamically loaded elements by making sure the loaded elem has id and then binding events to that id alone. and whenever a document event occurs, the dynamic one will be executed.

Related

Changed data attribute not recognized in jquery selector

I've the following html structure
<body data-page="first">
<div class="start">Test</div>
</body>
and the following js
$('body[data-page="first"] .start').on('click',function (){
body.attr('data-page','second');
});
$('body[data-page="second"] .start').on('click',function (){
console.log('Test');
});
I would expect, that after the second click on .start, the console would show "Test", but it doesn't...
Can you tell me what I'm doing wrong?
Thanks in advance!
While you have your answer, I don't think the essential point has been made in any of the answers so far, and that is that the binding of an event handler must happen after the target element exists.
When you try to bind an event handler to a particular element in the DOM, the element must exist at the time. If it does not exist, the handler has nothing to bind to, and so the binding fails. If you later create the element, it's too late, unless you re-run the binding statement.
It will soon become second nature to call appropriate event handler binding statements after you create a new element (by modifying the HTML using javascript) that needs a handler.
For instance, in my current project I regularly make AJAX calls to a server to replace blocks of HTML as things happen on the page. Even if some of the new elements are exactly the same as the ones being replaced, they will not inherit any bindings from the replaced elements. Whenever I update the HTML I call a function that contains necessary statements to bind my event handlers to the new copy of the active elements.
Your code would work if you made the following change:
$('body[data-page="first"] .start').on('click',function ()
{
body.attr('data-page','second');
$('body[data-page="second"] .start').on('click',function (){
console.log('Test');
});
})
A couple of other (off-topic, but related) points:
It's possible to bind a handler to an element multiple times. The trick to avoiding this is to include the .off() method in the chain before binding (noting though that .off("click") will unbind all click handlers bound to that element, not just yours) e.g.
$("#mybutton").off("click").click(function(){myHandler()});
"the arrow function doesn’t have its own 'this' value" () so don't use arrow functions in event handlers if you plan to reference any of the element's properties via 'this'. e.g.
$("#mybutton").off("click").click(() => {console.log(${this.id})}); // >> "undefined"
The issue is that the page is rendered with the data-page set to first, and when you click again on it, that part of javascript still see "first", since is not rerendered, so you need a dynamic function, the read all the intereaction with that button, and than check wich value that attribute has. Like this you can make infinite cases, and still go on.
$('body .start').on('click',function (){
const attr = $('body').attr('data-page');
if(attr === 'first') {
$('body').attr('data-page','second');
} else {
console.log('second');
}
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<body data-page="first">
<div class="start">Test</div>
</body>
And if you don't like the fact that is targetting all the "body" wich is weird, becouse you should have only 1 body, you can use an ID to target the right one
PS: is never a good idea to duplicate your function, if you can set everything in a dynamic function, that reads everything, is easier to debug in the feature, and is lighter and more clean to work on
$('body[data-page="first"] .start').click(function (){
var body = $('body[data-page="first"] .start');
body.attr('data-page','second');
});
This method can help :
var timesClicked = 0;
$('.start').on('click',function (){
timesClicked++;
if (timesClicked>1) {
console.log('Test');
}
});

.change() method with visible selector issue

I am trying to have a change method for all visible select elements.
$('.select_elements:visible').change(function() {
// function
)};
The above doesn't seem to run when I change the select element. However if I write this it seems to work.
$('.select_elements').change(function() {
if ($(this).is(':visible')) {
// function
}
)};
Can anyone explain this? It wasted years (hours) of my life...
The difference is that the first version, where :visible is in the selector, only attaches the change event handler to the select elements that are visible in the DOM when the page loads.
The latter version attaches the event handler to all select elements, then checks their visibility at the time the event was fired.
Rory McCrossan's answer is an excellent explanation of what is happening. I am answering as well to propose a more performant and (to my mind at least) idiomatic solution than checking the visibility of elements in your change handler.
$(document).on('change', '.select_elements:visible', function () {
// ...
});
This approach attaches only a single change handler to the document (you could pass it any parent of your .select_elements), which fires whenever the element that is changed matches the selector in the second argument. Since that selector is evaluated for every change event, it will also fire for visible elements that weren't visible when the handler was defined.

Calling a function , when element is Live

How can i call a function when element is live, i think we just raise a function by raising an event by live..
$('#a').live('click',function(){
//some code
});
but i need a function to be called when element is lived,Any Idea.
The context is
initially i dont have a select box , if i got a particular select box
then i want call a function that pushes result into it..
you can use on delegate event...
$(document).on("click",'#a',function(){
alert(clicked);
});
however,it is always recommended that you use the closest static elements that are in the HTML markup, then $(document) for better performance.. you can read more about on delegate event here..
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
Live is deprecated and removed on jQuery 1.9. Use .on()
This enable click on any element with id #a added after page is loaded
$(document).on('click','#a',function(){
//Code here
});

jquery issue with on and live

I have the following code:
var $reviewButton = $('span.review_button');
$reviewButton
.live('click',
function(){
$('#add_reviews').show();
}
)
Later in the script, I use an AJAX call to load some content and another instance of $('span.review_button') enters the picture. I updated my code above to use '.live' because the click event was not working with the AJAX generated review button.
This code works, as the .live(click //) event works on both the static 'span.review_button' and the AJAX generated 'span.review_button'
I see however that .live is depracated so I have tried to follow the jquery documentations instructions by switching to '.on' but when I switch to the code below, I have the same problem I had before switching to '.live' in which the click function works with the original instance of 'span.review_button' but not on the AJAX generated instance:
var $reviewButton = $('span.review_button');
$reviewButton
.on('click',
function(){
$('#add_reviews').show();
}
)
Suggestions?
The correct syntax for event delegation is:
$("body").on("click", "span.review_button", function() {
$("#add_reviews").show();
});
Here instead of body you may use any static parent element of "span.review_button".
Attention! As discussed in the comments, you should use string value as a second argument of on() method in delegated events approach, but not a jQuery object.
This is because you need to use the delegation version of on().
$("#parentElement").on('click', '.child', function(){});
#parentElement must exist in the DOM at the time you bind the event.
The event will bubble up the DOM tree, and once it reaches #parentElement, it is checked for it's origin, and if it matches .child, executes the function.
So, with this in mind, it's best to bind the event to the closest parent element existing in the DOM at time of binding - for best performance.
Set your first selector (in this case, div.content) as the parent container that contains the clicked buttons as well as any DOM that will come in using AJAX. If you have to change the entire page for some reason, it can even be change to "body", but you want to try and make the selector as efficient as possible, so narrow it down to the closest parent DOM element that won't change.
Secondly, you want to apply the click action to span.review_button, so that is reflected in the code below.
// $('div.content') is the content area to watch for changes
// 'click' is the action applied to any found elements
// 'span.review_button' the element to apply the selected action 'click' to. jQuery is expecting this to be a string.
$('div.content').on('click', 'span.review_button', function(){
$('#add_reviews').show();
});

jquery using $.on for added dom elements?

I am a bit confused, I have a bunch of elements that get added via jquery using a ajax call and I want to attach a click handler to them (there could be a lot).
But I have no idea how to even begin this, I looked at .on and it is really confusing. I want to attach a click event handler for a certain class so that when I click on it, I get the this.id and then do stuff with it.
What you're trying to do is called event delegation.
You want to set the event listener on a higher element in the DOM that'll never change, but only fire off the event handler if the child element that has been clicked matches a specific selector.
Here's how it's done with jQuery's .on():
$(document).on('click', '.your-selector', function(){
alert(this.id);
});
P.S. You could probably apply the event listener to an element lower down in the DOM tree...
This will get you the id of a clicked element with the class "test"...
$(".test").on("click", function() {
var id = $(this).attr("id")
});
You'll need to run that after the ajax call returns. It will only bind the click event to elements that exist when it runs, so it's no good at document.ready.

Categories