Я работаю над простым проектом JavaScript, в котором составляю список дел. Цель состоит в том, чтобы позволить пользователям добавлять новые элементы задач и удалять их, щелкнув значок корзины. Однако у меня возникли проблемы с тем, чтобы функция удаления работала должным образом.
Что я пробовал:
Добавление прослушивателя событий: я прикрепил прослушиватель событий непосредственно к значку корзины (<i>
) внутри функции addNewTodo()
. Предполагается, что этот прослушиватель сработает при нажатии на значок, а затем удалит соответствующий элемент <li>
из DOM.
Регистрация события клика: я добавил оператор console.info в прослушиватель событий, чтобы убедиться, что событие клика срабатывает. Это сообщение журнала не работает, поскольку событие щелчка не срабатывает.
Удаление элемента задачи. В том же прослушивателе событий я вызвал метод remove()
родительского элемента <li>
(newTodoLi()
), чтобы удалить элемент задачи из списка. Я ожидал, что щелчок по значку корзины приведет к удалению соответствующего элемента задачи.
Чего я ожидал:
Я ожидал, что каждый раз, когда я нажимаю на значок корзины рядом с элементом задачи, этот элемент будет немедленно удален из списка без каких-либо проблем. Поскольку прослушиватель событий, казалось, сработал правильно, я ожидал, что вызов remove()
родительского элемента <li>
надежно удалит элемент.
let inputElem = document.querySelector("input");
let addTodoForm = document.querySelector(".add");
let todoUlElem = document.querySelector(".todos");
function addNewTodo(newTodoValue) {
// Create the new li element
let newTodoLi = document.createElement("li");
newTodoLi.className =
"list-group-item d-flex justify-content-between align-items-center";
// Create the span for the todo title
let newTodoTitleSpan = document.createElement("span");
newTodoTitleSpan.innerHTML = newTodoValue;
// Create the trash icon element
let newTodoTrash = document.createElement("i");
newTodoTrash.className = "fa fa-trash delete";
// Append the title span and trash icon to the new li element
newTodoLi.append(newTodoTitleSpan, newTodoTrash);
// Append the new li element to the ul element
todoUlElem.append(newTodoLi);
// Add an event listener to the trash icon
newTodoTrash.addEventListener("click", (e) => {
console.info("clicked delete icon");
// You can add more functionality here, like removing the todo item
// newTodoLi.remove();
});
}
// Prevent the default form submission
addTodoForm.addEventListener("submit", (e) => {
e.preventDefault();
});
// Add a new todo item when Enter key is pressed
inputElem.addEventListener("keydown", (e) => {
let newTodoValue = e.target.value.trim();
if (e.keyCode === 13) {
inputElem.value = "";
if (newTodoValue) {
addNewTodo(newTodoValue);
}
}
});
body {
background: #353f5b;
}
.container {
max-width: 400px;
margin-top: 90px;
}
.search i {
position: absolute;
}
.search {
position: relative;
}
.icon {
padding: 10px;
left: 0;
}
.input-field {
text-align: center;
}
input[type = "text"],
input[type = "text"]:focus {
color: #fff;
border: none;
background: rgba(0, 0, 0, 0.3);
max-width: 400px;
}
.todos li {
background: #423a6f;
}
.delete {
cursor: pointer;
}
.filtered {
display: none !important;
}
span {
color: #fff;
}
<link href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin = "anonymous">
<link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity = "sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw= = " crossorigin = "anonymous" referrerpolicy = "no-referrer" />
<!-- originally=> <script src = "FontAwesome5/all.min.js"></script> -->
<script src = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/js/all.min.js" integrity = "sha512-LW9+kKj/cBGHqnI4ok24dUWNR/e8sUD8RLzak1mNw5Ja2JYCmTXJTF5VpgFSw+VoBfpMvPScCo2DnKTIUjrzYw= = " crossorigin = "anonymous" referrerpolicy = "no-referrer"></script>
<div class = "container">
<ul class = "list-group todos mx-auto text-light"></ul>
<form class = "add text-center my-4" autocomplete = "off">
<label class = "text-light"> Add New Todo... </label>
<input type = "text" class = "form-control m-auto" name = "add" />
</form>
</div>
Я принес все коды, чтобы облегчить вам задачу.
удалить <script src = "FontAwesome5/all.min.js"></script>
оптимизатор JS FontAwesome5
Помимо того, что писали другие: семантически плохая идея прикреплять событие щелчка к элементу значка. Для обеспечения доступности события кликов должны быть прикреплены к элементам, которые обычно их принимают, например к ссылкам (<a>
) или, в данном случае, к button
, и помещать значок внутрь этой кнопки.
Ваша проблема в том, что кнопка удаления <i class = "fa fa-trash delete">
заменяется элементом <svg>
в DOM. Исходный обработчик больше не применим.
Вместо этого вам следует прикрепить прослушиватель делегирования к <ul>
и проверить, нажали ли вы внутри <svg>
.
todoUlElem.addEventListener("click", (e) => {
const svg = e.target.closest(".delete"); // Find the SVG
if (svg) {
e.target.closest('li').remove();
}
});
let inputElem = document.querySelector("input");
let addTodoForm = document.querySelector(".add");
let todoUlElem = document.querySelector(".todos");
function addNewTodo(newTodoValue) {
// Create the new li element
let newTodoLi = document.createElement("li");
newTodoLi.className =
"list-group-item d-flex justify-content-between align-items-center";
// Create the span for the todo title
let newTodoTitleSpan = document.createElement("span");
newTodoTitleSpan.innerHTML = newTodoValue;
// Create the trash icon element
let newTodoTrash = document.createElement("i");
newTodoTrash.className = "fa fa-trash delete";
// Append the title span and trash icon to the new li element
newTodoLi.append(newTodoTitleSpan, newTodoTrash);
// Append the new li element to the ul element
todoUlElem.append(newTodoLi);
}
// Prevent the default form submission
addTodoForm.addEventListener("submit", (e) => {
e.preventDefault();
});
// Add a new todo item when Enter key is pressed
inputElem.addEventListener("keydown", (e) => {
let newTodoValue = e.target.value.trim();
if (e.keyCode === 13) {
inputElem.value = "";
if (newTodoValue) {
addNewTodo(newTodoValue);
}
}
});
todoUlElem.addEventListener("click", (e) => {
const svg = e.target.closest(".delete"); // Find the SVG
if (svg) {
e.target.closest('li').remove();
}
});
body {
background: #353f5b !important;
}
.container {
max-width: 400px;
margin-top: 90px;
}
.search i {
position: absolute;
}
.search {
position: relative;
}
.icon {
padding: 10px;
left: 0;
}
.input-field {
text-align: center;
}
input[type = "text"],
input[type = "text"]:focus {
color: #fff;
border: none;
background: rgba(0, 0, 0, 0.3);
max-width: 400px;
}
.todos li {
background: #423a6f;
}
.delete {
cursor: pointer;
}
.filtered {
display: none !important;
}
span {
color: #fff;
}
<link href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin = "anonymous">
<link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity = "sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw= = " crossorigin = "anonymous" referrerpolicy = "no-referrer" />
<!-- originally=> <script src = "FontAwesome5/all.min.js"></script> -->
<script src = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/js/all.min.js" integrity = "sha512-LW9+kKj/cBGHqnI4ok24dUWNR/e8sUD8RLzak1mNw5Ja2JYCmTXJTF5VpgFSw+VoBfpMvPScCo2DnKTIUjrzYw= = " crossorigin = "anonymous" referrerpolicy = "no-referrer"></script>
<div class = "container">
<ul class = "list-group todos mx-auto text-light"></ul>
<form class = "add text-center my-4" autocomplete = "off">
<label class = "text-light"> Add New Todo... </label>
<input type = "text" class = "form-control m-auto" name = "add" />
</form>
</div>
Вы можете добавить обработчик click
к элементу списка дел, и делегат нажмет кнопку удаления.
// Where `ref` is the todo list component
ref.addEventListener('click', delegateClick);
// Where `.todo-remove` is a button for removing the list item
function delegateClick(event) {
if (event.target.matches('.todo-remove')) {
event.target.closest('li').remove();
}
}
См. пример ниже:
const initialTasks = ['Take out the trash', 'Wash car', 'Clean the house'];
TodoList(document.querySelector('#my-todo'));
function TodoList(ref) {
ref.classList.add('todo');
ref.innerHTML = `
<form name = "todo-form">
<input name = "task" type = "text" placeholder = "Enter a task" autocomplete = "off" required>
<button type = "submit">Add</button>
</form>
<ul class = "todo-list"></ul>
`;
const form = ref.querySelector('[name = "todo-form"]');
form.addEventListener('submit', handleSubmit);
ref.addEventListener('click', delegateClick);
initialTasks.forEach(task => addItem(form, task));
function handleSubmit(event) {
event.preventDefault();
addItem(event.target, event.target.elements.task.value.trim());
}
function addItem(form, value) {
if (!value) return;
const todo = form.closest('.todo');
const items = todo.querySelector('.todo-list');
items.insertAdjacentHTML('beforeend', `
<li data-id = "${'todo-' + crypto.randomUUID()}">
<span>${value}</span>
<button type = "button" class = "todo-remove" title = "Remove"></button>
</li>
`);
form.reset();
}
function delegateClick(event) {
if (event.target.matches('.todo-remove')) {
event.target.closest('li').remove();
}
}
};
html, body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
body {
display: flex;
align-items: flex-start;
justify-content: center;
padding: 1rem;
}
.todo {
display: flex;
flex-direction: column;
gap: 1rem;
border: thin solid grey;
padding: 0.5rem;
}
.todo form {
display: flex;
gap: 1rem;
}
.todo input[name = "task"] {
border: none;
border-bottom: thin solid grey;
}
.todo-list {
display: flex;
flex-direction: column;
gap: 0.25rem;
margin: 0;
padding: 0;
}
.todo-list li {
display: flex;
margin: 0;
padding: 0;
}
.todo-list span {
flex: 1;
}
.todo-remove {
background: red;
border: black;
color: white;
font-weight: bold;
}
.todo-remove:hover {
background: #F77;
cursor: pointer;
}
.todo-remove:after {
content: "x"
}
<div id = "my-todo"></div>
Прежде всего -> удалить<script src = "FontAwesome5/all.min.js"></script>
потому что оно заменяет ваше:<i class = "fa fa-trash delete"></i>
элементом <svg>
.
Также вам следует знать некоторые возможности JS:
HTML-шаблоны: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template
Делегирование события = (Event_bubbling + метод .matches()
)
--> Что такое делегирование мероприятий и как оно работает?
по вашему коду:
const
inputElem = document.querySelector('input')
, addTodoForm = document.querySelector('.add')
, todoList = document.querySelector('.todos')
, todoElmT = document.querySelector("#todo-list-elm-template")
;
addTodoForm.addEventListener("submit", (e) => { e.preventDefault(); })
;
inputElem.addEventListener("keydown", (e) =>
{ // Add a new todo item when Enter key is pressed
if (e.key === 'Enter' )
{
let newTodoValue = inputElem.value.trim();
inputElem.value = '';
if (newTodoValue.length > 0 )
addNewTodo( newTodoValue );
}
});
todoList.addEventListener("click", (e) => // use event delegation for this
{
if (!e.target.matches('.delete') ) return; // ignore click event
todoList.removeChild(e.target.closest('li'));
});
function addNewTodo( newTodoValue ) // HTML template is usefull
{
const todoElm = todoElmT.content.cloneNode(true);
todoElm.querySelector('span')
.textContent = newTodoValue;
todoList.append(todoElm);
}
body {
background: #353f5b !important;
}
.container {
max-width : 400px;
margin-top : 90px;
}
.search i {
position : absolute;
}
.search {
position : relative;
}
.icon {
padding : 10px;
left : 0;
}
.input-field {
text-align: center;
}
input[type = "text"],
input[type = "text"]:focus {
color : #fff;
border : none;
background : rgba(0, 0, 0, 0.3);
max-width : 400px;
}
.todos li {
background : #423a6f;
}
.delete {
cursor : pointer;
color : orange
}
.filtered {
display : none !important;
}
span {
color : #fff;
}
<link href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel = "stylesheet" integrity = "sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin = "anonymous">
<link rel = "stylesheet" href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.9.0/css/all.min.css" integrity = "sha512-q3eWabyZPc1XTCmF+8/LuE1ozpg5xxn7iO89yfSOd5/oKvyqLngoNGsx8jq92Y8eXJ/IRxQbEC+FGSYxtk2oiw= = " crossorigin = "anonymous" referrerpolicy = "no-referrer" />
<div class = "container">
<ul class = "list-group todos mx-auto text-light"></ul>
<form class = "add text-center my-4" autocomplete = "off">
<label class = "text-light"> Add New Todo... </label>
<input type = "text" class = "form-control m-auto" name = "add" />
</form>
</div>
<template id = "todo-list-elm-template">
<li class = "list-group-item d-flex justify-content-between align-items-center">
<span></span>
<i class = "fa fa-trash delete"></i>
</li>
</template>
Пожалуйста, покажите код, а не описывайте его. Мы не можем отладить код, который вы нам не показали.