ilyes chicha
ilyes chicha

Reputation: 121

addEventListener to innerHTML

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

Answers (4)

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

Lajos Arpad
Lajos Arpad

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

LilFlower
LilFlower

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

Emre Koc
Emre Koc

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

Related Questions