You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何检查选中文本范围与<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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 13:53:18