Adding Event Handlers on Dynamically Created Elements with Vanilla Javascript

In jQuery adding event handlers to dynamic elements is quite simple. But when you try to do the same thing with pure Javascript, it is not very direct.

With pure Javascript attaching event handlers on elements that don't yet exist in the page will throw an error, and the event handler code won't work.

For example in the below snippet, an event handler is defined for an element. This element is appended to a container later on with Javascript.

<div id="button-container"></div>
// #submit-button is not yet present so this wil throw an error
document.querySelector("#submit-button").addEventListener('click', function() {
	alert('CLICKED');
});

// #submit-button is dynamically added with Javascript
document.querySelector("#button-container").innerHTML = '<button id="submit-button">Submit</button>';

// click on #submit-button is not going to work
document.querySelector("#submit-button").click();

Creating the element first and then attaching the event handler will work, but obviously the usual pattern is to define event listeners separately.

To keep the event handling code of the dynamic element separate as if it is present in the DOM, the event delegation model can be used.

Event Delegation — Attaching Event Handler on Parent of the Dynamically Created Element

The yet-to-be created dynamic element is not present in the DOM, so event handlers cannot be attached to it. But its parent is present in the DOM, so event handler can be attached to the parent element.

In case the parent is also going to be dynamically created, then the body element or the document element can be chosen as the parent.

So the event is attached to the parent container. The event object passed to the callback function contains a property target that holds the DOM element which caused the event to occur. This target can be compared whether it is the same dynamic element which is created. If yes, the event handler code can be executed.

Going by the same example above, the codes would look like :

<div id="button-container"></div>
// event is added for the parent of #submit-button
document.querySelector("#button-container").addEventListener('click', function(e) {
	if(e.target.id == 'submit-button') {
		alert('CLICKED');
	}
});

// #submit-button is dynamically created
document.querySelector("#button-container").innerHTML = '<button id="submit-button">Submit</button>';

// click on #submit-button will now work
document.querySelector("#submit-button").click();

You can compare the id attribute of the target with the id of the dynamic element to check if they are the same.

If you want to compare it based on CSS class, the classList attribute of target can be checked.

document.querySelector("#button-container").addEventListener('click', function(e) {
	// check whether class "submit-button" is present in the CSS classes of target
	if(e.target.classList.contains('submit-button')) {
		alert('CLICKED');
	}
});

Background Concepts

The event delegation model is based on event bubbling and event capturing. Javascript Bubbling and Capturing is a good tutorial on it.

Writing jQuery find() in Pure Javascript
Writing jQuery closest() in Pure Javascript