如何检查选中文本范围与<span>标签是否相交?字体修改异常求助
解决选中文本字体修改及选区与span交集检查问题
一、实现已包裹文本的字体修改
原代码仅对未被<span>包裹的文本创建新标签,无法处理已存在的<span>元素。以下是改进后的代码,支持修改已被<span>包裹的选中文本字体:
let txt = document.getElementById('txt') let font = document.getElementsByClassName('form-select')[0]; const fontChange = () => { let selectedFont = font.options[font.selectedIndex].value; let selection = window.getSelection(); if (selection.rangeCount === 0) return; let range = selection.getRangeAt(0); let tempRange = range.cloneRange(); // 获取选区范围内所有带有font-family样式的span let targetSpans = tempRange.commonAncestorContainer.querySelectorAll('span[style*="font-family"]'); let hasMatchedSpan = false; targetSpans.forEach(span => { let spanRange = document.createRange(); spanRange.selectNode(span); // 检查当前span是否与选区有交集 if (range.intersectsNode(span)) { hasMatchedSpan = true; // 情况1:选区完全包含当前span,直接修改字体 if (range.compareBoundaryPoints(Range.START_TO_START, spanRange) <= 0 && range.compareBoundaryPoints(Range.END_TO_END, spanRange) >= 0) { span.style.fontFamily = selectedFont; } // 情况2:选区仅部分选中span,拆分span后修改选中部分的字体 else { // 拆分span的起始部分 if (range.startContainer === span || range.startContainer.parentNode === span) { let beforeSpan = span.cloneNode(false); beforeSpan.appendChild(span.splitText(range.startOffset)); span.parentNode.insertBefore(beforeSpan, span.nextSibling); } // 拆分span的结束部分 if (range.endContainer === span || range.endContainer.parentNode === span) { let afterSpan = span.cloneNode(false); afterSpan.appendChild(span.splitText(range.endOffset)); span.parentNode.insertBefore(afterSpan, span.nextSibling); } // 修改拆分后对应选中部分的span字体 span.style.fontFamily = selectedFont; } } }); // 情况3:选区无匹配的span,用新span包裹选中内容 if (!hasMatchedSpan) { let span = document.createElement('span'); span.style.fontFamily = selectedFont; span.appendChild(range.extractContents()); range.insertNode(span); } selection.removeAllRanges(); }
代码逻辑说明:
- 克隆选区范围,避免操作原范围影响后续判断
- 筛选出选区范围内所有带有
font-family样式的<span> - 对每个span判断是否与选区交集:
- 完全包含则直接修改该span的字体样式
- 部分包含则拆分原span为"未选中前部分"、"选中部分"、"未选中后部分",仅修改选中部分的字体
- 无匹配span时,沿用原逻辑创建新span包裹内容
二、检查选中文本范围与span标签的交集
可以通过以下两种方式判断选区范围与指定span是否存在交集:
1. 使用Range.intersectsNode()方法
这是最直接的API,返回布尔值表示范围是否与节点相交:
const checkSpanIntersection = (range, span) => { return range.intersectsNode(span); }
2. 手动比较边界点
通过Range.compareBoundaryPoints()方法比较选区范围与span的范围边界:
const checkSpanIntersection = (range, span) => { let spanRange = document.createRange(); spanRange.selectNode(span); // 判断选区是否与span范围有重叠 const startBeforeEnd = range.compareBoundaryPoints(Range.START_TO_END, spanRange) < 0; const endAfterStart = range.compareBoundaryPoints(Range.END_TO_START, spanRange) > 0; return startBeforeEnd && endAfterStart; }
使用示例:
// 获取当前选区和目标span let selection = window.getSelection(); if (selection.rangeCount > 0) { let range = selection.getRangeAt(0); let targetSpan = document.querySelector('#txt span'); if (checkSpanIntersection(range, targetSpan)) { console.log("选区与span存在交集"); } else { console.log("选区与span无交集"); } }
内容的提问来源于stack exchange,提问作者satyam
相关产品推荐
相关产品推荐

