Javascript – Where to declare event handlers: Markup or Script

htmljavascript

If the event handler is declared in the markup, it'd be (kind-of) adding presentation logic to HTML.

<button id="btn" onClick="foo()">Click Me!</button>

If the event handler is declared in the script, it'd be (kind-of) hard to look at the code and figure out what button does.

document.getElementById('btn').onclick=foo;

Is there any best-practice for this scenario?


The reason for this question is that I was attaching event handlers on page load via the script. However, I ran into a problem where the DOM element may not be present when the page loads (I'm using KnockoutJS's if binding), and the event is not bound to the handler. To attach the handler as and when the if condition is satisfied would complicate the code further. So now I'm thinking of moving my handlers to the click binding. What might be the potential pitfalls and what best-practices should I follow to avoid them?

Best Answer

There's no right or wrong to this, both things are fine. There are a few things to consider which makes one or the other better in one or the other situation.

And actually, there are at least three ways of assigning event handlers:

  1. Using an event handler attribute in HTML, like this:

    <button id="buttonSave" onClick="save()">Save</button>
    
  2. Using a script to set the property, like this:

    <button id="buttonSave">Save</button>
    
    document.getElementById("buttonSave").onclick = save;
    
  3. Modifying the event handlers using addEventListener()

    document.getElementById("buttonSave").addEventListener("onclick", save);
    

Here's the decision path which I use.

  • A lot (>~20) of elements share the same handler -> 2. or 3., because doing it in JavaScript will save bytes, and in page loading traffic is slower than JavaScript.
  • Multiple handlers for a single element needed
    • Handler needs to be added or removed dynamically -> 3. addEventListener()
    • Handler doesn't change -> 1. HTML
  • Single handler sufficient
    • Handler needs to be added or removed dynamically -> 2. JavaScript
    • Handler doesn't change -> 1. HTML

The problem which you describe can actually be solved by moving the JavaScript code which installs the handlers into a JavaScript function which itself is installed as onload handler, so the JavaScript would only run when the DOM is ready for it. There might still be a "race condition" (not in the sense of concurrency but in the sense of sequence) when other JavaScripts run and modify the DOM. To prevent that, I usually do not install multiple onload handlers, but just a single handler which calls all the functions that need to be executed during onload in the required sequence.