当我用 <span> 标签替换字符串时,MutationObserver 创建无限循环

Cam*_*lle 2 javascript dom contenteditable dom-events mutation-observers

我想用 html contenteditable div 替换所有出现的情况。我需要用标签替换该事件,以使用 css 对其进行自定义。但是当我尝试用跨度替换时,MutationObserver 会生成无限循环......

document.designMode = "on";

var text = document.getElementById('text');

let observer = new MutationObserver(mutations =>
  mutations.forEach(mutation => {
    console.log(text.textContent);
    // let a = "a";
    // a.style.color = "red";
    text.textContent = text.textContent.replace('/', '<span>/</span>');
    observer.disconnect(); 
    const range = document.createRange();
    const textNode = text.firstChild;
    range.setStart(textNode, text.textContent.length);
    range.setEnd(textNode, text.textContent.length);
    const sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
    // observer.disconnect(); 
  })
);

observer.observe(text, {
  childList: true,
  characterData: true,
  subtree: true,
  
});

Run Code Online (Sandbox Code Playgroud)
<p id="text" contenteditable="true">I'm a text will be change</p>
Run Code Online (Sandbox Code Playgroud)

tra*_*r53 5

无限循环是由于在没有先关闭观察器的情况下替换键入的/字符而引起的<span>/</span>- 每次替换都会再次触发观察器。

这个问题的解决方案很简单:在替换之前关闭观察器,然后再打开它:

"use strict";
document.designMode = "on";

var text = document.getElementById('text');

let observer = new MutationObserver(mutations =>
  mutations.forEach(mutation => {
    console.log(text.textContent);
    observer.disconnect(); // turn observer off;
    text.textContent = text.textContent.replace('/', '<span>/</span>');

    const range = document.createRange();
    const textNode = text.firstChild;
    range.setStart(textNode, text.textContent.length);
    range.setEnd(textNode, text.textContent.length);
    const sel = window.getSelection();
    sel.removeAllRanges();
    sel.addRange(range);
    observe(); // turn back on 
  })
);

const observe = ()=> {

  observer.observe(text, {
    childList: true,
    characterData: true,
    subtree: true,
   
  });
};
observe();
Run Code Online (Sandbox Code Playgroud)
span { background-color: yellow;}
Run Code Online (Sandbox Code Playgroud)
<p id="text" contenteditable="true">I'm a text will be change</p>
Run Code Online (Sandbox Code Playgroud)

但这只会突出一个潜在的新问题:代码替换textContent为 HTML,因为它位于文本节点内部,所以以源形式显示,并且由于<span>/</span>位于文本节点内部,CSS 以黄色突出显示 SPAN 元素不起作用(它是不是 SPAN 元素)。

假设 span 元素应该呈现为 HTML,那么现在的设计问题就变成了如何用 DOM 中的 SPAN 元素(而不是文本节点中的)替换新键入的正斜杠。突变记录的属性mutation.target可能是一个很好的起点,可以将其分成三个节点:一个用于 之前文本的文本节点/,一个用于 之前文本的元素节点,<span>/</span>以及一个用于后续文本(/如果有)的最终文本节点,然后将光标重新定位在跨度节点之后如果需要。