Reputation: 121
I am new to Javascript and I try to add an event listener for each button on every card, but the code make the last card (button) only have the event 'click' so is there's any way to make it happen with innerHTML card this is the code:
let tracksRender = (track) => {
track.forEach(element => {
//this the card that will add the button for
let card = `<div class="card">
<div class="image">
<img class="image_img" src="${element.artwork_url || 'http://lorempixel.com/100/100/abstract/'}">
</div >
<div class="content">
<div class="header">
<a href="${element.permalink_url}" target="_blank">${element.title}</a>
</div>
</div>
</div >`;
//here i add the card to DOM
let searchResults = document.querySelector('.js-search-results');
searchResults.innerHTML += card;
// store the content of the button
let inBtn = `<i class="add icon"></i>
<span>Add to playlist</span>`;
// created button container
let btn = document.createElement("div");
btn.classList.add("ui", "bottom", "attached", "button", "js-button");
// added the content of the button
btn.innerHTML += inBtn;
// here i add the the event Listener to the button
btn.addEventListener('click', () => {
console.log("click");
});
//here i add the button to the last card have been created
searchResults.querySelector(".card:last-child").append(btn);
});
}
and the structure:
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>SoundCloud Player</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css'>
<link rel='stylesheet' href='styles/main.css'>
<style></style>
</head>
<body id="soundcloud-player">
<div class="ui container col">
<div class="col">
<div class="main">
<div class="js-search-results search-results ui cards">
</div>
</div>
</div>
</div>
<script src="https://connect.soundcloud.com/sdk/sdk-3.3.2.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js'></script>
<script src="javascript/main.js"></script>
</body>
</html>
Fiddle: https://jsfiddle.net/y73bstju/7/
but it will add the event to the last card only
Upvotes: 4
Views: 11501
Reputation: 89
I think: you're doing:
let searchResults = document.querySelector('.js-search-results');
searchResults.innerHTML += card;
Serializing again and again in your div "searchResults"
innerHTML ~erases~ listeners, and probably it is ~erasing~ your previous listeners. Besides, i don't remember where i read that innerHTML script code cannot run (for security purposes)
From https://medium.com/@kevinchi118/innerhtml-vs-createelement-appendchild-3da39275a694
using innerHTML reparses and recreates all DOM nodes inside the div element and is less efficient than simply appending a new element to the div. In the above cases, createElement is the more performant choice.
Be careful:
Using "append" again and again uses badly browser resources, redrawing many times.
But, you can append in a documentFragment and append it to the div -js-search-results
DocumentFragment: https://developer.mozilla.org/es/docs/Web/API/DocumentFragment
Upvotes: 0
Reputation: 76464
I agree with you that theoretically the object should have the event, but the behavior we experience is that whenever another write happens at the relevant section of the DOM, the event handler is lost, which is the reason the last element has the click event. So, let's write first into the DOM and only when we are done with that should we add the event listeners, like:
let SoundCloudAPI = {};
SoundCloudAPI.init = () => {
SC.initialize({ client_id: 'cd9be64eeb32d1741c17cb39e41d254d' });
};
SoundCloudAPI.init();
SoundCloudAPI.getTrack = (inputVlue) => {
SC.get('/tracks', {
q: inputVlue
}).then((tracks) => {
console.log(tracks);
SoundCloudAPI.renderTracks(tracks);
});
}
SoundCloudAPI.getTrack("alan walker");
SoundCloudAPI.renderTracks = (track) => {
track.forEach(element => {
//this the card that will add the button for
let card = `<div class="card">
<div class="image">
<img class="image_img" src="${element.artwork_url || 'http://lorempixel.com/100/100/abstract/'}">
</div >
<div class="content">
<div class="header">
<a href="${element.permalink_url}" target="_blank">${element.title}</a>
</div>
</div>
</div >`;
//here i add the card to DOM
let searchResults = document.querySelector('.js-search-results');
searchResults.innerHTML += card;
// store the content of the button
let inBtn = `<i class="add icon"></i>
<span>Add to playlist</span>`;
// created button container
let btn = document.createElement("div");
btn.classList.add("ui", "bottom", "attached", "button", "js-button", "fresh");
// added the content of the button
btn.innerHTML += inBtn;
//here i add the button to the last card have been created
searchResults.querySelector(".card:last-child").append(btn);
});
for (let btn of document.querySelectorAll('.ui.attached.button.js-button.fresh')) {
// here i add the the event Listener to the button
btn.addEventListener('click', () => {
console.log("click");
});
}
}
Fiddle: https://jsfiddle.net/r84um9pt/
Upvotes: 1
Reputation: 61
Welcome in the community. In you're code you're adding many classes to button. You can add event Listener to any one of the unique class name which is specifically applied on button element only.
You can replace:
btn.addEventListener('click', () => {
console.log('click');
});
with:
document.querySelectorAll('.js-button').forEach(el=>{
el.addEventListener('click', (event) => {
event.preventDefault();
console.log("click");
});
});
Also, it would be good if you add eventListener out of the loop in which you are appending elements.
Upvotes: -1
Reputation: 1588
It might help to create elements instead of appending innerHTML:
let tracksRender = (track) => {
// Select the results here, so you wont have to repeat it
const searchResults = document.querySelector('.js-search-results');
track.forEach(element => {
// Create the card, give it its class and innerHTML
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = `<div class="image">
<img class="image_img" src="${element.artwork_url || 'http://lorempixel.com/100/100/abstract/'}">
</div >
<div class="content">
<div class="header">
<a href="${element.permalink_url}" target="_blank">${element.title}</a>
</div>
</div>`;
// Created the button, give its classes and innerHTML
const btn = document.createElement('div');
btn.className = 'ui bottom attached button js-button';
btn.innerHTML = '<i class="add icon"></i><span>Add to playlist</span>';
// Add the event listener
btn.addEventListener('click', () => {
console.log('click');
});
// Append the button to the created card
card.appendChild(btn);
// Add the card to the results
searchResults.appendChild(card);
});
}
Upvotes: 3