根据textContent

时间:2018-04-08 19:37:43

标签: javascript dom text offset highlight

恩惠

在测试创建此类解决方案时最有用的答案时,赏金将在最新版本的Firefox,Chrome和Internet Explorer中获得最快的解决方案,如jsPerf所示。 由我自行决定。 Mwahahaha!

主要对解决方案感到满意,该解决方案采用所有偏移和未经处理的<span>并添加突出显示,因此parent.textContent = parent.textContent然后在更新的偏移列表上运行解决方案将重新突出显示,但这具有不利的时间复杂性,因此不是首选。

相关问题

我有一个只包含文字的元素,我想突出显示。我还有一个[startline, startcol, endline, endcol]数组,知道.textContent中每行的长度,我可以归一化为[startoffset, endoffset]。如何在每对偏移之间突出显示

这个问题比看起来更难,因为:

  • 不保证内容没有重复(因此没有查找/替换)和
  • 最终必须在已突出显示的文本上执行突出显示,有时与已突出显示的文本相交,
  • 必须根据父元素.textContent属性的索引执行突出显示。

解释

  • 突出显示:将元素的textContent中的文字子集放在一个或多个<span class="highlighted">中,而不更改父元素&#39; s { {1}}值,使得突出显示n次的文本位于n个嵌套textContent元素内。
  • offset :一个非负整数,表示某个点之前的字符的数量(在两个字符之间)。
  • character :JavaScript提供的实例,作为<span class="highlighted">字符串(包括空格)的给定索引处的值。

MCVE

&#13;
&#13;
.textContent
&#13;
function highlight(parent, startoff, endoff) {
  // Erm...
  parent.textContent;
}

// Test cases

var starts = [
  5,  44, 0, 50, 6,  100, 99,  50, 51, 52
];
var ends = [
  20, 62, 4, 70, 10, 100, 101, 54, 53, 53
];
for (var i = 0; i < 10; i += 1) {
  highlight(document.getElementById("target"),
            starts[i], ends[i]);
}
&#13;
#target {
  white-space: pre-wrap;
}
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:4)

对开始/结束位置进行标准化以避免重叠。

  1. 将起始位置和结束位置合并为具有相反值的单个列表(例如,-1和1)
  2. 按位置值排序列表,然后按标记值排序(基于第二级排序,您可以区分连续范围合并它们)
  3. 浏览职位列表并将当前位置的值标记添加到当前总和;一旦它&#34; 0&#34; - 这意味着你刚刚找到了一些嵌套/交叉部分的结尾;
  4. 通过这种方式,您将获得突出显示的位置,而不会嵌套/重叠范围。

    用文本节点和HTML元素(例如<span>documentFragment.replaceChild()混合替换文本节点会有所帮助:

    &#13;
    &#13;
    let starts = [
        5,  44, 0, 50, 6,  100, 99,  50, 51, 52
    ];
    let ends = [
        20, 62, 4, 70, 10, 100, 101, 54, 53, 53
    ];
    
    let positions = [];
    let normalizedPositions = [];
    starts.forEach(function(position) {
        positions.push({position, value: 1});
    });
    ends.forEach(function(position) {
        positions.push({position, value: -1});
    });
    positions = positions.sort(function(a, b) {
        return a.position - b.position || 
            b.value - a.value
    });
    
    var currentSection = {from: 0, counter: 0};
    
    for(position of positions) {
        if (!currentSection.counter) {
            if (position.value === -1) {
                throw `inconsistent boundaries: closing before opening ${position.position}`;
            }
            currentSection.from = position.position;  
        }
        currentSection.counter += position.value;
    
        if (!currentSection.counter) { 
            normalizedPositions.push({
                from: currentSection.from, 
                to: position.position
            });
        }
    }
    if (currentSection.counter) {
        throw "last section has not been closed properly";   
    }
    
    
    let parentNode = document.querySelector('p');
    let textNodeToReplace = parentNode.childNodes[0];
    let sourceText = textNodeToReplace.nodeValue;
    
    let documentFragment = document.createDocumentFragment();
    let withoutHighlightingStart = 0;
    
    normalizedPositions.forEach(function (highlightRange) {
        if (highlightRange.from> withoutHighlightingStart) {
          let notHighlighted = createTextNode(sourceText.slice(withoutHighlightingStart, highlightRange.from));
          documentFragment.appendChild(notHighlighted);
        }
        let highlighted = createHighlighted(sourceText.slice(highlightRange.from, highlightRange.to));
        documentFragment.appendChild(highlighted);
        withoutHighlightingStart = highlightRange.to;
    });
    let lastNotHighlighted = createTextNode(sourceText.slice(withoutHighlightingStart));
    documentFragment.appendChild(lastNotHighlighted);
    
    parentNode.replaceChild(documentFragment, textNodeToReplace);
    
    function createTextNode(str) {
       return document.createTextNode(str);
    }
    
    function createHighlighted(str) {
       let span = document.createElement('span');
       span.classList.add('highlight');
       span.appendChild(createTextNode(str));
       return span;
    }
    &#13;
    .highlight {
        background-color: yellow;
        color: dark-blue;
    }
    &#13;
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
    &#13;
    &#13;
    &#13;