微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Javascript:替换元素时事件属性丢失

如何解决Javascript:替换元素时事件属性丢失

我正在尝试使用 vanilla js 创建一个 todoList 应用程序。到目前为止,我已经完成了标记、设计以及添加其他功能,例如将提交的任务添加到 ui、删除任务、将任务标记为已完成。

现在我坚持添加编辑功能。(当然还有其他事情要做,比如验证、实现 localStorage、添加提醒、使其具有响应性等。)

要实现的目标:

  1. 用户应该能够点击编辑图标并且它应该更改为保存图标,
  2. 点击编辑图标后,用户应该可以实时编辑文本
  3. 最后点击保存图标后,文本应该不再可编辑,图标应该变回编辑图标

一些事情尝试过: 点击编辑图标,

  1. 为文本更改了 contentEditable=true,因此我们可以实时编辑它(任务文本)。
  2. editBtn 替换为 saveBtn(新创建的元素)

但是在替换元素后,我找不到恢复它的方法。当我尝试使用 eventTarget(我用来存储事件的目标属性的变量)访问它时,我什么也没得到。我也试过用 document.querySelector('.save') 抓取它,但这只适用于每个文档流。(我的意思是如果我们点击第二个按钮,在 dom 中第一个按钮被改变)

现在我想要将saveBtn替换回editBtn将contentEditable改回false或继承的功能

这是处理ui事件的函数

static taskEvents(eventTarget) {
        const targetClassName = eventTarget.classList 
        
        if(targetClassName.contains('complete')) {
            targetClassName.toggle('statusIcon');
            eventTarget.parentElement.nextElementSibling.classList.toggle('task');
        } 
        
        else if(targetClassName.contains('edit')) {
            // let textEditableStatus = eventTarget.parentElement.parentElement.prevIoUsElementSibling;
            // textEditableStatus.contentEditable=true;

            // const editBtn = eventTarget.parentElement

            // const saveBtn = document.createElement('a');
            // saveBtn.className = "btn";
            // saveBtn.id = "saveBtn";
            // saveBtn.innerHTML = '<i class="fas fa-save save"></i>';

            // editBtn.replaceWith(saveBtn)

        } 
        
        else if(targetClassName.contains('delete')) {
            eventTarget.parentElement.parentElement.parentElement.remove();
        }
    }

完整代码

class UI {
  // dummy data; for Now.
  static displayTasks() {
    const tasks = ['Take out trash','Do laundry','Visit part'];

    tasks.forEach(task => UI.addTask(task));
  }

  static addTask(task) {
    const tbody = document.querySelector('#tasks');

    const taskRow = document.createElement('tr');
    taskRow.className += 'task';
    taskRow.innerHTML = `
            <td><i class="far fa-check-circle complete"></i></td>
            <td>${task}</td>
            <td><a href="#" class="btn" id="editBtn"><i class="fas fa-edit edit"></i></a></td>
            <td><a href="#" class="btn" id="deleteBtn"><i class="fas fa-trash delete"></i></a></td>
        `;
    tbody.appendChild(taskRow);
    document.querySelector('#todoInput').value = '';
  }

  static taskEvents(eventTarget) {
    const targetClassName = eventTarget.classList

    if (targetClassName.contains('complete')) {
      targetClassName.toggle('statusIcon');
      eventTarget.parentElement.nextElementSibling.classList.toggle('task');
    } else if (targetClassName.contains('edit')) {
      // let textEditableStatus = eventTarget.parentElement.parentElement.prevIoUsElementSibling;
      // textEditableStatus.contentEditable=true;

      // const editBtn = eventTarget.parentElement

      // const saveBtn = document.createElement('a');
      // saveBtn.className = "btn";
      // saveBtn.id = "saveBtn";
      // saveBtn.innerHTML = '<i class="fas fa-save save"></i>';

      // editBtn.replaceWith(saveBtn)

    } else if (targetClassName.contains('delete')) {
      eventTarget.parentElement.parentElement.parentElement.remove();
    }
  }
}

// Ui events 
document.addEventListener('DOMContentLoaded',UI.displayTasks);

const tbody = document.querySelector('#tasks');
tbody.addEventListener('click',event => {
  UI.taskEvents(event.target);
})
.statusIcon {
  font-weight: bold;
  color: rgb(48,158,81);
}

td.task {
  opacity: .6;
  text-decoration: line-through;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <Meta charset="UTF-8">
  <Meta http-equiv="X-UA-Compatible" content="IE=edge">
  <Meta name="viewport" content="width=device-width,initial-scale=1.0">
  <title>Todo List App</title>
  <!-- Custom css -->
  <link rel="stylesheet" href="style.css">
  <!-- fontawesome script-->
  <script src="https://kit.fontawesome.com/39350fd9df.js"></script>
</head>

<body>

  <div class="main-container">

    <div class="container">
      <div class="input-group">
        <input type="text" id="todoInput" placeholder="Enter new task...">
        <a href="#" class="btn addBtn"><i class="fas fa-plus"></i></a>
      </div>
      <table class="taskLister">
        <thead>
          <tr>
            <th>Status</th>
            <th>Task</th>
            <th>Edit</th>
            <th>Delete</th>
          </tr>
        </thead>
        <tbody id="tasks"></tbody>
      </table>
    </div>
  </div>

  <!-- Custom script -->
  <script src="app.js"></script>
</body>

</html>

简短说明:我使用的是纯 JavaScript

这是小提琴:https://jsfiddle.net/Pixie_Dust/Lcnwu4ao/5/

解决方法

使用 innerHTML 重写元素内容会破坏其上定义的处理程序。如果使用event delegation,则不会出现此问题,因为委托处理程序使用实际元素来确定需要哪个操作。这是包含一个条目的表的 minimal reproducable example

Here's a rewritten version of your jsFiddle,这是一个(有些扩展的)stackblitz version of it

document.addEventListener("click",handle);

function handle(evt) {
  const origin = evt.target;
  if (origin.dataset.edit) {
    const entryEdit = origin.closest("tr").querySelector("td:nth-child(2)");
    entryEdit.contentEditable = true;
    return entryEdit.focus();
  }

  if (origin.dataset.save) {
    const entry = origin.closest("tr");
    const value = entry.querySelector("td:nth-child(2)")
    if (value.contentEditable === "true") {
      value.contentEditable = false;
      return entry.querySelector("td:nth-child(5)").textContent = "saved!";
    };

    return entry.querySelector("td:nth-child(5)").textContent = "nothing to do";
  }
  
  if (origin.dataset.setstatus) {
    const row = origin.closest("tr");
    const nwStatus = origin.dataset.setstatus === "Todo" ? "Done" : "Todo";
    row.dataset.status = nwStatus;
    origin.dataset.setstatus = nwStatus;
    row.querySelectorAll("td > button")
      .forEach(btn => 
        btn[nwStatus === "Done" 
          ? 'setAttribute' 
          : 'removeAttribute']("disabled",true));
    return row.querySelector("td:nth-child(5)").textContent = "";
  }
}
body {
  margin: 2rem;
  font: 12px/15px normal verdana,arial;
}
th {
  background: black;
  color: white;
  text-align: left;
  padding: 2px;
}
td {
  padding: 2px;
}
th:nth-child(5) {
  min-width: 75px;
}

th:nth-child(2) {
  min-width: 200px;
}

td[data-setstatus]:after {
  content: attr(data-setstatus);
}
tr[data-status='Done'] td:nth-child(2) {
  text-decoration: line-through;
}
<table>
  <thead>
    <tr>
      <th>Status</th>
      <th>Task</th>
      <th>edit</th>
      <th>save</th>
      <th>result</th>
    </tr>
  </thead>
  <tbody>
    <tr data-status="Todo">
      <td data-setstatus="Todo"></td>
      <td>Hi,I am a task</td>
      <td><button data-edit="1">edit</button></td>
      <td><button data-save="1">save</button></td>
      <td></td>
    </tr>
  </tbody>
</table>

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。