Search code examples
javascripthtmlonclickonclicklistener

Reattaching the onclicklistener to the button


This question is related to another question on SO. I am not rephrasing it so please read that first.

In solving the above problem I encountered a behaviour that is unknown to me.

This snippet works:

var bt1;
document.addEventListener('DOMContentLoaded', load);

function load() {
  document.body.innerHTML += '<div>welcome</div>';
  bt1 = document.getElementById('bt1');
  bt1.onclick = clicked;
}

function clicked(a) {
  document.body.innerHTML += '<div>welcome</div>';
  // getting the button from document.getElement method
  bt1 = document.getElementById('bt1');
  bt1.onclick = clicked;
}
<body>
  <button id="bt1">Click Me</button>
</body>

While this doesn't:

var bt1;
document.addEventListener('DOMContentLoaded', load);

function load() {
  document.body.innerHTML += '<div>welcome</div>';

  bt1 = document.getElementById('bt1');
  bt1.onclick = clicked;
}

function clicked(a) {
  document.body.innerHTML += '<div>welcome</div>';
  // getting the button from event.target
  var b = a.target;
  b.onclick = clicked;
}
<body>
  <button id="bt1">Click Me</button>
</body>

Any insights would help to clear the concepts.


Solution

  • The bt1 button gets re-created in the clicked function - and a.target points to the old button, not the new one.

    The line document.body.innerHTML += '<div>welcome</div>'; doesn't just append a div to the body, but sets the innerHTML of the complete body which causes all elements inside body to be initialized again. In your first code sample you retrieve the newly created button by the id (because getElementById is called after the innerHTML assignment), but in the second sample the old button is referenced which isn't the one displayed on the browser anymore.

    This method of appending stuff to the body is a code smell, because the browser has to recreate anything inside the body. A better solution would be to use the appendChild API. By doing so, the button doesn't get re-created and you don't have to re-attach the event listener:

    var bt1;
    document.addEventListener('DOMContentLoaded', load);
    
    function load() {
      document.body.innerHTML += '<div>welcome</div>';
      bt1 = document.getElementById('bt1');
      bt1.onclick = clicked;
    }
    
    function clicked(a) {
      var newDiv = document.createElement('div');
      newDiv.textContent = 'welcome';
      document.body.appendChild(newDiv);
    }