When searching how to attach an event listener to a dynamically loaded div the accepted answer seems to be event delegation.
I did not find another suggested way when HTML is added with insertAdjacentHTML
.
I am adding divs to the DOM like follows:
document.getElementById('container').insertAdjacentHTML(
'beforeend', `<div class="card">
<div class="card-header">Card header content</div>
<div class="card-body">
<h2>Card body</h2>
<select>
<option value="value1">Value 1</option>
</select><br>
Some more text
</div>
</div>`
);
// Event delegation
document.addEventListener('click', function (e) {
console.log(e.target.className);
});
.card{
background: rosybrown;
text-align: center;
width: 50%;
}
h2{
margin-top: 0;
}
<div id="container">
</div>
I hope you can see the console output when clicking anywhere on the card.
Event delegation how I know it works by attaching the click event on a "big" parent element like document
and then comparing attributes like the className
of the clicked element and making something if it matches the intended class name.
The issue is that this event.target
targets the precise clicked elements inside the card, but I can't do much with that info, can I? I need the whole card (a parent of those elements) to be able to make the right action for the user (in this case, redirect to a more detailed view of what the card previewed).
A dirty possible solution, I thought, would be wrapping the div with an <a>
-tag, but then the select
and other buttons / interactive elements of the card wouldn't work, and the user would just be directed to the link.
Edit - Solution
Thank you so much @epascarello and @KooiInc and the others for your answers!
Not sure if this should be an own answer or if it's better in an edit of the question but as I need to target the card only when the user doesn't click in the interactive element, in my case the select
. I made a little addition and this works beautifully now:
document.getElementById('container').insertAdjacentHTML(
'beforeend', `<div class="card" data-id="1">
<div class="card-header">Card header content</div>
<div class="card-body">
<h2>Card body</h2>
<select>
<option value="value1">Value 1</option>
</select><br>
Some more text
</div>
</div>`
);
// Event delegation
document.addEventListener('click', function (e) {
console.clear();
const card = e.target.closest('.card');
if (card && e.target.tagName !== 'SELECT'){
console.log('Redirect to ' + card.dataset.id);
}
});
.card{
background: rosybrown;
text-align: center;
width: 50%;
}
h2{
margin-top: 0;
}
<div id="container">
</div>
If someone has a more efficient suggestion than using e.target.tagName
to manually check each HTML element that we don't want the event listener to be triggered on, I would love to see it.