JavaScript event registering without using jQuery

Solution 1:

The easiest way is:

// DOM Level 0 way
window.onload = function () {
  document.getElementById("someButton").onclick = function() {
    alert("Hello");
  };
};

This will work on all browsers but note that with this approach only one event handler can be attached to an event.

Also notice that the onload event is not completely equivalent to the jQuery's ready event, onload will be fired when all the resources of the page are completely loaded (images, sub-frames, etc...), while ready fires as soon the DOM has been parsed.

If you want to attach multiple event handlers, you can use the DOM Level 2 Standard element.addEventListerner method (or element.attachEvent for IE)

The simplest abstraction to get a cross-browser way to bind events is:

function addEvent(el, eventType, handler) {
  if (el.addEventListener) { // DOM Level 2 browsers
    el.addEventListener(eventType, handler, false);
  } else if (el.attachEvent) { // IE <= 8
    el.attachEvent('on' + eventType, handler);
  } else { // ancient browsers
    el['on' + eventType] = handler;
  }
}

Then you can:

var button = document.getElementById("someButton");
addEvent(button, 'click', function () {
  alert("Hello");
});

addEvent(button, 'click', function () {
  alert("world!");
});

Be aware also that the addEventListener and the IE's attachEvent methods have their differences, when you attach multiple handlers to an event with addEventListener they will be fired in the same order, as the handlers were bound (FIFO), attachEvent will trigger the handlers in the contrary order (LIFO).

Check an example here.

Solution 2:

TL;DR

On a modern browser:

<input type="button" id="someButton" value="Some Button">
<script>
document.getElementById("someButton").addEventListener("click", function() {
    alert("Hello");
}, false);
</script>

Note the script is after the button in the HTML. Why does it it matter? Keep reading.

document.getElementById("someButton").addEventListener("click", function() {
    snippet.log("Hello");
}, false);
<input type="button" id="someButton" value="Some Button">
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Oh good, you kept reading.

There are three parts to this:

  1. When to hook up the handler (the part the jQuery code is using ready for)

  2. How to find the element(s) to hook up (the part the jQuery code is using $("#someButton") for)

  3. How to hook up the handler (the part it's using click for)

When to hook up the handler

When handling events directly on an element, you can't hook up the event handler until the element exists. When dealing with elements that are in the page's main HTML, you know they exist once their HTML has been parsed. So for instance:

<div>...</div>
<script>
    // Here, we know that that div exists
</script>

So unless you have a good reason to do otherwise, put the script tags for your JavaScript code at the end of the document, just before the closing </body> tag. Then you know that all of the elements above (including document.body) exist.

If you have no choice but to put your script tags elsewhere (such as the anti-pattern that is putting them in <head>), then you have to resort to hooking up an event handler for when the page's content has been parsed. The standard one is DOMContentLoaded, which you hook on document. Unfortunately, IE8 and earlier don't support it, which is part of why jQuery provides the ready callback. You can also use the load event on the window object, but that doesn't fire until long after the page's elements have been parsed, because it waits for all external resources to load (all images, etc.) before firing.

How to find the element(s) to hook up

In your example, the element has an id value, and so you can use document.getElementById to get it; that returns a reference to the element object, or null if there is none with that id.

Modern browsers, and also IE8, support document.querySelector, which will find the first element matching any CSS selector. So for instance, document.querySelector('div') finds the first div in the document, if any (null if there aren't any). document.querySelector('div.foo table tr.bar') finds the first tr element with the class bar that's inside a table that's inside a div element with class foo.

Modern browsers (and IE8) also provide document.querySelectorAll, which gives you a list (collection) of all elements matching a CSS selector.

querySelector and querySelectorAll are also available on elements; when you use them on elements, they only look at that element's descendants.

There are also various other functions, like getElementsByTagName (almost universally supported, even in old IE), getElementsByName (using the name attribute; I have no idea how widely-supported it is), getElementsByClassName (look up by a single CSS class; IE8 and earlier don't have it), and some others.

How to hook up the handler

Modern browsers, including IE9 and higher in standards mode, support addEventListener. With IE8 and earlier, you have to use Microsoft's original attachEvent (which predates addEventListener).

theElement.addEventListener("click", function() {
    // ...your handler code here
}, false);

// or

theElement.attachEvent("onclick", function() {
    // ...your handler code here
});

Note that addEventListener just uses the event name ("click"), but attachEvent uses "on" plus the event name ("onclick"). Also note that addEventListener has a third argument which you almost always wan to set to false. (Recent browsers have made the argument optional, but it wasn't always, so if you may need to support older ones, include it.)

The primary advantage to using addEventListener or attachEvent is that doing so plays nicely with others, because an element can have more than one event handler registered for the same event when using these methods.

Elements also expose properties you can assign functions to, where the property name is on followed by the event name, for instance onclick:

theElement.onclick = function() {
    // Your handler code
};

This is supported universally cross-browser, but has the problem that an element can only have one handler attached in this way.

So, we can implement your jQuery code like this:

<script>
document.getElementById("someButton").onclick = function() {
    alert("Hello");
};
</script>
</body>
</html>

(Note where that tag is.) But that doesn't play nicely with others.

document.getElementById("someButton").onclick = function() {
    snippet.log("Hello");
};
<input type="button" id="someButton" value="Some Button">
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Or on a modern browser:

<script>
document.getElementById("someButton").addEventListener("click", function() {
    alert("Hello");
}, false);
</script>
</body>
</html>

document.getElementById("someButton").addEventListener("click", function() {
    snippet.log("Hello");
}, false);
<input type="button" id="someButton" value="Some Button">
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Or on IE8 and earlier:

<script>
document.getElementById("someButton").attachEvent("onclick", function() {
    alert("Hello");
});
</script>
</body>
</html>

document.getElementById("someButton").attachEvent("onclick", function() {
    snippet.log("Hello");
});
<input type="button" id="someButton" value="Some Button">
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="//tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

Now, suppose we use querySelectorAll or similar and get back a list of elements, instead of just one element? How do we hook up a handler on each of them?

We can use the techniques in this other answer to loop through the collection, as it will be an array-like object (but not an actual array). Say we want to look up a handler on all div elements with the class foo:

var list = document.querySelectorAll("div.foo");
Array.prototype.forEach.call(list, function(element) {
    element.addEventListener("click", handler, false);
});
function handler() {
    this.innerHTML = "You clicked me";
}

(Don't worry about that this, I cover that in "More to explore" below.)

var list = document.querySelectorAll("div.foo");
Array.prototype.forEach.call(list, function(element) {
    element.addEventListener("click", handler, false);
});
function handler() {
    this.innerHTML = "You clicked me";
}
<div class="foo">foo</div>
<div class="foo">foo</div>
<div class="foo">foo</div>
<div class="foo">foo</div>
<div class="bar">bar (no handler)</div>
<div class="bar">bar (no handler)</div>
<div class="bar">bar (no handler)</div>
<div class="bar">bar (no handler)</div>

More to explore

Event handling isn't just about getting the event. There are four other things event handlers sometimes want to do:

  1. Get information about the event (for instance, if a key was pressed, what key?)

  2. Prevent the default action of the event, if it has one (for instance, preventing the default in a submit event on a form stops it from being submitted)

  3. Stop propagation (bubbling) of the event to container elements

  4. Reference the element that the event was hooked on (so we can use the same handler on multiple elements)

#4 is easy: On a handler attached with any of the mechanisms we discussed above, within the handler, this will refer to the element that the event was hooked on. (This is not true of HTML attribute hookups, e.g. <div onclick="handler()">, which is one of the many reasons not to use them.) If you've done something else with this (which you can), you can also access the element you hooked the event on via the currentTarget property of the event object (more below).

Unfortunately, the way you do this is different on IE8 and earlier with attachEvent and on standards-compliant browsers (addEventListener). Here's a function, hookEvent, (which I originally wrote for this other answer) that lets you use the standards-based way even on old IE:

var hookEvent = (function() {
    var div;

    // The function we use on standard-compliant browsers
    function standardHookEvent(element, eventName, handler) {
        element.addEventListener(eventName, handler, false);
        return element;
    }

    // The function we use on browsers with the previous Microsoft-specific mechanism
    function oldIEHookEvent(element, eventName, handler) {
        element.attachEvent("on" + eventName, function(e) {
            e = e || window.event;
            e.preventDefault = oldIEPreventDefault;
            e.stopPropagation = oldIEStopPropagation;
            handler.call(element, e);
        });
        return element;
    }

    // Polyfill for preventDefault on old IE
    function oldIEPreventDefault() {
        this.returnValue = false;
    }

    // Polyfill for stopPropagation on old IE
    function oldIEStopPropagation() {
        this.cancelBubble = true;
    }

    // Return the appropriate function; we don't rely on document.body
    // here just in case someone wants to use this within the head
    div = document.createElement('div');
    if (div.addEventListener) {
        div = undefined;
        return standardHookEvent;
    }
    if (div.attachEvent) {
        div = undefined;
        return oldIEHookEvent;
    }
    throw "Neither modern event mechanism (addEventListener nor attachEvent) is supported by this browser.";
})();

Using hookEvent, we can do the three things listed earlier in the standards-based way:

  1. We receive the event object as the first argument to the handler function, which means we can use its currentTarget to get a reference to the element we hooked the event on, or target to get a reference to the element where the event originated, and so on

  2. We can prevent the default by calling the event object's preventDefault function

  3. We can stop propagation by calling the event object's stopPropagation function

  4. We can use this (or event.currentTarget) to reference the element where we hooked the event

hookEvent is not a thorough, all-singing, all-dancing event hookup function. (For one thing, I never bothered to support unhooking events with it.) It's sample code, mainly.

Reference material

  • Most of this is now covered by the HTML5 specification, which is about a lot more than HTML

  • Various aspects are defined by the DOM; list of specs here, current one is DOM4

  • Mozilla Developer Network has a huge range of reasonably accurate referenece material (unlike some other meta sites, which frequently have accuracy issues).

Solution 3:

How about something like this?

document.getElementById('someButton').onclick = function () { alert('Hello');}

Solution 4:

This answer isn't so simple without jQuery, because there are two major styles (depending on which browser you are using) for hooking up events. If you don't use one of those two styles, then your events can overwrite each other. Here's the simpler/overwriting style:

window.onload = function() {
    document.getElementById("someButton").onclick = function() {
        alert("Hello");
    }
}

But if you try and to the above twice, you'll only get one alert, because the second time will overwrite the first one. Alternatively you can do some browser/feature detection and then use the specific event mechanisms for specific browsers; for more info on that style check out QuirksMode.com:

http://www.quirksmode.org/js/events_advanced.html

Solution 5:

try this:

<html>

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Test clicks</title>


  <script type="text/javascript">
    function setClicks() {
      var element = document.getElementById('test');
      element.onclick = function() {
        alert('test');
      }
    }
  </script>
</head>

<body onload="setClicks();">
  <div id="test" name="test">
    test
  </div>


</body>

</html>