Click on table cell targets child element - javascript

I have a simple HTML table with ids in some cells:
<td id="x-11"><b>Is it a cell?</b> What a cell!</td>
Now I'd like to pass the ID to a JavaScript function triggered onclick:
const $tds = document.querySelectorAll('td[id]')
for (let i = 0; i < $tds.length; i++)
$tds[i].addEventListener('click', ev => console.log(ev.target.id))
It works as expected if I click an empty area or normal text within the cell. But if I hit the text inside the <b> element something strange happens: The browser says that the <b> element is the ev.target - although it has no listener whatsoever.
Can someone explain this behavior or offer a solution?
Update: As stated in the comments, Difference between e.target and e.currentTarget provides the answer (for ActionScript, but that doesn't make a difference here) but the question is different.

You need to use this or ev.currentTarget that always refers to the DOM element the listener was attached to, instead, the ev.target will refer to the element that fired the event, in this case, the DOM element that was clicked :
$tds[i].addEventListener('click', function(){
console.log(this.id)
});
//Or
$tds[i].addEventListener('click', ev => console.log(ev.currentTarget.id))

Related

Why compare element to this keyword?

I am looking at the following link which is a part of event delegation documentation.
In the link, we have the followig
let table = document.getElementById('bagua-table');
let selectedTd;
table.onclick = function(event) {
let target = event.target;
while (target != this) {
if (target.tagName == 'TD') {
highlight(target);
return;
}
target = target.parentNode;
}
}
I am just unable to understand why the following while (target != this). I understand that the this keyword here refers to the entire table since that is the object in play but why write a loop in this manner?
Maybe my understanding of this is not as deep. If someone could please clarify.
Thanks
What this does is, the target starts at the event's target - the innermost element the event was dispatched to. Then it searches the target, then its ancestor, then its ancestor, and so on. The first one that matches the condition target.tagname == 'TD', if any, will result in highlight being called and the loop ending. If no elements match, then the loop will continue until the target is this - the table - and will end at that point.
But this is pretty confusing code. Using .closest would be much easier, since it will find the first ancestor matching a selector.
table.onclick = (event) => {
const td = event.target.closest('td');
if (td) highlight(td);
};
If nested tables are a possibility, and you don't want the event to go outward past the table in question, include the table in the selector string:
table.onclick = (event) => {
const td = event.target.closest('#bagua-table td');
if (td) highlight(td);
};
In an event listener, this is equivalent to event.currentTarget, which is the element that the listener was added to (the table).
event.target is the nested element that was actually clicked on.
So the loop is walking up the DOM hierarchy (using target.parentNode) from the clicked element until it reaches the table that the listener was added to.
If it reaches a TD element along the way, it highlights that element and returns, which breaks out of the loop.
It's roughly equivalent to:
highlight(event.target.closest("TD"));

Get data of specific node element on click

I'm trying to get specific data depending on which node element the user is clicking. I have 4 elements that I have targeted using the querySelectorAll code. What I want to accomplish is that if I click the first element I will console.log that specific data, and if I select the third element I will get that data logged. I've tried a couple of things, but haven't got it to work yet.
function selectedSplit() {
var macroSplits = document.querySelectorAll(".card");
console.log(macroSplits[0].childNodes[3].childNodes[1].innerHTML);
}
It's unclear where you are using selectedSplit - Wether or not it is being used as the event listener return function. But using an onClick event listener, you're return function will be passed the information you need.
If you want to accomplish this in the markup, you could do -
<div class='card' onClick="selectedSplit"></div>
Then you can simply access it via event.target
function selectedSplit(event) {
var thisCard=event.target;
console.log(thisCard.innerHTML);
}
event.target has the clicked element:
d.onclick = e => console.log(e.target)
<div id=d>
<button><b>b</b></button>
<button><i>i</i></button>
<button><u>u</u></button>
<button><s>s</s></button>
</div>

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);
});

Javascript/jQuery - How do I obtain name of the class of clicked element?

I googled and googled and I concluded that it's very hard to get answer on my own.
I am trying to use jquery or JavaScript to get a property of clicked element. I can use "this.hash" for example - it returns hash value I presume.
Now I would like to get name of the class of clicked element.
Is it even possible? How? And where would I find this kind of information?
jQuery documentation? - All I can find is methods and plugins, no properties.. if its there - please provide me with link.
JavaScript documentation? - is there even one comprehensive one? again please a link.
DOM documentation? - the one on W3C or where (link appreciated).
And what is this.hash? - DOM JavaScript or jQuery?
In jQuery, if you attach a click event to all <div> tags (for example), you can get it's class like this:
Example: http://jsfiddle.net/wpNST/
$('div').click(function() {
var theClass = this.className; // "this" is the element clicked
alert( theClass );
});
This uses jQuery's .click(fn) method to assign the handler, but access the className property directly from the DOM element that was clicked, which is represented by this.
There are jQuery methods that do this as well, like .attr().
Example: http://jsfiddle.net/wpNST/1/
$('div').click(function() {
var theClass = $(this).attr('class');
alert( theClass );
});
Here I wrapped the DOM element with a jQuery object so that it can use the methods made available by jQuery. The .attr() method here gets the class that was set.
This example will work on every element in the page. I'd recommend using console.log(event) and poking around at what it dumps into your console with Firebug/Developer tools.
jQuery
​$(window).click(function(e) {
console.log(e); // then e.srcElement.className has the class
});​​​​
Javascript
window.onclick = function(e) {
console.log(e); // then e.srcElement.className has the class
}​
Try it out
http://jsfiddle.net/M2Wvp/
Edit
For clarification, you don't have to log console for the e.srcElement.className to have the class, hopefully that doesn't confuse anyone. It's meant to show that within the function, that will have the class name.
$(document).click(function(e){
var clickElement = e.target; // get the dom element clicked.
var elementClassName = e.target.className; // get the classname of the element clicked
});
this supports on clicking anywhere of the page. if the element you clicked doesn't have a class name, it will return null or empty string.
$('#ele').click(function() {
alert($(this).attr('class'));
});
And here are all of the attribute functions.
http://api.jquery.com/category/attributes/
You can use element.className.split(/\s+/); to get you an array of class names, remember elements can have more than one class.
Then you can iterate all of them and find the one you want.
window.onclick = function(e) {
var classList = e.srcElement.className.split(/\s+/);
for (i = 0; i < classList.length; i++) {
if (classList[i] === 'someClass') {
//do something
}
}
}
jQuery does not really help you here but if you must
$(document).click(function(){
var classList =$(this).attr('class').split(/\s+/);
$.each( classList, function(index, item){
if (item==='someClass') {
//do something
}
});
});
There's a way to do this without coding. Just open the console of your browser (f12?) and go to element you want. After that, hover or click the item you want to track.
Every change done on the DOM will be for a few seconds marked (or lightened) as another color on the console. (Watch the screen capture)
On the example, each time I hover a "colorItem", the 'div' parent and the "colorItem" class appears lightened. So in this case the clicked class will be 'swiper-model-watch' or 'swiper-container' (class of the lightened div)

Categories