Attaching Event Handlers to dynamically created JavaScript elements
Tags: javascript
Published: 2020-09-30 14:28:13

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
* Creating an onclick function
* Attaching an onclick function
* Dynamically creating elements
* Attaching the event dynamically
* Practical usage
* Keeping functions outside the loop
* Demo function


**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


```javascript
<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 our element, and setup a click handler:

```javascript
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:

```javascript
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.

First I’m going to setup querySelector, a native DOM selector, in a jQuery-style way using the dollar symbol:

```javascript
// querySelector, jQuery style
var $ = function (selector) {
  return document.querySelector(selector);
};
```
This allows us to do this to target what we need:

```javascript
$('.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.


```javascript
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:

```javascript
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:

```javascript
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:

```javascript
// 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 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, utilizing the steps above, you can have a look through this and the comments:


```javascript
(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!!!