When working with JavaScript, you can sometimes need to create new elements on-the-fly, and from that, you’ll need to do something with that new element. It might be a click, which more often than not will need to execute a function.
The problem with dynamically created elements, is that they aren’t born with the same event handlers as the existing elements. Let’s say we have a list of items that you could click on to toggle/add a class name, when a new element is created and appended to that same list - it won’t work - the event handler attachment is missing. This tutorial is going to cover a pure JavaScript way of dynamically attaching event handlers to newly created elements, so they merge in seamlessly with your other elements.
Table of contents
Creating some markup
Let’s create some HTML to get started from, I am going to take the list scenario into account here, and create a simple <ul> with some links inside:
<ul id="links">
<li class="dynamic-link">List item 1</li>
<li class="dynamic-link">List item 2</li>
<li class="dynamic-link">List item 3</li>
<li class="dynamic-link">List item 4</li>
</ul>
Creating an onclick function
To create an onclick function is simple, we just target our element, and setup a click handler:
var element = document.getElementById('id');
element.onclick = function() {
// onclick stuff
}
It’s good practice to setup functions separately and then call them like so, especially when dealing with loops:
var element = document.getElementById('id');
function myFunction() {
// onclick stuff
}
element.onclick = myFunction; // Assigned
Attaching an onclick function
Taking our knowledge from above, we can loop through our HTML and attach the event handler to each <li> tag.
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components <ng-template> syntax
- <ng-container> and Observable Composition
- Advanced Rendering Patterns
- Setters and Getters for Styles and Class Bindings
First I’m going to setup querySelector, a native DOM selector, in a jQuery-style way using the dollar symbol:
// querySelector, jQuery style
var $ = function (selector) {
return document.querySelector(selector);
};
This allows us to do this to target what we need:
$('.className');
Using querySelector, let’s target our #links ID, and then find the list elements inside. We could use $(‘#links li’) but this would require querySelectorAll instead. I’ve then looped through the array of links, attaching the above ‘myFunction’ to each element.
var links = $('#links').getElementsByTagName('li');
// For each <li> inside #links
for (var i = 0; i < links.length; i++) {
var link = links[i];
link.onclick = myFunction;
}
That’s great, but let’s add a real function called dynamicEvent:
function dynamicEvent() {
this.innerHTML = 'Dynamic event success.';
this.className += ' dynamic-success';
}
// Assign it like so (this will be inside the loop)
link.onclick = dynamicEvent;
So far we’ve attached an onclick event handler to each static item on the page, which is easy. When we click on them now, they will run the dynamicEvent function and the text will change to ‘Dynamic event success.’.
Dynamically creating elements
Now we want to dive deeper and create a new <li> element using JavaScript, with some text inside, and append it to the #link unordered list. This would be easily done like so:
var li = document.createElement('li');
$('#links').appendChild(li);
Nice and easy, I’ve created a new element and appended it to our #links ID - no problem. But there is a problem! Simply appending the new list item will not magically allow me to click on it and run a function, which is often a problem when creating new elements. The link will do nothing, unless we create it and attach an event handler as well. AJAX also has this problem, pulling new information off the server will have no JavaScript readiness attached to it.
Attaching the event dynamically
This is a lot simpler than you think, in our function that will create our new element, we need to attach the event handler, and function we want to assign to it, this can be done like so:
// Create the new element
var li = document.createElement('li');
li.className = 'dynamic-link'; // Class name
li.innerHTML = dynamicValue; // Text inside
$('#links').appendChild(li); // Append it
li.onclick = dynamicEvent; // Attach the event!
All done. But let’s put it into a more practical use. “What can I use it for?” - anything! I ran into this when creating jResize and my browser-based responsive development tool (though I cheated a bit with jQuery so here’s the JavaScript way).
Practical usage
In the demo I’ve setup, you’ll see the existing list of items, give one or two a click and watch the text change and a nice icon appear. Voila! Now, the next step is to create your own element, which I’ve created a nice little script and small form to do exactly that. Simply type a word into the field input, and generate your element. The newly created element will be born with its onclick function attached.
Keeping functions outside the loop
JSLint likes to remind everyone that you shouldn’t create functions inside a loop, in some cases it’s okay to do, but for this tutorial I totally agree. It will save us from writing duplicated markup when running the function on both the static and dynamically created elements (which is why dynamicEvent is created outside the loop and simply called).
Demo function
For anyone interested to see how the demo works, utilising the steps above, you can have a look through this and the comments:
(function(){
// querySelector, jQuery style
var $ = function (selector) {
return document.querySelector(selector);
};
// Create function outside loop
function dynamicEvent() {
this.innerHTML = 'Dynamic event success.';
this.className += ' dynamic-success';
}
// Iterate over #links <li>
// Use querySelector to target #links and then get tag names <li>
var links = $('#links').getElementsByTagName('li');
// For each <li> inside #links
for (var i = 0; i < links.length; i++) {
var link = links[i];
// <li> onclick, runAlert function
link.onclick = dynamicEvent;
}
// Onsubmit
$('.generate').onsubmit = function() {
// Grab the input value
var dynamicValue = $('.generate-input').value;
// If empty value
if(!dynamicValue) {
alert('Please enter something.');
} else {
// Change the submit value
$('.generate-submit').value = 'Click your item below!';
// Create the links with the input value as innerHTML
var li = document.createElement('li');
li.className = 'dynamic-link';
li.innerHTML = dynamicValue;
// Append it and attach the event (via onclick)
$('#links').appendChild(li);
li.onclick = dynamicEvent;
}
// Prevent the form submitting
return false;
}
})();
Thank you for reading!