我有<ol contenteditable=true>元素,我想添加一个eventListener,以便在这些列表的任何<li>进行任何更改之前,将原始textContent(即任何更改之前的文本)推送到var alterations = []

我尝试使用addEventListener("beforeinput", ...,如下所示:

window.alterations = [];

window.onload = () => {
  document.getElementById('main').addEventListener("beforeinput", () => {
    alert('event listened!');
    window.alterations.push(window.getSelection().anchorNode.parentElement.textContent);

    console.log(window.alterations);
  });
}
    
<body>
<section id="main">
<ol contenteditable="true">
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
  <li>Fourth</li>
</ol>
<ol contenteditable="true">
  <li>First</li>
  <li>Second</li>
  <li>Third</li>
  <li>Fourth</li>
</ol>
</section>
</body>


但是beforeinput似乎不适用于contenteditable元素(如以上代码所示,回调函数未执行)。仅input事件用作addEventListener的参数,但为时已晚,因为已经输入了一个字母或从原始文本中删除了一个字母。

问题:如何获取要通过contenteditable更改的元素的原始文本?例如,如果我输入“First” <li>诸如“not First”之类的其他名称,如何将textContent的原始<li>(===仅“First”)推入alterations数组?

编辑:我认为这是浏览器问题。 beforeinput在基于Chromium的浏览器中有效,但在基于Firefox的浏览器(如Firefox和PaleMoon)中则无效。我的快速解决方案是使用keydown而不是beforeinput。该代码可在所有浏览器中使用:
 for (let x of document.querySelectorAll('[contenteditable="true"]')) {
   x.addEventListener('keydown',() => {
     let originalText = window.getSelection().anchorNode.textContent;
     console.log(originalText);
   });

但这有一个不幸的问题,每次按下任何键(例如Ctrl + Shift或Alt)都会触发。我尝试使用keypress而不是keydown,但是基于Chromium的浏览器并未触发删除操作,并且按下的任何“上/下/左/右/上移”键也会触发该代码。我想我可以使用charcode一对一地处理这些异常,但是除了使用keydown之外,没有别的方法吗?

最佳答案

好的,这应该可以工作:

window.alterations = [];

window.onload = () => {
  document.getElementById('main').addEventListener("keydown", (event) => {
    //0 == delete in PaleMoon, 46 == delete in other browsers

    /* ALT is needed for some characters, like § and º, but to exclude ALT, META, etc.:
    if (event.altKey || event.metaKey) {return;} */

    if (event.key.length === 1 || ["Enter", "Delete", "Backspace"].includes(event.key)) {
      //when Ctrl is pressed, trigger only if Ctrl+V or Ctrl+X and ALT is not pressed:
      if (event.key.toLowerCase() !== 'v' && event.key.toLowerCase() !== 'x' && event.ctrlKey && !event.altKey) {return;}

      alert('event listened!');
      window.alterations.push(window.getSelection().anchorNode.parentElement.textContent);

      console.log(window.alterations);
    }
  });
}
<body>
  <section id="main">
    <ol contenteditable="true">
      <li>First</li>
      <li>Second</li>
      <li>Third</li>
      <li>Fourth</li>
    </ol>
    <ol contenteditable="true">
      <li>First</li>
      <li>Second</li>
      <li>Third</li>
      <li>Fourth</li>
    </ol>
  </section>
</body>


从兼容性的 Angular 来看,这似乎是最好的,因为我开始认为每种浏览器对beforeinput(可能对没有处理程序的keydown)都有不同的响应。我下载了基于Chromium的浏览器进行测试,结果似乎与Chrome不同。

编辑:我将event.which替换为event.key,因为前者已弃用,并且在不同的浏览器中无法可靠地工作。

仍然存在的问题:
  • 如果用户反复键入~~~´´´´等,则不会
    处理。
  • 如果用户在<li>聚焦时复制文本(Ctrl + C)或使用其他类似的快捷方式,则事件将不愿被触发,因为它监听了c。 [由于@trysis评论而得到修复]
  • 09-20 22:26