Как редактировать содержимое span с помощью функции кнопки onclick

#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 это просто шутка, и я поддержал ваш пост 😉 (и я также рад, что вдохновил вас, я полагаю, это хорошо для моей кармы?)