ref – https://stackoverflow.com/questions/34896106/attach-event-to-dynamic-elements-in-javascript
index.html
1) we write an HTML page with a ul list that has three items.
2) apply some js effects to our li elements
3) create a button that would dynamically add a li item
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<html lang="en-US"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Today's Shopping List</title> </head> <body> <button onclick="addItem()">Click me</button> <ul id="groceries"> <li class="item">milk</li> <li class="item">coconut</li> <li class="item">mango</li> </ul> </body> <script src="./script.js"></script> <script> function addItem() { var element = document.createElement("li"); var textNode = document.createTextNode("Food Item"); element.appendChild(textNode); element.classList.add("item"); document.getElementById("groceries").appendChild(element); } </script> </html> |
script.jsjs
We have a simple script that attaches inline css and js effects to all the li elements.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
let listItems = document.getElementsByTagName("li"); for (let item of listItems) { // css item.style.border = "2px solid red"; item.style.padding = "10px"; item.style.margin = "8px"; // events item.addEventListener("click", function(e) { alert(e.target.textContent); }); } |
Now, when you run the page, you’ll see the list with its effects. Specifically, some js added inline CSS effects and a click handler that displays the text of the li element.
This is because when the page has finished putting its DOM together, it runs the script.js file, and it also declares the addItem function.
The execution of script.js file makes it so that it applies the inline CSS and click handler to the existing li elements in the DOM.
The declaration of the addItem function exists to that later when the user clicks on the add button, we have a function that can run.
Thus, all js code executed so far comes after the readiness of the DOM.
Now, when you click on the li element, it would respond to the click event like so:
However, there is a problem. When you click on the ‘add’ button to dynamically add an item, you don’t see the inline css effects anymore. If you try clicking on the li elements, nothing happens:
CSS
Inline CSS is when we attach the CSS to the element itself.
Internal CSS is when we declare CSS on top of the html page.
External CSS is when we attach the CSS code in a separate file.
Thus, when we use inline CSS, it only applies to the loaded DOM. Any dynamic new data that comes in after won’t get the CSS effects.
In order to have CSS effects on all elements, including later added dynamic elements, simply use internal or external CSS.
In your script.js remove the css:
1 2 3 4 5 6 7 8 |
let listItems = document.getElementsByTagName("li"); for (let item of listItems) { // events item.addEventListener("click", function(e) { alert(e.target.textContent); }); } |
Then in the head tag of the html page, add some internal CSS:
1 2 3 4 5 6 7 8 9 10 11 12 |
<head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Today's Shopping List</title> <style> li { border: 2px solid red; padding: 10px; margin: 8px; } </style> </head> |
Now, when you add dynamic li elements, you’ll see tht the CSS is applied immediately:
Note that you can also apply some inline CSS when the items were created and added to the DOM. That will work too, but it’s cumbersome, and we miss out on the benefits that CSS brings us:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function addItem() { var element = document.createElement("li"); var textNode = document.createTextNode("Food Item"); element.appendChild(textNode); element.classList.add("item"); // you can apply styles via js on the add. element.style.border = "2px solid red"; element.style.padding = "10px"; element.style.margin = "8px"; document.getElementById("groceries").appendChild(element); } |
Events were not applied
If you were to click on the li items, you’ll notice that even though internal/external CSS will aways apply to past, current, and future DOM changes, JS events won’t be applied to future DOM additions.
In order to apply it, we’ll implement an add event listener. It simply listens for a certain event, and when that event happens, it executes code.
script.js
1 2 3 4 5 6 7 8 9 10 11 12 13 |
} document.addEventListener('click', function (e) { // add event listener to: 'click' events on elements with class 'item' if (hasClass(e.target, 'item')) { console.log('---> .item clicked'); console.log(e.target); } }, false); function hasClass(elem, className) { return elem.className.split(' ').indexOf(className) > -1; } |
Now when you refresh the page, every newly added li elements will the click event.
In jQuery, you’d use the .on function
In this way, all dynamically added elements will have your chosen events bound.
In certain situations when you are working with templates, a lot of effects are applied by JS on data elements. When these data elements are added dynamically, you won’t see your effects for these newly added elements. What you have to do is:
1) Make sure effects on data that are added/removed are CSS based.
2) Using addEventListener or .on, you’ll have to apply these effects on the fly.