-1

I'm basically building a pretty simple list. Write something in the input field at the top, click the button and it appears in a ul underneath.

I'm getting two errors in my console, I'm not really sure what I've done wrong.

Firstly, on line 12, this event listener:

deleteBtn.addEventListener('click', removeItem);

The error says "Cannot read property 'addEventListener' of null" and I believe it is because the deleteBtn is not on the page at load, it is added to the DOM with the li as you add items to the list.

Secondly, on line 40:

selectedItem.removeChild(checkMark);

The error says "Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node."

Here is all of my code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Bootstrap Crash Course</title>

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css"
        integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb"
        crossorigin="anonymous" />
  <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"
        integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN"
        crossorigin="anonymous">

  <style type="text/css">
    #myButton {
      cursor: pointer;
    }

    .input-group {
      margin: 15px 0;
    }

    .fa-times-circle-o {
      font-size: 24px;
      cursor: pointer;
    }

    .fa-check-circle {
      font-size: 24px;
    }

  </style>
</head>
<body>

<div class="container">

  <div class="input-group">
    <input type="text" class="rounded form-control" id="myInput" />
    <span id="myButton" class="input-group-addon">Click</span>
  </div>

  <ul class="list-group" id="myOutput">

  </ul>

</div> <!-- .containter -->




  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
          integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
          crossorigin="anonymous"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js"
          integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh"
          crossorigin="anonymous"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"
          integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ"
          crossorigin="anonymous"></script>
  <script src="app.js"></script>
</body>
</html>

//Code from app.js

let myButton = document.getElementById('myButton');
let myOutput = document.getElementById('myOutput');
let myInput = document.getElementById('myInput');
let listGroupItems = document.querySelectorAll('.list-group-item');
let deleteBtn = document.getElementById('deleteBtn');

setUpEventListeners();

function setUpEventListeners() {
  myButton.addEventListener('click', addItem);
  myOutput.addEventListener('click', toggleItem);
  deleteBtn.addEventListener('click', removeItem);
}

function addItem() {
  if (myInput.value === '') {
    console.log('Field is empty!');
  } else {
    let li = document.createElement('li');
    let inputValue = document.createTextNode(myInput.value);

    li.innerHTML = '<i class="fa fa-times-circle-o float-right" aria-hidden="true" id="deleteBtn"></i>';
    li.className = 'list-group-item';
    myOutput.appendChild(li);
    li.appendChild(inputValue);
  }
  myInput.value = '';
}

function toggleItem(e) {
  let selectedItem = e.target;
  let checkMark = document.createElement('i');
  checkMark.classList.add('fa', 'fa-check-circle', 'float-left');

  console.log(selectedItem);

  if (selectedItem.classList.contains('bg-success') && selectedItem.classList.contains('list-group-item')) {
    selectedItem.classList.remove('bg-success');
    selectedItem.classList.remove('text-white');
    //listGroupItems.removeChild(checkMark);
  } else if (!selectedItem.classList.contains('bg-success') && selectedItem.classList.contains('list-group-item')) {
    selectedItem.classList.add('bg-success');
    selectedItem.classList.add('text-white');
    selectedItem.appendChild(checkMark);
  }
}

function removeItem() {
  e.target.parentElement.remove();
}
Greggo
  • 97
  • 1
  • 2
  • 11

3 Answers3

0

the deleteBtn variable is undefined, since you don't have atleast one item with the id deleteBtn on the page load.

VarunDevPro
  • 346
  • 2
  • 11
0

Instead of adding event to deleteBtn prior to its creation add inline event using onclick while creation of the button and call the function removeItem and pass the context using this

For the second problem listGroupItems is a an array. So there is no removeChild method in array.

To solve the second problem get the index of the icon from the childNodes collection.Then use that index to remove this specific child

let myButton = document.getElementById('myButton');
let myOutput = document.getElementById('myOutput');
let myInput = document.getElementById('myInput');
let listGroupItems = document.querySelectorAll('.list-group-item');


setUpEventListeners();

function setUpEventListeners() {
  myButton.addEventListener('click', addItem);
  myOutput.addEventListener('click', toggleItem);

}

function addItem() {
  if (myInput.value === '') {
    console.log('Field is empty!');
  } else {
    let li = document.createElement('li');
    let inputValue = document.createTextNode(myInput.value);
    // Changed here adding onclick
    li.innerHTML = '<i class="fa fa-times-circle-o float-right" aria-hidden="true" onclick="removeItem(this)" class="deleteBtn"></i>';
    li.className = 'list-group-item';
    myOutput.appendChild(li);
    li.appendChild(inputValue);

  }
  myInput.value = '';
}

function toggleItem(e) {
  let selectedItem = e.target;
  if (selectedItem.classList.contains('bg-success') && selectedItem.classList.contains('list-group-item')) {
    selectedItem.classList.remove('bg-success');
    selectedItem.classList.remove('text-white');
    var iconIndex = '';
    // getting the index of the icon which have the specifc class from childNodes using its class
    for (var i = 0; i < selectedItem.childNodes.length; i++) {
      if (selectedItem.childNodes[i].className === "fa fa-check-circle float-left") {
        iconIndex = i;
      }
    }
    // Using that index to remove the icon child
    selectedItem.removeChild(selectedItem.childNodes[iconIndex]);
  } else if (!selectedItem.classList.contains('bg-success') && selectedItem.classList.contains('list-group-item')) {
    let checkMark = document.createElement('i');
    checkMark.classList.add('fa', 'fa-check-circle', 'float-left');
    selectedItem.classList.add('bg-success');
    selectedItem.classList.add('text-white');
    selectedItem.appendChild(checkMark);
  }
}

function removeItem(elem) {
  elem.parentNode.remove();
}
#myButton {
  cursor: pointer;
}

.input-group {
  margin: 15px 0;
}

.fa-times-circle-o {
  font-size: 24px;
  cursor: pointer;
}

.fa-check-circle {
  font-size: 24px;
}
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css" integrity="sha384-PsH8R72JQ3SOdhVi3uxftmaW6Vc51MKb0q5P2rRUpPvrszuE4W1povHYgTpBfshb" crossorigin="anonymous" />
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous" />
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js" integrity="sha384-vFJXuSJphROIrBnz7yo7oB41mKfc8JzQZiCq4NCceLEaO4IHwicKwpJf9c9IpFgh" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js" integrity="sha384-alpBpkh1PFOepccYVYDB4do5UnbKysX5WZXm3XxPqe5iKTfUKjNkCk9SaVuEZflJ" crossorigin="anonymous"></script>

<div class="container">

  <div class="input-group">
    <input type="text" class="rounded form-control" id="myInput" />
    <span id="myButton" class="input-group-addon">Click</span>
  </div>

  <ul class="list-group" id="myOutput">

  </ul>

</div>
brk
  • 48,835
  • 10
  • 56
  • 78
  • Your code worked :) I'm just going through it now to make sure I understand what is happening with the for loop. SO with iconIndex, we are counting all of the children of selectedItem (the li) and when the className of a node is equal to the checkmark's classes it assigned the index to iconIndex, and now that we know the index of the checkmark, we can remove it from the DOM below the for loop with .childNodes[iconIndex] – Greggo Dec 24 '17 at 19:24
0

To remove an element use removeChild() method, signature is:

parentOfTarget.removeChild(target)

In the following demo, Template Literals and insertAdjacentHTML() was used to consolidate element creation and insertion into the DOM. BTW you can't assign an id to an element that's created multiple times. ids are unique so #deleteBtn was removed. An id isn't needed on the buttons if you delegate events anyways.

There's a ton of classes going on and off so I put them in arrays and then toggled each class on each iteration of forEach()

Details commented in Demo

Demo

var xBtn = document.getElementById('xButton');
var xOut = document.getElementById('xOutput');
var xInp = document.getElementById('xInput');

xBtn.addEventListener('click', addItem);
xOut.addEventListener('click', iconClick);

function addItem() {
  if (xInp.value === '') {
    console.log('Field is empty!');
  } else {
    /* This is a Template Literal which is a string
    || with powerful syntax and methods
    */
    let li = `<li class='list-group-item'>
    <i class="fa fa-circle-o float-left"></i>
    &nbsp;${xInp.value}&nbsp;
    <i class="fa fa-times-circle-o float-right"></i>
    </li>`;

    // Use insertAdjacentHTML() instead of innerHTML
    xOut.insertAdjacentHTML('beforeend', li);
  }
  xInp.value = '';
}

function iconClick(e) {

  // Reference e.target
  var tgt = e.target;
  // Reference e.target's parent
  var item = tgt.parentElement;

  /* if e.target has class .float-left
  || forEach() will...
  */
  if (tgt.classList.contains('float-left')) {

    // toggle e.target's FA icon classes
    ['fa-check-circle', 'fa-circle-o'].forEach(function(c, idx) {
      tgt.classList.toggle(c);
    });

    // toggle e.target's parent's state classes
    ['bg-success', 'text-white'].forEach(function(i, idx) {
      item.classList.toggle(i);
    });

    /* ...but if it has class .float-right
    || remove the parent of e.target by referencing
    || the parent of the parent of e.target (grandma)
    */
  } else if (tgt.classList.contains('float-right')) {
    xOut.removeChild(item);

    // ...otherwise just end function
  } else {
    return false;
  }
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Bootstrap Crash Course</title>

  <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/css/bootstrap.min.css">
  <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">

  <style>
    #xButton {
      cursor: pointer;
    }
    
    .input-group {
      margin: 15px 0;
    }
    
    .fa {
      font-size: 24px;
      cursor: pointer;
    }
  </style>
</head>

<body>

  <div class="container">

    <div class="input-group">
      <input type="text" class="rounded form-control" id="xInput" />
      <button id="xButton" class="btn btn-primary input-group-addon">ADD</button>
    </div>

    <ul class="list-group" id="xOutput">

    </ul>

  </div>
  <!-- .containter -->




  <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.3/umd/popper.min.js"></script>
  <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.2/js/bootstrap.min.js"></script>

</body>

</html>
Community
  • 1
  • 1
zer00ne
  • 41,936
  • 6
  • 41
  • 68