Home > OS >  How to set addEventListener on innerHTML added from button clicked
How to set addEventListener on innerHTML added from button clicked

Time:04-10

I want to get each element reference and want to add event listener on each item, but when I add HTML using innerHTML, I am not able to use it outside anywhere. How can I use it everywhere and instead of just event listener, how can I reference it also? I know there are other ways to add HTML using javascript, but for now, I want to do it with innerHTML.

const mybtn = document.getElementById('maybe');
const parent = document.getElementById('parent');

mybtn.addEventListener('click',function(e){
        parent.innerHTML = `<div>
          <h1 id="one" >Hello 1</h1>
          <h1 >Hello 2</h1>
          <h1 >Hello 3</h1>
          <h1 >Hello 4</h1>
        </div>`
});

const one = document.getElementById('one');
one.addEventListener('click',function(e){
        alert('one');
});
<div id="parent"></div>
<button id="maybe">Set</button>

CodePudding user response:

You are attempting to add the eventListener to elements which are yet to be created. As currently your elements are only added to parent on buttonClick of myBtn.

If you add the event listener when the elements are created then your code will work.

const mybtn = document.getElementById("maybe");
const parent = document.getElementById("parent");

mybtn.addEventListener("click", function (e) {
  parent.innerHTML = `<div>
              <h1 >Hello 1</h1>
              <h1 >Hello 2</h1>
              <h1 >Hello 3</h1>
              <h1 >Hello 4</h1>
            </div>`;
  const one = document.querySelector(".one");
  one.addEventListener("click", function (e) {
    alert("one");
  });
});
<div id="parent"></div>
<button id="maybe">Set</button>

CodePudding user response:

The keyword that you would want to google for is event delegation.

To understand the issue you are facing, you will need to understand the executing order of your code.

mybtn.addEventListener('click',function(e){
        parent.innerHTML = `<div>
          <h1 >Hello 1</h1>
          <h1 >Hello 2</h1>
          <h1 >Hello 3</h1>
          <h1 >Hello 4</h1>
        </div>`
});

const one = document.querySelector('.one');
one.addEventListener('click',function(e){
        alert('one');
});

In your code, you are adding a click event on mybtn that will set the innerHTML of another HTML element. In addition to that, you also select other elements by className .one and then add click event to it. The question is, which one happens first? Your innerHTML set or querySelector?

And the answer is the querySelector.

If you look into the dev tools and put up some checkpoint, you will see that innerHTML line only run when you actually clicking the button, and might be way after the querySelector. So by the time the querySelector runs, your HTML page doesn't even know about any elements that have the class .one.

So, how would you deal with these dynamically generated elements?

Set the handler after you create the element (IMO, bad way)

You would simply move your click handler to after the innerHTML set.

mybtn.addEventListener('click',function(e){
        parent.innerHTML = `<div>
          <h1 >Hello 1</h1>
          <h1 >Hello 2</h1>
          <h1 >Hello 3</h1>
          <h1 >Hello 4</h1>
        </div>`
    const one = document.querySelector('.one');
    one.addEventListener('click',function(e){
        alert('one');
    });
});

This will ensure that your querySelector can target the elements you are looking for

Set up event delegation (IMO, better way)

So the problem with the above one is that, you will have to rerun the click handler whenever you click the button. With a small app, this is acceptable. However, this is still not a good practice.

Since JS event will bubble up to the parent element, you don't need to target the exact element to add the events. Instead, you can target the parent.

Your code will look something like this

mybtn.addEventListener('click',function(e){
        parent.innerHTML = `<div>
          <h1 >Hello 1</h1>
          <h1 >Hello 2</h1>
          <h1 >Hello 3</h1>
          <h1 >Hello 4</h1>
        </div>`
});

parent.addEventListener('click', function(e) {
    if (e.target.classList.contains('one')) {
        alert('one');
    }
}

What the code is doing is:

  • add the click event to the parent element - as we know, the event will be bubbling up from the actual .one element to parent element, this will be fired when .one is click
  • we use e (the click event) to retrieve e.target, which is the element that actual trigger the event
  • we then check whether that is the element that we are looking for.

You can read more about it here

  • Related