I'm trying to make certain "cards" that display next to each other, and when the card is clicked, I want to get the id of the card. However, my function gets the id of the child elements when I click it, which is undefined, rather than of the parent card, unless I click on the border of the card div. How do I make sure it gets the id of the card specifically?
Here is the HTML:
<div id="parentCard" onclick="getID(event)">
<div id="child1">I'm a child</div>
<div id="text">
<p>Text 1</p>
<p>Text 2</p>
<p>Text 3</p>
</div>
</div>
Javascript:
function getID(event){
console.log(event.target.id);
}
To get the clicked element, use target property on the event object. Use the id property on the event. target object to get an ID of the clicked element.
Answer: Use the jQuery attr() Method You can simply use the jQuery attr() method to get or set the ID attribute value of an element. The following example will display the ID of the DIV element in an alert box on button click.
Grab the target element of the event (the element that was clicked).
Here I've used destructuring to get the id
, tagName
, and parentNode
properties from the target.
If the clicked element is a card div element log the id, otherwise log the id of the parent element.
Note: this assumes that the cards won't have nested HTML any deeper than they are in your example or parentNode
won't apply to the right parent element.
const parent = document.getElementById('parentCard');
parent.addEventListener('click', handleClick, false);
function handleClick(e) {
const { id, tagName, parentNode } = e.target;
console.log(tagName === 'DIV' ? id : parentNode.id);
}
<div id="parentCard">
<div id="child1">I'm a child</div>
<div id="text">
<p>Text 1</p>
<p>Text 2</p>
<p>Text 3</p>
</div>
</div>
Try with Event.currentTarget which
Identifies the current target for the event, as the event traverses the DOM. It always refers to the element to which the event handler has been attached, as opposed to
event.target
which identifies the element on which the event occurred.
function getID(event){
console.log(event.currentTarget.id);
}
<div id="parentCard" onclick="getID(event)">
<div id="child1">I'm a child</div>
<div id="text">
<p>Text 1</p>
<p>Text 2</p>
<p>Text 3</p>
</div>
</div>
Every time the user clicks somewhere on a page, the user agent will determine which "non-compound" element was clicked on and fire an event of type "click" event with that element as event target.
A "non-compound" element here means one that does not have descendant elements (it may have descendant nodes, like text, for instance).
Since your cards will typically be compound elements -- containing other elements (which may contain other elements in turn) -- there is no guarantees about which of these elements will be the click event target
.
The user agent is a machine and cannot think for you and know that that the user "clicks on a card". If the card contains a button or a link or an image, all the user agent will do is tell you which of these the user has clicked on, through the means of the target
property on the event -- you need to determine the fact that from your perspective, a card was clicked on.
There are at least two ways to determine said fact:
Find out which card element is or contains the clicked element:
function get_card(element) {
for(; element; element = element.parentElement) {
if(is_card(element)) return element;
}
}
How do you know, traversing ancestor list, that an element is a card? As far as the document object model is concerned, it's just a div
element, with an onclick
attribute and an ID. You may have div
elements with defined id
and onclick
attributes in your document that are not cards.
Adding a class to an element is a natural way to, well, classify it:
<div class="card">
<!-- descendant elements -->
</div>
With the above we decide and now assume that every card element will include "card" in the list of its class names. Now you can write your is_card
function (used in get_card
), as follows:
function is_card(element) {
return element.classList.contains("card");
}
With a working get_card(element)
function in your program you can now reliably answer the question "which card, if any, contains element
?". In combination with a single event listener on, say, the window
object you can now know which card the user clicks on:
addEventListener("click", function(ev) {
var card = get_card(ev.target);
if(card) {
/// A card was clicked, do something about it.
}
}
As you can see, you don't even need to add listeners to every card, adding one to one container element of your choice (or window
as is the case above) is all you need with this solution.
Now, this is a case of a so-called linear-time algorithm -- the "deeper" your cards (descendant elements containing other descendant elements in turn), the longer average time it will take to locate the card for a descendant element. But it's nothing to worry about with "click" handling and performance of modern JavaScript runtimes, if I am allowed to be general here.
Use the [so-far experimental] CSS4 and the pointer-events: none
rule for card's children. Assuming every card includes the class "card", you can specify that no descendant element will have pointer events enabled -- these will never fire "click" events, the card element will instead:
.card > * {
pointer-events: none;
}
The support for pointer-events
is still experimental but I know that recent Google Chrome and Firefox do support it. You have to be careful here though -- like, are you prepared to disable interactivity for some of card's elements? What if you have form elements there, which are typically interactive? You need to assess these kind of requirements yourself.
There is a third solution, but I consider it inferior to at least the first solution. For the sake of completeness, assigning an event listener on each card may be wasteful, especially if there are many cards on each page. However, in any case, if you attach a "click" listener to a card like you yourself did, the element the listener for the event type "click" was added to, is available with the ev.currentTarget
property! Meaning that no matter which descendant element fires the "click" event, the event will reach the card element, through its capture and bubbling phases, and the currentTarget
property value will always refer to the element the listener is added to -- in this case the card element. Of course, this solution implies that the listener is attached to every card.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With