1

let btn = document.querySelector('button'),
    ul = document.getElementById('lista');


btn.onclick = () =>{
    ele =  ul.children[0].cloneNode(true, true);
    ele.innerHTML = 'item #' + (ul.children.length +1)
    ul.appendChild(ele);

}

 
Array.prototype.forEach.call(ul.children , child => {
    child.onclick = () => {
        document.getElementById('heading').textContent = child.textContent;
        Array.prototype.forEach.call(ul.children, wald  =>{
            wald.classList.remove('active')
        })
        child.classList.add('active');
        
    }
})
 
*{
  
    padding: 0;
    box-sizing: border-box;
    font-family: Arial, Helvetica, sans-serif;
}

.container{
    width: 60%;
    margin: 30px auto;
    border: 1px solid;
    padding: 30px 10px;


}


.container h1{
    text-align: center;
    background-color: tomato;
    color: #fff;

}

.container button{
    padding: 10px 20px;
    background-color: tomato;
    font-weight: bold;
    font-size: 18px;
    color: #FFF;
    border: none;
    cursor: pointer;


}
.container button:active{
    transform: scale(.99)
}


.container ul li{
    list-style-position: inside;
    padding: 7px 15px
}

.active{
    background-color: tomato;
    color: #FFF;
}
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>Page Title</title>
    <link rel="stylesheet" href="css/app.css" />
</head>

<body>
    <div class="container">
        <h1 id="heading">item here ! </h1>
        <button>add item</button>
        <ul id="lista">
            <li>item #1</li>
            <li>item #2</li>
            <li>item #3</li>
        </ul>
    </div>
     
    <script src="js/app.js"></script>
</body>

</html>

Cloned elements problem

The first event of this code to add new cloned elements to the <ul> when I click the button.

The second event when I click on the li it suppose to have the class .active .. but this second event only works with the original items not the new ones.

So what is the solution to attach the handler to new elements?

Brian Tompsett - 汤莱恩
  • 5,195
  • 62
  • 50
  • 120

3 Answers3

1

The event handler is being called only once in the begining, so it only sees the existing elements. My fix is that the event handler is called every time new elements are being added. Also, to optimize it you don't have to go through the loop every time and add event listeners on all items. Only the new items will get the event listeners added on button click

let btn = document.querySelector('button'),
  ul = document.getElementById('lista');


btn.onclick = () => {
  ele = ul.children[0].cloneNode(true, true);
  ele.innerHTML = 'item #' + (ul.children.length + 1)
  ul.appendChild(ele);
  addHandlerToItem(ele);
}

var eventHandler = function() {
  Array.prototype.forEach.call(ul.children, child => {
    addHandlerToItem(child);
  });
}

var addHandlerToItem = function(child) {
  child.onclick = () => {
    document.getElementById('heading').textContent = child.textContent;
    Array.prototype.forEach.call(ul.children, wald => {
      wald.classList.remove('active')
    })
    child.classList.add('active');
  }
};


eventHandler();
* {
  padding: 0;
  box-sizing: border-box;
  font-family: Arial, Helvetica, sans-serif;
}

.container {
  width: 60%;
  margin: 30px auto;
  border: 1px solid;
  padding: 30px 10px;
}

.container h1 {
  text-align: center;
  background-color: tomato;
  color: #fff;
}

.container button {
  padding: 10px 20px;
  background-color: tomato;
  font-weight: bold;
  font-size: 18px;
  color: #FFF;
  border: none;
  cursor: pointer;
}

.container button:active {
  transform: scale(.99)
}

.container ul li {
  list-style-position: inside;
  padding: 7px 15px
}

.active {
  background-color: tomato;
  color: #FFF;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>Page Title</title>
  <link rel="stylesheet" href="css/app.css" />
</head>

<body>
  <div class="container">
    <h1 id="heading">item here ! </h1>
    <button>add item</button>
    <ul id="lista">
      <li>item #1</li>
      <li>item #2</li>
      <li>item #3</li>
    </ul>
  </div>

  <script src="js/app.js"></script>
</body>

</html>
Afron Orana
  • 584
  • 3
  • 12
0

Obviously the "li" elements you create with a click on "button" do not exist when you first loop through ul.children,so no event listener will be registered to any newly created "li" element.

What you need to use is called event delegation.Listen to the clicks on the parent or any other ancestor of "#lista>li" so that a click will be detected no matter which li element is clicked.

What is DOM Event delegation?

let btn = document.querySelector('button'),
  ul = document.getElementById('lista');


btn.onclick = () => {
  ele = ul.children[0].cloneNode(true, true);
  ele.innerHTML = 'item #' + (ul.children.length + 1)
  ul.appendChild(ele);

}

document.addEventListener('click', function(event) {
  switch (true) {
    case event.target.matches('#lista>li'):
      [].forEach.call(document.querySelectorAll('#lista>li'), function(el) {
        el.classList.remove('active');
      })
      event.target.classList.add('active');

  }


})
* {
  padding: 0;
  box-sizing: border-box;
  font-family: Arial, Helvetica, sans-serif;
}

.container {
  width: 60%;
  margin: 30px auto;
  border: 1px solid;
  padding: 30px 10px;
}

.container h1 {
  text-align: center;
  background-color: tomato;
  color: #fff;
}

.container button {
  padding: 10px 20px;
  background-color: tomato;
  font-weight: bold;
  font-size: 18px;
  color: #FFF;
  border: none;
  cursor: pointer;
}

.container button:active {
  transform: scale(.99)
}

.container ul li {
  list-style-position: inside;
  padding: 7px 15px
}

.active {
  background-color: tomato;
  color: #FFF;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>Page Title</title>
  <link rel="stylesheet" href="css/app.css" />
</head>

<body>
  <div class="container">
    <h1 id="heading">item here ! </h1>
    <button>add item</button>
    <ul id="lista">
      <li>item #1</li>
      <li>item #2</li>
      <li>item #3</li>
    </ul>
  </div>

  <script src="js/app.js"></script>
</body>

</html>
mpm
  • 19,494
  • 7
  • 46
  • 55
0

After add a new element to DOM, you need to set the onclick for this element to.

let btn = document.querySelector('button'),
    ul = document.getElementById('lista');


btn.onclick = () =>{
    ele =  ul.children[0].cloneNode(true, true);
    ele.innerHTML = 'item #' + (ul.children.length +1)
    ul.appendChild(ele);
    onClick(ul.children, ele);
}

Array.prototype.forEach.call(ul.children , child => {
    onClick(ul.children, child);      
});

function onClick(children, child) {
    child.onclick = () => {
        document.getElementById('heading').textContent = child.textContent;
        Array.prototype.forEach.call(children, wald  =>{
            wald.classList.remove('active')
        })
        child.classList.add('active');
    }
}
 
*{
  
    padding: 0;
    box-sizing: border-box;
    font-family: Arial, Helvetica, sans-serif;
}

.container{
    width: 60%;
    margin: 30px auto;
    border: 1px solid;
    padding: 30px 10px;


}


.container h1{
    text-align: center;
    background-color: tomato;
    color: #fff;

}

.container button{
    padding: 10px 20px;
    background-color: tomato;
    font-weight: bold;
    font-size: 18px;
    color: #FFF;
    border: none;
    cursor: pointer;


}
.container button:active{
    transform: scale(.99)
}


.container ul li{
    list-style-position: inside;
    padding: 7px 15px
}

.active{
    background-color: tomato;
    color: #FFF;
}
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>Page Title</title>
    <link rel="stylesheet" href="css/app.css" />
</head>

<body>
    <div class="container">
        <h1 id="heading">item here ! </h1>
        <button>add item</button>
        <ul id="lista">
            <li>item #1</li>
            <li>item #2</li>
            <li>item #3</li>
        </ul>
    </div>
     
    <script src="js/app.js"></script>
</body>

</html>