contenteditable元素移动选中内容后绑定事件监听器丢失问题
问题原因
- 浏览器对
contenteditable容器内的选中拖拽移动操作,底层执行逻辑是:先将选中的DOM片段序列化为纯HTML字符串,随后删除原位置的对应DOM节点,最后在目标位置将这段HTML字符串重新解析为新的DOM节点插入。所有直接绑定在原DOM节点上的事件监听器(包括通过addEventListener绑定的动态事件、以及部分浏览器场景下的行内onxxx事件属性)都会随着原节点销毁而失效,新生成的DOM节点不会自动继承原节点的事件绑定。 - 你示例中使用的行内事件属性失效,是因为部分浏览器处理富文本内容的剪切/拖拽操作时,会默认过滤掉DOM节点上的事件类属性,避免恶意脚本注入,重新生成的span节点已经不存在
onmouseover、onmouseout属性,自然无法触发对应逻辑。
解决方案
最稳定的解决方式是使用事件委托,把事件绑定在固定不变的父级contenteditable容器上,通过事件冒泡机制匹配触发事件的目标元素即可:
<div contenteditable="true" id="editor"> If you move by selection this sentence "LOREM <span id="1" class="element">WORD 1</span>FINIBUS", you lose the listener.<br> Other example with this sentence "NNULLAM <span id="2" class="element">WORD 2</span>METUS". </div> <script> const editor = document.getElementById('editor') // 委托绑定mouseover事件 editor.addEventListener('mouseover', e => { if (e.target.classList.contains('element')) { console.log(`mouseover word ${e.target.id}`) e.target.style.backgroundColor = 'yellow' } }) // 委托绑定mouseout事件 editor.addEventListener('mouseout', e => { if (e.target.classList.contains('element')) { console.log(`mouseout word ${e.target.id}`) e.target.style.backgroundColor = 'red' } }) </script>
- 这种方式不受内部DOM节点增删、移动的影响,只要父级容器不被销毁,事件就能正常触发。
- 如果需要保留行内事件的写法,可以监听
contenteditable容器的input或者drop事件,每次内容变动后重新给所有span绑定事件即可,但性能比事件委托差,不推荐。
内容的提问来源于stack exchange,提问作者jean
相关产品推荐
相关产品推荐

