4

I am new in JavaScript and still learning various things. Right now i'm stuck with adding and removing li elements from the list. I did the exercise just fine using jQuery, but now have difficulties with the pure JS version of the same task.

The main idea is to add a new li element by clicking on the button, and remove the the element by clicking on the X button right next to it. I have tried using 'this' and other advices mentioned in similar questions here on Stackoverflow, but nothing worked for me. Could you, please, guide me what am I doing wrong?

P.S. the adding function seems to be working in snippet, but console logs error: cannot read property 'addeventlistener' of null.

//declaring the variables
var btn = document.getElementsByClassName('btn');
var list = document.getElementById('list');
var add = document.getElementById('add');

//adding a new element to the list
add.addEventListener('click', function(){
  var newElement = document.createElement('LI');
  list.appendChild(newElement);
  newElement.innerHTML= "I am a new element<button class='btn'>X</button>";
});

//removing the clicked element
btn.addEventListener('click', function(){
  list.parentNode.removeChild(this);
});
ul li {
  decoration: none;
  display: block;
  margin-top: 1em;
  text-align: center;
  font-family:  'Avant Garde', Avantgarde, 'Century Gothic', CenturyGothic, AppleGothic, sans-serif;
  font-size: 18px;
}

#add {
  background-color: black;
  color: white;
  border: none;
  width: 280px;
  font-color: white;
  border-radius: 8px;
  font-size: 16px;
  padding: 15px;
  outline: none;
  text-align: center;
  margin: 20px auto;
  display: block;
}

#add:hover {
  background-color: #28364d;
  color: white;
  border: none;
  outline: none;
}

#add:active {
  position: relative;
  bottom: 2px;
}

.btn{
  margin-left: 10px;
  border-radius: 10px;
  background-color: #000;
  color: white;
  border: none;
  outline: none;
  font-size: 14px;
  font-family: sans-serif;
}

.btn:active {
position: relative;
bottom: 2px;
}
<div>
      <ul id="list">
        <li class="element">I am a new element<button class="btn">X</button></li>
        <li class="element">I am a new element<button class="btn">X</button></li>
        <li class="element">I am a new element<button class="btn">X</button></li>
        <li class="element">I am a new element<button class="btn">X</button></li>
        <li class="element">I am a new element<button class="btn">X</button></li>
      </ul>

    <button id="add">Add an element to the list</button>
</div>
Ani
  • 57
  • 1
  • 1
  • 7
  • 1
    to solve your immediate problem, as well as your future problems with the new elements, look into something called "event delegation" https://davidwalsh.name/event-delegate – rlemon Mar 16 '16 at 12:32
  • Thank you, I definitely will! – Ani Mar 16 '16 at 12:50

3 Answers3

2

In your case, btn is a node list of elements, not an element, so you can't attach an event to an array. You need to iterate through them:

for(i=0; i < btn.length; i++) {
   btn[i].addEventListener('click', function(){
      list.parentNode.removeChild(this);
   });
}
Marcos Pérez Gude
  • 21,869
  • 4
  • 38
  • 69
  • 1
    While correct, This won't work in the long term. He's adding elements dynamically. – rlemon Mar 16 '16 at 12:33
  • That's another task, I can improve the answer, but OP is asking for the `cannot read property 'addeventlistener' of null.`. You're right in dinamic elements. – Marcos Pérez Gude Mar 16 '16 at 12:34
2

This returns a non-live collection:

var btn = document.getElementsByClassName('btn');

Which means that it will only contain the objects which exist at the point of the method call. You need to call getElementsByClassName() after the creation of the new li elements, and attach the EventListeners on the invidual buttons. Just remember not to put an EventListener twice on the buttons.

A nicer solution

Better yet: do not use getElementsByClassName(), just attach the event handler directly in the function, in which you create the new button. That way, you don't have to worry about pre-existing event handlers:

add.addEventListener('click', function(){
    var newElement = document.createElement('LI');
    list.appendChild(newElement);
    newElement.innerHTML= "I am a new element<button class='btn'>X</button>";
    newElement.addEventListener('click', function () {
        this.parentNode.removeChild(this);
    });
});
meskobalazs
  • 15,741
  • 2
  • 40
  • 63
1

I am learning too and this was a good little challenge as a newcomer. Thanks to rlemon for pointing us in the right direction with that article link. I learnt something here.

In case you were interested what I arrived at: https://jsfiddle.net/nyxhj0tg/1/

JS:

var list = document.getElementById('list');
var add = document.getElementById('add');

//adding a new element to the list
add.addEventListener('click', function(){
  var newElement = document.createElement('LI');
  list.appendChild(newElement);
  newElement.innerHTML= "I am a new element<button class='btn'>X</button>";

});

list.addEventListener('click', function(e){
  if(e.target && e.target.nodeName == "BUTTON") {
        console.log("Button ", e, " was clicked!");
    e.target.parentNode.remove();
    }
});
Gerico
  • 5,079
  • 14
  • 44
  • 85