Ошибка манипуляции DOM Javascript с помощью removeChild и addEventListener
Я в основном строю довольно простой список. Напишите что-нибудь в поле ввода вверху, нажмите кнопку, и оно появится в ul под ним.
Я получаю две ошибки в своей консоли, я не совсем уверен, что я сделал неправильно.
Во-первых, в строке 12 слушатель этого события:
deleteBtn.addEventListener('click', removeItem);
Ошибка говорит: "Не удается прочитать свойство 'addEventListener' со значением NULL", и я считаю, что это потому, что deleteBtn не находится на странице при загрузке, он добавляется в DOM вместе с li, когда вы добавляете элементы в список.
Во-вторых, в строке 40:
selectedItem.removeChild(checkMark);
Ошибка говорит: "Не удалось выполнить" removeChild "на" Узле ": удаляемый узел не является потомком этого узла".
Вот весь мой код:
<!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();
}
3 ответа
Вместо добавления события в deleteBtn
до его создания добавьте встроенное событие, используя onclick
при создании кнопки и вызове функции removeItem
и передать контекст, используя this
Для второй проблемы listGroupItems
это массив. Так нет removeChild
метод в массиве.
Чтобы решить вторую проблему, получите индекс значка из childNodes
collection.Then использовать этот индекс, чтобы удалить этот конкретный дочерний элемент
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>
Переменная deleteBtn не определена, так как у вас нет по крайней мере одного элемента с идентификатором deleteBtn при загрузке страницы.
Для удаления элемента используйте removeChild()
метод, подпись это:
parentOfTarget.removeChild(target)
В следующем демо, шаблонные литералы и insertAdjacentHTML()
был использован для консолидации создания и вставки элементов в DOM. Кстати, вы не можете присвоить идентификатор элементу, который создан несколько раз. идентификаторы уникальны, так #deleteBtn
был удален. Идентификатор не нужен для кнопок, если вы все равно делегируете события.
Есть куча классов, включающих и выключающих, поэтому я помещаю их в массивы, а затем переключаю каждый класс на каждой итерации forEach()
Подробности прокомментированы в демоверсии
демонстрация
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>
${xInp.value}
<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>