Chrome中contenteditable容器内不可编辑元素的Backspace异常问题
Chrome中contenteditable内不可编辑元素批量删除问题解决
问题场景
在Chrome浏览器中,当给contenteditable="true"的div添加多个contenteditable="false"且user-select: none的span元素时,会出现异常行为:
- 多个span处于同一行时,将光标移至行尾按下Backspace会删除div内全部内容;
- 若将span分至不同行,Backspace仅删除最后一个不可编辑元素,符合预期。
手动给span换行可临时解决问题,但由于需动态添加不可编辑元素,该方法不适用,需针对Chrome适配解决。
代码示例
简化复现代码
<div contenteditable="true"> some random text <span contenteditable="false" style="user-select: none">1</span><span contenteditable="false" style="user-select: none">2</span> </div>
动态添加元素完整示例
HTML
<!DOCTYPE html> <html> <head> <title>Parcel Sandbox</title> <meta charset="UTF-8" /> </head> <body> <div id="app"> <div spellcheck="false" contenteditable="true" class="input" id="snafu"> <span>添加至少2个不可编辑标签后,把光标移到行尾按Backspace测试</span> </div> <button id="button" class="button">添加标签</button> </div> <script src="src/index.js"></script> </body> </html>
CSS
body { font-family: sans-serif; } .input { color: green; box-shadow: 2px 2px 10px mediumaquamarine; padding: 8px; border-radius: 8px; margin-bottom: 24px; } .xml-tag { display: inline-flex; justify-content: center; align-items: center; width: 16px; height: 16px; margin-inline: 2px; user-select: none; border-radius: 50%; background: lime; font-size: 10px; white-space: pre; } .button { background: transparent; box-shadow: 3px 3px 5px limegreen; border: none; border-radius: 8px; padding: 8px; } .button:hover { transform: scale(1.1); cursor: pointer; }
JavaScript
const input = document.getElementById("snafu"); const button = document.getElementById("button"); const onClick = () => { const tag = document.createElement("span"); tag.setAttribute("contenteditable", false); tag.classList.add("xml-tag"); tag.innerHTML = "t"; input.append(tag); }; button.addEventListener("click", onClick);
解决方案
方案一:插入零宽空格分隔节点
在每个动态添加的span后插入一个零宽空格(\u200B),视觉上无影响,但可让Chrome将每个不可编辑span识别为独立节点,避免批量删除。修改添加元素的代码:
const onClick = () => { const tag = document.createElement("span"); tag.setAttribute("contenteditable", false); tag.classList.add("xml-tag"); tag.innerHTML = "t"; input.append(tag); // 插入零宽空格用于分隔节点 input.append(document.createTextNode("\u200B")); };
方案二:手动监听Backspace事件控制删除行为
通过监听keydown事件,判断光标位置后手动删除最后一个不可编辑元素,完全控制删除逻辑:
input.addEventListener('keydown', (e) => { if (e.key !== 'Backspace') return; const selection = window.getSelection(); const range = selection.getRangeAt(0); // 确认光标处于内容末尾 const isAtEnd = range.startContainer === input && range.startOffset === input.childNodes.length; if (!isAtEnd) return; // 从后往前查找第一个不可编辑的span for (let i = input.childNodes.length - 1; i >= 0; i--) { const node = input.childNodes[i]; if (node.tagName === 'SPAN' && node.contentEditable === 'false') { e.preventDefault(); // 阻止浏览器默认的异常删除行为 input.removeChild(node); // 若使用了方案一,同时删除前面的零宽空格 if (i > 0 && input.childNodes[i-1].nodeType === Node.TEXT_NODE && input.childNodes[i-1].textContent === '\u200B') { input.removeChild(input.childNodes[i-1]); } break; } } });
效果说明
- 方案一实现简单,零宽空格不会影响页面布局,能快速解决Chrome的批量删除问题;
- 方案二更灵活,适合需要自定义删除逻辑的复杂场景,完全规避浏览器的异常行为。
内容的提问来源于stack exchange,提问作者Victor Zhuravlev
相关产品推荐
相关产品推荐

