#javascript
#javascript
Вопрос:
Я создаю простое в использовании приложение на Javascript. Пользователь может ввести новый элемент списка задач. Я также хочу, чтобы пользователь мог редактировать существующий элемент to-do.
Итак, я пытаюсь создать кнопку с функцией onClick для редактирования диапазона. Но он продолжает возвращать ошибку «span равен нулю».
Как я могу это исправить?
Вот мой код на данный момент:
function addToDo() {
let input = document.getElementById("input").value " ";
var list = document.getElementById("list1");
var li = document.createElement("li");
var doneButton = document.createElement("button");
doneButton.innerHTML = "Done";
doneButton.onclick = moveToDo;
doneButton.className = "move";
var editButton = document.createElement("button");
editButton.innerHTML = "Edit";
editButton.onclick = editToDo;
editButton.className = "edit";
var deleteButton = document.createElement("button");
deleteButton.innerHTML = "Delete";
deleteButton.onclick = deleteToDo;
deleteButton.className = "delete";
var span = document.createElement("span");
span.textContent = input;
span.className = "node";
li.appendChild(span);
li.appendChild(doneButton);
li.appendChild(editButton);
li.appendChild(deleteButton);
list.appendChild(li);
}
function moveToDo(x) {
x.preventDefault();
var button = x.target;
var li = button.closest("li");
button.remove();
document.getElementById("list2").append(li);
}
function editToDo(b) {
b.preventDefault();
var button = b.target;
var span = button.closest('span');
var edit = prompt("Edit to-do item:");
span.appendChild(edit);
}
function deleteToDo(x) {
x.preventDefault();
var button = x.target;
var li = button.closest("li");
li.remove();
}
<title>emil avara amp;nbsp;amp;mdash;amp;nbsp; inlämningsuppgift 2</title>
<h1 class="center">BIG BOI TO DO LIST APPLICATION</h1>
<!-- <a href=""><span id="reset" onclick="window.location.reload();">🔄</span></a> -->
<div id="input-wrapper">
<input type="text" id="input" placeholder="Add a to-do"></input>
<button id="add-button" onclick="addToDo()">Add</button>
</div>
<div id="wrapper">
<div id="list1-div">
<h1>TO-DO</h1>
<ul id="list1">
</ul>
</div>
<div id="list2-div">
<h1>DONE</h1>
<ul id="list2">
</ul>
</div>
</div>
Комментарии:
1. Я сделал вам фрагмент. Это выдает ошибки консоли
2. я не включил весь код, отредактировал его сейчас. теперь все работает, кроме кнопки редактирования.
3. Смотрите обновленный ответ. Я сделал более элегантную версию.
Ответ №1:
- ближайший не работает с братьями и сестрами
- для добавления требуется узел. У вас есть строка
Это будет работать лучше
var span = button.closest('li').querySelector('span');
var edit = prompt("Edit to-do node:");
span.textContent = edit;
function moveToDo() {}
function deleteToDo() {}
function addToDo() {
let input = document.getElementById("input").value " ";
var list = document.getElementById("list1");
var li = document.createElement("li");
var doneButton = document.createElement("button");
doneButton.innerHTML = "Done";
doneButton.onclick = moveToDo;
doneButton.className = "move";
var editButton = document.createElement("button");
editButton.innerHTML = "Edit";
editButton.onclick = editToDo;
editButton.className = "edit";
var deleteButton = document.createElement("button");
deleteButton.innerHTML = "Delete";
deleteButton.onclick = deleteToDo;
deleteButton.className = "delete";
var span = document.createElement("span");
span.textContent = input;
span.className = "node";
li.appendChild(span);
li.appendChild(doneButton);
li.appendChild(editButton);
li.appendChild(deleteButton);
list.appendChild(li);
}
function editToDo(b) {
b.preventDefault();
var button = b.target;
var span = button.closest('li').querySelector('span');
var edit = prompt("Edit to-do node:");
span.textContent = edit;
}
<title>emil avara amp;nbsp;amp;mdash;amp;nbsp; inlämningsuppgift 2</title>
<h1 class="center">BIG BOI TO DO LIST APPLICATION</h1>
<!-- <a href=""><span id="reset" onclick="window.location.reload();">🔄</span></a> -->
<div id="input-wrapper">
<input type="text" id="input" placeholder="Add a to-do"></input>
<button id="add-button" onclick="addToDo()">Add</button>
</div>
<div id="wrapper">
<div id="list1-div">
<h1>TO-DO</h1>
<ul id="list1">
</ul>
</div>
<div id="list2-div">
<h1>DONE</h1>
<ul id="list2">
</ul>
</div>
</div>
Вот более элегантная версия, вдохновленная рефакторингом Mister Jojo, но с моими собственными предпочтениями
- сделал кнопки type=»button» и добавил класс btn
- делегировано с помощью класса btn
- вместо createElement использовался шаблон
// buttons of type="button" do not need e.preventDefault();
window.addEventListener("load", function() {
const list1 = document.getElementById("list1")
const list2 = document.getElementById("list2")
document.getElementById("wrapper").addEventListener("click", function(e) {
const tgt = e.target;
if (!tgt.classList.contains("btn")) return; // not a button
const li = tgt.closest("li");
if (tgt.classList.contains("move")) {
tgt.remove();
list2.append(li);
} else if (tgt.classList.contains("edit")) {
const span = li.querySelector('span');
const edit = prompt("Edit to-do node:", span.textContent); // allow modification of existing text
span.textContent = edit;
} else if (tgt.classList.contains("delete")) {
li.remove();
}
});
document.getElementById("add-button").addEventListener("click", function() {
let input = document.getElementById("input").value;
let li = document.getElementById("liTemplate").content.cloneNode(true)
if (input) {
li.querySelector("span").textContent = input;
list1.appendChild(li);
}
})
})
<title>emil avara amp;nbsp;amp;mdash;amp;nbsp; inlämningsuppgift 2</title>
<h1 class="center">BIG BOI TO DO LIST APPLICATION</h1>
<div id="input-wrapper">
<input type="text" id="input" placeholder="Add a to-do" />
<button type="button" id="add-button">Add</button>
</div>
<div id="wrapper">
<div id="list1-div">
<h1>TO-DO</h1>
<ul id="list1">
</ul>
</div>
<div id="list2-div">
<h1>DONE</h1>
<ul id="list2">
</ul>
</div>
</div>
<template id="liTemplate">
<li>
<span class="node"></span>
<button type="button" class="move btn">Done</button>
<button type="button" class="edit btn">Edit</button>
<button type="button" class="delete btn">Delete</button>
</li>
</template>
Ответ №2:
Вы не должны использовать button.closest('span')
для выбора span
элемента. closest()
обходит предков элемента, а не его братьев и сестер. В вашем текущем коде элементы edit button и span находятся в пределах одного родительского узла, поэтому результат closest()
всегда должен быть нулевым.
Ответ №3:
Потому что ближайший не смог найти селектор диапазона. Причина в том, что .closest() находит только родительские селекторы. Если button.closest(‘ul’) или button.closest(‘li’) получат элемент. Вы должны использовать другой метод для выбора диапазона.
Ответ №4:
пришло время переделать этот код mplungjan ответил.
Но поскольку я внес много улучшений…
const DomParser = new DOMParser()
, eInput = document.getElementById('input')
, list_s = document.querySelector('div#wrapper')
, todoList = document.querySelector('ul#list1')
, doneList = document.querySelector('ul#list2')
;
function addToDo()
{
let newLI = `
<li>
<span class="node">${eInput.value} </span>
<button class="move" data-op="done" > Done </button>
<button class="edit" data-op="edit" > Edit </button>
<button class="delete" data-op="delete" > Delete </button>
</li>`;
todoList.appendChild( (DomParser.parseFromString( newLI, 'text/html')).body.firstChild );
}
list_s.onclick = e => // use event delegation for All buttons
{
if (!e.target.matches('button[data-op]')) return // reject others click on area
let li = e.target.closest('li')
switch (e.target.dataset.op)
{
case 'done':
e.target.remove()
doneList.append(li);
break;
case 'edit':
let span = li.querySelector('span')
, txt = prompt('Edit to-do item:', span.textContent)
;
if (txt != null) // cancel
span.textContent = txt
break;
case 'delete':
li.remove()
break;
}
}
div#wrapper span
{
display : inline-block;
width : 12em;
border-bottom : 1px solid lightgray;
}
<h1 class="center">BIG BOI TO DO LIST APPLICATION</h1>
<div id="input-wrapper">
<input type="text" id="input" placeholder="Add a to-do"></input>
<button id="add-button" onclick="addToDo()">Add</button>
</div>
<div id="wrapper">
<div id="list1-div">
<h1>TO-DO</h1>
<ul id="list1">
</ul>
</div>
<div id="list2-div">
<h1>DONE</h1>
<ul id="list2">
</ul>
</div>
</div>
признайте, что это все еще более читабельно! 😉
Комментарии:
1. Хм, возможно, над головой OP… Мне не нравится ваш переключатель. Он основан на том, что каждый элемент имеет только одно имя класса. Я предпочитаю if:
if (tgt.classList.contains("edit")) ...; else if (tgt.classList.contains("delete"))
— еще проще читать без разрывов и падежей2. И ваш код DOMParser действительно трудно читать по сравнению с innerHTML = liString
3. @mplungjan шаблон / клонирование — неплохая идея, я использовал эту технику раньше, но у меня получилось слишком много элементов (для разных вещей), и я предпочитаю, чтобы коды вставок были видны непосредственно в кодах JS
4. @mplungjan это просто шутка, и я поддержал ваш пост 😉 (и я также рад, что вдохновил вас, я полагаю, это хорошо для моей кармы?)