替代document.execCommand('removeFormat')实现选中文本格式移除的方案咨询
实现自定义选中文本格式移除功能(替代废弃的document.execCommand('removeFormat'))
核心算法思路
要实现移除选中文本格式的功能,本质是剥离选中范围内所有格式化标签、保留纯文本内容,同时处理部分选中的节点(比如仅选中某文本节点的一部分),最终将纯文本合并后替换原选中区域,并清理残留的空标签。
分步实现细节
1. 获取选区与选中范围
首先通过window.getSelection()获取当前选区,再通过getRangeAt(0)拿到具体的选中范围对象:
const selection = window.getSelection(); if (selection.rangeCount === 0) return; // 无选中内容则退出 const range = selection.getRangeAt(0);
2. 拆分部分选中的文本节点
如果选中范围的起始或结束位置落在某个文本节点的中间(比如例子中选中了<p>里的"1234"前两个字符),需要用Text.splitText()将该文本节点拆分为两个独立节点,确保选中范围能精确覆盖完整的节点片段:
function splitTextNodesIfNeeded(range) { // 处理起始节点 if (range.startContainer.nodeType === Node.TEXT_NODE && range.startOffset > 0) { const splitNode = range.startContainer.splitText(range.startOffset); range.setStart(splitNode, 0); } // 处理结束节点 if (range.endContainer.nodeType === Node.TEXT_NODE && range.endOffset < range.endContainer.length) { range.endContainer.splitText(range.endOffset); } } splitTextNodesIfNeeded(range);
3. 提取选中范围内的纯文本并清理标签
遍历选中范围内的所有节点,收集纯文本内容,同时移除所有包含在选中范围内的格式化元素:
function extractTextAndRemoveFormatting(range) { const fragment = range.extractContents(); const textContent = fragment.textContent.trim(); return textContent; } const plainText = extractTextAndRemoveFormatting(range);
这里range.extractContents()会把选中范围内的内容从DOM中移除,返回一个文档片段,我们直接取它的纯文本内容即可。
4. 插入纯文本并清理空元素
将提取的纯文本作为新的文本节点插入到原选中位置,然后递归清理所有空的父元素(比如原<h1>、<span>、<b>在内容被提取后会变成空标签,需要删除):
// 插入纯文本节点 const textNode = document.createTextNode(plainText); range.insertNode(textNode); // 清理空元素 function cleanupEmptyElements(element) { const parent = element.parentNode; if (!parent || parent === document.body) return; if (!parent.hasChildNodes() || (parent.textContent.trim() === '' && parent.children.length === 0)) { const grandparent = parent.parentNode; grandparent.removeChild(parent); cleanupEmptyElements(grandparent); } } cleanupEmptyElements(textNode);
完整示例代码
结合你的测试HTML,完整的实现代码如下:
function removeFormatting() { const selection = window.getSelection(); if (selection.rangeCount === 0) return; const range = selection.getRangeAt(0); // 拆分部分选中的文本节点 function splitTextNodesIfNeeded(range) { if (range.startContainer.nodeType === Node.TEXT_NODE && range.startOffset > 0) { const splitNode = range.startContainer.splitText(range.startOffset); range.setStart(splitNode, 0); } if (range.endContainer.nodeType === Node.TEXT_NODE && range.endOffset < range.endContainer.length) { range.endContainer.splitText(range.endOffset); } } // 提取纯文本并移除格式化标签 function extractTextAndRemoveFormatting(range) { const fragment = range.extractContents(); return fragment.textContent.trim(); } // 清理空元素 function cleanupEmptyElements(element) { const parent = element.parentNode; if (!parent || parent === document.body) return; if (!parent.hasChildNodes() || (parent.textContent.trim() === '' && parent.children.length === 0)) { const grandparent = parent.parentNode; grandparent.removeChild(parent); cleanupEmptyElements(grandparent); } } splitTextNodesIfNeeded(range); const plainText = extractTextAndRemoveFormatting(range); if (plainText) { const textNode = document.createTextNode(plainText); range.insertNode(textNode); cleanupEmptyElements(textNode); } // 清除选区 selection.removeAllRanges(); }
效果验证
针对你提供的测试HTML:
- 选中从
hello到12的文本后调用removeFormatting(),会将<h1>、<span>、<b>这些格式化标签移除,保留纯文本hello world bold text 12,同时<p>标签仅保留未被选中的34部分,最终DOM结构和你期望的一致。
内容的提问来源于stack exchange,提问作者dio
相关产品推荐
相关产品推荐

