Event delegation issue with JQUERY - javascript

I am new in Jquery and I am trying to understand how event delegation work.
I am trying this:
$("#32298").on( 'click',function() { // event delegation
alert("df");
var imgs = document.getElementById("32298")[0].src;
alert(imgs);
});
When I click on the image with this Id I get the first alert but it doesn't work the second alert.
What am I doing wrong here?
Thank you.

If you want to perform event delegation then the second argument of the event handler function needs to be a selector to match the element that matches the one you want to be clicked.
$(document.body).on('click', "#32290", function(event) {
The problem with your code has nothing to do with event delegation. getElementById returns a single element (an id must be unique in the document), not a HTML Collection. (Compare with getElementsByTagName and getElementsByClassName which use the plural Elements.) It won't have a 0 property.
var imgs = document.getElementById("32298").src;

Since you're using jQuery, you can simply use the $(this) constructor, rather than document.getElementById():
$("#32298").on( 'click',function() {
alert("df");
var imgs = $(this).attr('src');
alert(imgs);
});
For what it's worth, this isn't event delegation by the way, it's just an event bound to an element.
If you must use vanilla JS to fetch the src attribute, you don't need to pass an index to the returned value of getElementById(), since this function returns a DOMObject, not an array. Update as follows:
$("#32298").on( 'click',function() {
alert("df");
var imgs = document.getElementById("32298").src;
alert(imgs);
});
It's also worth noting that IDs should be unique, so #32298 should reference a single element in the DOM. I don't know whether it's a typo, but it appears that you may have multiple elements with the same ID (since you use the variable name imgs - i.e. plural).

you can try this
$("#32298").click( function() {
alert("df");
var imgs = $(this).attr('src');
alert(imgs);
});

Related

JQuery: Elements made with clone() not taking event properties of original

HTML:
<h2>CHECK AS MANY AS YOU CAN</h2>
<form id="boxone">
</form>
JS:
$boxone = $("#boxone");
$boxone.html('<input type="checkbox" class="fourthboxes">');
$fourthboxes = $(".fourthboxes");
for(var i=0; i <341; i++) {
$fourthboxes.clone(true, true).appendTo($boxone);
}
$fourthboxes.change(function() {
alert('yo');
});
The rest of the checkboxes don't alert when I click on them, only the original one does
I even tried $fourthboxes.on('click'... instead and still nothing.
I took a look at this question and tried the solution but it didn't work.
jQuery clone() not cloning event bindings, even with on()
Use .on():
$(document).on('change', '.fourthboxes', function() {
alert('yo');
});
This makes your event handler work for current elements, but also future added elements, that match the .fourthboxes selector. This uses the principle of delegated events.
From the 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.
Delegated events have the advantage that they can process events from descendant elements that are added to the document at a later time.
The issue is because although you use clone(true, true) you're cloning the elements before you add the change event handler to them. You just need to swap the logic around:
$boxone = $("#boxone");
$boxone.html('<input type="checkbox" class="fourthboxes">');
$fourthboxes = $(".fourthboxes");
$fourthboxes.change(function() {
alert('yo');
});
for(var i=0; i <341; i++) {
$fourthboxes.clone(true, true).appendTo($boxone);
}
However, it would be much better to use a single delegated event handler, like this:
var $boxone = $("#boxone").on('change', '.fourthboxes', function() {
alert('yo');
});
var $fourthboxes = $('<input type="checkbox" class="fourthboxes">').appendTo('#boxone');
for (var i = 0; i < 341; i++) {
$fourthboxes.clone().appendTo($boxone);
}
Working example
Since you assign $(".fourthboxes") to the variable $fourthboxes before you add the 340 other checkboxes, the variable still holds only that one checkbox when you add the change-function.
Put the change-function in front of the for-loop and everything works as expected.
It only selects the original because you never select the new elements. The selector only has the original, it is not a live collection. So you need to reselect them.
$(".fourthboxes").on("change", ...)
You can use event delegation so you are not selecting all the checkboxes. Listen for the change event on the form.
$("#boxone").on("change", ".fourthboxes", function(){});
When you clone an object, only the objects are cloned. And not their events. Because the events are bound to the original object (based on the jQuery selector used) before creating the clone.
As #trincot mentioned in his answer, you need to have an event at the document level.
For eg. Let's say my DOM contains three input checkboxes
<input type="checkbox" class="fourthboxes">
<input type="checkbox" class="fourthboxes">
<input type="checkbox" class="fourthboxes">
Now when you bind events using the jQuery selector like this,
$(".fourthboxes").change(function() {
alert('yo');
});
Things to note, is that this jQuery selector returns an array of DOM elements which are present on the page, at that instant of time. And then the onchange event is registered on each of them. It is equivalent to binding the event to each of the existing DOM.(In this case, three checkboxes)

jQuery selector precedence when using pattern matching

I am creating a form that implements a bunch of similar elements. They are custom select boxes, created out of <ul>s.
Some of these elements are slightly different in the way I want the mousedown event to be handled though.
The way I have it set up currently is that, by appending _custom_select to the end of an elements class name, it will be treated as one of these special elements as far as CSS is concerned.
However, when the string selections is found inside a class name (that will coincidentally also end with _custom_select in order to apply the proper styling) I want to use a different mousedown event handler.
This is the relevant section of my event listener set up:
$('[class$="_custom_select"] li').mousedown(function(event){
var opt= event.target;
if(opt.className!='li_disabled' && event.which==1)
{
if(opt.className=='li_unselected'){
opt.className= 'li_selected';
}
else{
opt.className= 'li_unselected';
}
update_selections(opt.parentElement);
}
});
$('[class*="selections"]').mousedown(function(event){
var opt=event.target;
if(event.which==1){
if(opt.className=='li_unselected'){
opt.className= 'li_selected_2';
}
else{
opt.className= 'li_unselected';
}
}
});
This code works, but notice how, in the second binding, I had to bind the event listener to the ul that holds the li that is actually being clicked.(The ul is the element whose class name matches the pattern) In the first one however, I can bind the event listener directly to the li elements contained within the ul.
If I change the second jQuery selector to $('[class*="selections"] li') the event listener is never bound to the corresponding lis.
What is causing this behavior?
I am aware that I can just check event.target.tagName to ensure the event is bubbling up from an <li>, but that is not what the question is about.
I originally thought it had something to do with precedence and that the listeners weren't being bound because the lis that would have matched the second selector already matched against the first selector.
However, after implementing logging and looking at the DOM I have determined that when I change the second selector to: $('[class*="selections"] li') neither event listener is bound to the lis that match the second selector.
Here is a link to a JS fiddle of the 'working version'. If you add ' li' to the second selector and then try to click the <li>s in the box to the right, you will see that they no longer become green.
jsFiddle
https://jsfiddle.net/6sg6z33u/4/
Okay, thanks for posting the jsFiddle. This is an easy fix!
The elements in your second li are being added dynamically. When you bind to elements using the shortcut methods like .click() it only binds to the elements on the page when it initially bound
The fix: use the .on() method, which is the preferred method per jQuery foundation. This method allows for live binding meaning it will pick up on dynamic elements.
$('[class*="selections"]').on( 'mousedown', 'li', function(event) {
var opt = event.target;
if (event.which == 1) {
if (opt.className == 'li_unselected') {
opt.className = 'li_selected_2';
} else {
opt.className = 'li_unselected';
}
}
});

Get any element tagName on click

I need to take $('this') information from any element i click on the document.
I tried the following code:
$('body').click(function(){
var element = this.tagName; // or var element = $(this).prop('tagName');
alert(element);
});
The problem is that wherever i click i get only BODY element. If i click on a button or a div i want to get that element. How can i create something general to take every element i click ?
Because you are attaching your event handler to the body element, this will always be the body. Instead, interrogate the event.target property:
$('body').click(function(e){
var element = e.target.tagName;
alert(element);
});
Example fiddle
nodeName
$('body').click(function(e){
alert(e.target.nodeName);
});
http://quirksmode.org/dom/core/#t23
My advice is not to use tagName at all. nodeName contains all
functionalities of tagName, plus a few more. Therefore nodeName is
always the better choice.
it also looks like the performance is slightly better on some versions of chrome and firefox.
http://jsperf.com/tagname-vs-nodename/2
this always refers to the element where the event handler is assigned, not where the event originated (well, you can change it, but it's pretty unusual to do so). For that, you need Event.target:
$('body').click(function(event){
var element = event.target.tagName; // or var element = $(this).prop('tagName');
alert(element);
});

Dynamically adding onchange function to drop down with jQuery

I have a couple of drop down boxes with ids country1, country2, ... When the country is changed in a drop down the value of the country shoudl be displayed in an alert box.
if I add the onchange handler for one box like this it works fine:
$('#country1') .live('change', function(e){
var selectedCountry = e.target.options[e.target.selectedIndex].value;
alert(selectedCountry);
});
But I need to do this dynamically for all drop down boxes so I tried:
$(document).ready(function() {
$('[id^=country]') .each(function(key,element){
$(this).live('change', function(e){
var selectedCountry = e.target.options[e.target.selectedIndex].value;
alert(selectedCountry);
});
});
});
This doesn't work. No syntax error but just nothing happens when the seleted country is changed. I am sure that the each loop is performed a couple of times and the array contains the select boxes.
Any idea on that?
Thanks,
Paul
The reason .live() existed was to account for elements not present when you call the selector.
$('[id^=country]') .each(function(key,element){ iterates over elements that have an id that starts with country, but only those that exist when you run the selector. It won't work for elements that you create after you call .each(), so using .live() wouldn't do you much good.
Use the new style event delegation syntax with that selector and it should work:
$(document).on('change', '[id^=country]', function(e) {
// ...
});
Replace document with the closest parent that doesn't get dynamically generated.
Also, consider adding a class to those elements along with the id attribute.
Instead of incremental ids I'd use a class. Then the live method is deprecated but you may use on with delegation on the closest static parent or on document otherwise.
$('#closestStaticParent').on('change', '.country', function() {
// this applies to all current and future .country elements
});
You don't need an each loop this way; plus events are attached to all the elements in the jQuery collection, in this case all .country elements.

Get the id of a the item that is clicked

I have this html code:
<b class = "edit" id = "foo1">FOO</b>
<b class = "edit" id = "foo2">FOO2</b>
<b class = "edit" id = "foo3">FOO3</b>
And I have this code in jQuery:
$('b.edit').click(function(){
//GET THE ID OF THE b.edit: e.g foo1, foo2, foo3
$('.editP').focus();
});
How can I get the id value of the b.edit, as there are multiple instances of b.edit, and I want to get the specific id of the one clicked? How can I do this?
Thanks, Sorry, I am pretty new to javascript.
I'm assuming from your sample code that you're using jQuery? If so you can get the id as follows:
$('b.edit').click(function(){
this.id;
});
EDIT:
The direct reference to the attribute is indeed more efficient if all that is required is simply the id.
Also can be obtained from the jQuery object:
$('b.edit').click(function(){
$(this).attr('id');
});
Sample fiddle: http://jsfiddle.net/5bQQT/
Try with this:
$('b.edit').click(function(e){ //When you use an event is better
//send the event variable to the
//binding function.
var id = e.target.id; //get the id of the clicked element.
/*do your stuff*/
$('.editP').focus();
});
try this. You can use keyword "this" to retrieve the attr ID...
$('b.edit').click(function(){
alert($(this).attr("id"));
});
$('.edit').click(function(){
var theId = $(this).attr('id');
}
This will get you the ID of anything clicked with a class of .edit
$('.edit').live('click', function() {
alert( this.id );
});
Sample
http://jsfiddle.net/ck2Xk/
When passing a click handler in JQuery, you actually have a reference to something called an event object. This event object has a property called target, which is a reference to the element that was clicked.
$('b.edit').click(function(eventObject){
eventObject.target // this is the element that was clicked.
});
Since you have a reference to the target element, you can do whatever you like. In this case, you could just access eventObject.target.id.
Since nobody has shown the simplest method yet that doesn't even need jQuery to get the id:
$('.edit').click(function() {
alert(this.id);
});
I never understand why people use jQuery for getting simple attributes which involves two jQuery function calls (and a bunch of overhead to create a jQuery object) instead of one direct attribute reference.
something like this:
var id = $(this).attr('id');
More clearly:
$('b.edit').live('click', function(){
var id = $(this).attr('id');
// in this scope this.id works too
// var id = this.id;
});
This is called event delegation in Javascript. More info can be found in Zakas blog http://www.nczonline.net/blog/2009/06/30/event-delegation-in-javascript/
The idea in few words is you attache the event to a parent node and then waiting for some event on the child node. In the example below I attach the onclick event to the document itself. Then inside the event handler you will write a switch statement to check the clicked element id, then do what you want to do for that element
document.onclick = function(event){
//IE doesn't pass in the event object
event = event || window.event;
//IE uses srcElement as the target
var target = event.target || event.srcElement;
switch(target.id){
case "foo1":
foo1();
break;
case "foo2":
foo2();
break;
case "foo3":
foo3();
break;
//others?
}
};
//some dummy handlers
var foo1 = function(){
alert("You clicked foo1");
};
var foo2 = function(){
alert("You clicked foo2");
};
var foo3 = function(){
alert("You clicked foo3");
};
For how to implement event delegation in jQuery you can check http://api.jquery.com/on/#direct-and-delegated-events
Even though this is not a real answer to your question. I will try to explain why what your asking is not the way to go. Since you are new especially, since learing bad practices could be hard to unlearn. Allways try to search for an ID before finding an element by its Class. Also try to avoid giving every element the same class (and in this case ID to), just give it an encapsulating parent.
Furthermore, the id of an element is really specific and should preferably used to find / select / bind events to. An id should usually be unique for this to work, so in your case a couple of things could be optimized, lets say like:
<div id="foo">
<b id="1">Foo</b>
<b id="2">Other foo</b>
<b id="3">Some foo</b>
</div>
Now if you want to know which was clicked there are multiple ways to accomplish it, but a nice one is simply binding a parent its children (i.e <div id="foo"> .. </div>). This way you can alter the structure of your pretty fast, without changing all the classes and id's.
With jQuery you can get the attribute id using the .attr() function. However I told you the id was pretty specific and thus has its own rights in javascript world. An id can also be directly targeted ('DOMelement.id', but this would be too much to explain for now)
In two ways the <b> can be targetted:
Example a)
var b_elements = $("#foo").children();
Example b)
var b_elements = $("#foo").find('b');
We can add jQuery (or javascript events) to these found elements. The nice thing about jQuery is that it simplifies alot of work. So in your case if you would like to know an id of a certain clicked <b> field you could use a very verbose way:
b_elements.click(function()
{
var clicked_element = $(this);
alert(clicked_element.attr('id'));
});
Verbose because you can do it much much shorter, but who cares about a few bytes when your learning and this makes remembering functions and events alot easier. Say you wanted to get the class edit by finding the where you knew the id that was clicked:
b_elements.click(function()
{
var clicked_element = $(this);
alert(clicked_element.attr('class'));
});
And to conclude, the id of an element is ment to be unique because it makes searching through big documents alot faster. Also don't forget to look and learn plain javascript, as it helps coding in jQuery alot too, but not the other way around. Your given question would require a for loop in plain javascript since it cannot do a lookup by class nor id nor have they have a common parent.
Good luck with learning :)

Categories