0

I write a very simple Todo app with JS. It's work fine, I have some default tasks that I can click on them to change them to done or undone, but the problem is when I add a new task, I can't do anything on it.

const input   = document.querySelector('#todoText');
const todo  = document.querySelector('#todo');
const todoLi  = document.querySelectorAll('#todo li');

// Add Todo
input.addEventListener('keyup', e => {
  if (e.keyCode === 13) {
    // Get input value
    let val = e.target.value;
    // Create <li>
    const li = document.createElement('li');
    // Create text node from input value
    let text = document.createTextNode(val);
    // Pass input value into <li>
    li.appendChild(text);
    // Add input value in Todo list
    todo.appendChild(li);
    // Reset input value after enter
    e.target.value = '';
  }
})


todoLi.forEach( item => {
  item.addEventListener('click', e => {

    if ( e.target.classList.contains('done') ) {
      e.target.classList.remove('done')
    } else {
      e.target.classList.add('done')
    }

  })
})

This is my PEN on Codepen

pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
  • Possible duplicate of [Event listener for current and future elements Without jQuery](https://stackoverflow.com/questions/33898188/event-listener-for-current-and-future-elements-without-jquery) – 31piy Jul 21 '18 at 18:02

2 Answers2

1

Add

li.addEventListener('click', e => {
  if ( e.target.classList.contains('done') ) {
    e.target.classList.remove('done')
  } else {
    e.target.classList.add('done')
  }
});

After

li.appendChild(text);

To add a click listener on the node before you insert it into the DOM.

Forked and fixed snippet:

const input   = document.querySelector('#todoText');
const todo  = document.querySelector('#todo');
const todoLi  = document.querySelectorAll('#todo li');

// Add Todo
input.addEventListener('keyup', e => {
  if (e.keyCode === 13) {
    // Get input value
    let val = e.target.value;
    // Create <li>
    const li = document.createElement('li');
    // Create text node from input value
    let text = document.createTextNode(val);
    // Pass input value into <li>
    li.appendChild(text);
    
    li.addEventListener('click', e => {
  if ( e.target.classList.contains('done') ) {
    e.target.classList.remove('done')
  } else {
    e.target.classList.add('done')
  }
});
    // Add input value in Todo list
    todo.appendChild(li);
    // Reset input value after enter
    e.target.value = '';
  }
})


todoLi.forEach( item => {
  item.addEventListener('click', e => {
    
    if ( e.target.classList.contains('done') ) {
      e.target.classList.remove('done')
    } else {
      e.target.classList.add('done')
    }

  })
})
body {
  margin: 50px;
}

input {
  padding: 5px 15px;
  background: #eee;
  border: 0;
  width: 100%;
  margin-left: 10px;
  border: solid 2px #eee;
  border-radius: 50px;
  transition: all 350ms;
  font-size: 12px;
  &:focus {
    border: solid 2px #eee;
    background: #fff;
    outline: none;
  }
}

ul {
  li {
    position: relative;
    cursor: pointer;
    transition: all 350ms;
    &:hover {
      &:before {
        content: '';
        position: absolute;
        left: -25px
      }
    }
  }
}

.done {
  text-decoration: line-through;
  color: #888;
}
<div class="d-flex align-items-center mb-5">
  <span class="font-weight-bold text-muted">TODO:</span>
  <input id="todoText" placeholder="Write something and press Enter">
</div>

<small>Todo list:</small>
<ul id="todo">
  <li>Add something new </li>
  <li>This is a todo app with JS </li>
  <li class="done">I'm a done task </li>
</ul>

<hr>
pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
1

You might solve your problem and simplify your code by adding click event listener to #todo, not to li elements:

const input = document.querySelector('#todoText');
const todo = document.querySelector('#todo');

// Add Todo
input.addEventListener('keyup', e => {
  if (e.keyCode === 13) {
    // Get input value
    let val = e.target.value;
    // Create <li>
    const li = document.createElement('li');
    // Create text node from input value
    let text = document.createTextNode(val);
    // Pass input value into <li>
    li.appendChild(text);
    // Add input value in Todo list
    todo.appendChild(li);
    // Reset input value after enter
    e.target.value = '';
  }
})


todo.addEventListener('click', e => {
  var li = e.target;
  while (li.nodeName.toLowerCase() !== 'li') {
    if (li === this) return;
    li = li.parentNode;
  }
  li.classList.toggle('done')
})
body {
  margin: 50px;
}

input {
  padding: 5px 15px;
  background: #eee;
  border: 0;
  width: 100%;
  margin-left: 10px;
  border: solid 2px #eee;
  border-radius: 50px;
  transition: all 350ms;
  font-size: 12px;
  &:focus {
    border: solid 2px #eee;
    background: #fff;
    outline: none;
  }
}

ul {
  li {
    position: relative;
    cursor: pointer;
    transition: all 350ms;
    &:hover {
      &:before {
        content: '';
        position: absolute;
        left: -25px
      }
    }
  }
}

.done {
  text-decoration: line-through;
  color: #888;
}
<div class="d-flex align-items-center mb-5">
  <span class="font-weight-bold text-muted">TODO:</span>
  <input id="todoText" placeholder="Write something and press Enter">
</div>

<small>Todo list:</small>
<ul id="todo">
  <li>Add something new </li>
  <li>This is a todo app with JS </li>
  <li class="done">I'm a done task </li>
</ul>

<hr>
Kosh
  • 16,966
  • 2
  • 19
  • 34