Javascript选择文本突出显示概率

时间:2015-04-27 11:44:16

标签: javascript getselection

我有一个带有文字内容的html页面。在选择任何文本并按突出显示按钮时,我可以更改所选文本的样式以突出显示相同的文本。为了实现这个功能,我写了以下方法。

sel = window.getSelection();
var range = sel.getRangeAt(0);
var span = document.createElement('span');
span.className = "highlight" + color;
range.surroundContents(span);

如果选择没有html标记的文本,但是当文本中间有任何html标记时,这是正常的,它会给出错误

  

无法执行' surroundContents' on' Range':Range已部分选择了非Text节点。

如何解决这个问题。是否可以为每个部分分别突出显示相同的内容(除以html标签)?

7 个答案:

答案 0 :(得分:26)

请参阅Range.extractContents

document.getElementById('execute').addEventListener('click', function() {
    var range = window.getSelection().getRangeAt(0),
        span = document.createElement('span');

    span.className = 'highlight';
    span.appendChild(range.extractContents());
    range.insertNode(span);
});
.highlight { background-color: yellow; }
<div id="test">
    Select any part of <b>this text and</b> then click 'Run'.
</div>

<button id="execute">Run</button>

答案 1 :(得分:5)

我不是重新发明轮子,而是使用Rangy的突出显示功能。

我分配了RGraham created的小提琴并创建了一个显示其工作原理的new fiddle。这就是它的完成方式:

var applier = rangy.createClassApplier("highlight");
var highlighter = rangy.createHighlighter();
highlighter.addClassApplier(applier);

document.getElementById('execute').addEventListener('click', function() {
    highlighter.removeAllHighlights();
    highlighter.highlightSelection("highlight");
});

这样做会创建一个荧光笔,它会在完全位于选区内的元素上设置highlight类,并根据需要为跨越选区的元素创建highlight类。单击标识为execute的按钮时,将删除旧的高亮显示并应用新的高光。

荧光笔功能是Rangy发布的一部分,被认为是&#34; alpha&#34;。 然而,我一直在使用Rangy的alpha版本几年了,但是我发现我的应用程序出现了问题,因为它非常罕见追溯到Rangy。有几次我发现Rangy有问题,Tim Down(其作者)非常敏感。

答案 2 :(得分:2)

试试这个:

newNode.appendChild(range.extractContents())

根据MDN

  

克隆部分选定的节点以包含父标记   使文档片段有效所必需的。

Range.surroundContents

  

但是,如果Range拆分非Text,则会抛出异常   节点只有一个边界点。也就是说,不像   上面的替代方案,如果有部分选定的节点,他们会   不能克隆,而是操作失败。

没有测试,但是......

答案 3 :(得分:2)

这个解决方案有点棘手,但我发现它就足够了

当您仔细查看我们通过调用

获得的选择对象时
matplotlib

您将看到有4个属性:window.getSelection().getRangeAt(0) startContainerstartOffsetendContainer

现在您需要从endOffset开始startContainer,然后从那里开始放置必要的span节点。

如果现在它startOffset是不同的节点,那么您需要开始遍历从endContainerstartContainer的节点

对于遍历,您需要检查可以从DOM对象获取的子节点和兄弟节点。首先浏览endContainer,遍历它的所有子节点并检查子节点是否为内联元素,然后在其周围应用span标记,然后你需要为各种边角情况编写少量编码。

答案 4 :(得分:0)

我的解决方案突出显示了所有选定的节点。

function highlight() {
  const sel = window.getSelection();
  const range = sel.getRangeAt(0);
  const {
    commonAncestorContainer,
    startContainer,
    endContainer,
    startOffset,
    endOffset,
  } = range;
  const nodes = [];

  console.group("range");

  console.log("range", range);
  console.log("commonAncestorContainer", commonAncestorContainer);
  console.log("startContainer", startContainer);
  console.log("endContainer", endContainer);
  console.log("startOffset", startOffset);
  console.log("endOffset", endOffset);
  console.log("startContainer.parentNode", startContainer.parentNode);
  console.groupEnd();

  if (startContainer === endContainer) {
    const span = document.createElement("span");
    span.className = "highlight";
    range.surroundContents(span);
    return;
  }

  // get all posibles selected nodes
  function getNodes(childList) {
    console.group("***** getNode: ", childList);
    childList.forEach((node) => {
      console.log("node:", node, "nodoType", node.nodeType);

      const nodeSel = sel.containsNode(node, true);
      console.log("nodeSel", nodeSel);

      // if is not selected
      if (!nodeSel) return;

      const tempStr = node.nodeValue;
      console.log("nodeValue:", tempStr);

      if (node.nodeType === 3 && tempStr.replace(/^\s+|\s+$/gm, "") !== "") {
        console.log("nodo agregado");
        nodes.push(node);
      }

      if (node.nodeType === 1) {
        if (node.childNodes) getNodes(node.childNodes);
      }
    });
    console.groupEnd();
  }

  getNodes(commonAncestorContainer.childNodes);

  console.log(nodes);

  nodes.forEach((node, index, listObj) => {
    const { nodeValue } = node;
    let text, prevText, nextText;

    if (index === 0) {
      prevText = nodeValue.substring(0, startOffset);
      text = nodeValue.substring(startOffset);
    } else if (index === listObj.length - 1) {
      text = nodeValue.substring(0, endOffset);
      nextText = nodeValue.substring(endOffset);
    } else {
      text = nodeValue;
    }

    const span = document.createElement("span");
    span.className = "highlight";
    span.append(document.createTextNode(text));
    const { parentNode } = node;

    parentNode.replaceChild(span, node);

    if (prevText) {
      const prevDOM = document.createTextNode(prevText);
      parentNode.insertBefore(prevDOM, span);
    }

    if (nextText) {
      const nextDOM = document.createTextNode(nextText);
      parentNode.insertBefore(nextDOM, span.nextSibling);
    }
  });

  sel.removeRange(range);
}

示例https://codesandbox.io/s/api-selection-multiple-with-nodes-gx2is?file=/index.html

答案 5 :(得分:-1)

解决方案真的很棘手。 我以某种方式找到了解决方法。查看我的fiddle

function highlight() {
    var range = window.getSelection().getRangeAt(0),
        parent = range.commonAncestorContainer,
        start = range.startContainer,
        end = range.endContainer;
    var startDOM = (start.parentElement == parent) ? start.nextSibling : start.parentElement;
    var currentDOM = startDOM.nextElementSibling;
    var endDOM = (end.parentElement == parent) ? end : end.parentElement;
    //Process Start Element
    highlightText(startDOM, 'START', range.startOffset);
    while (currentDOM != endDOM && currentDOM != null) {
        highlightText(currentDOM);
        currentDOM = currentDOM.nextElementSibling;
    }
    //Process End Element
    highlightText(endDOM, 'END', range.endOffset);
}

function highlightText(elem, offsetType, idx) {
    if (elem.nodeType == 3) {
        var span = document.createElement('span');
        span.setAttribute('class', 'highlight');
        var origText = elem.textContent, text, prevText, nextText;
        if (offsetType == 'START') {
            text = origText.substring(idx);
            prevText = origText.substring(0, idx);
        } else if (offsetType == 'END') {
            text = origText.substring(0, idx);
            nextText = origText.substring(idx);
        } else {
            text = origText;
        }
        span.textContent = text;

        var parent = elem.parentElement;
        parent.replaceChild(span, elem);
        if (prevText) { 
            var prevDOM = document.createTextNode(prevText);
            parent.insertBefore(prevDOM, span);
        }
        if (nextText) {
            var nextDOM = document.createTextNode(nextText);
            parent.appendChild(nextDOM);
        }
        return;
    }
    var childCount = elem.childNodes.length;
    for (var i = 0; i < childCount; i++) {
        if (offsetType == 'START' && i == 0) 
            highlightText(elem.childNodes[i], 'START', idx);
        else if (offsetType == 'END' && i == childCount - 1)
            highlightText(elem.childNodes[i], 'END', idx);
        else
            highlightText(elem.childNodes[i]);
    }
}

答案 6 :(得分:-2)

if (window.getSelection) {
                var sel = window.getSelection();
                if (!sel) {
                    return;
                }
                var range = sel.getRangeAt(0);
                var start = range.startContainer;
                var end = range.endContainer;
                var commonAncestor = range.commonAncestorContainer;
                var nodes = [];
                var node;

                for (node = start.parentNode; node; node = node.parentNode){
                   var tempStr=node.nodeValue;
                   if(node.nodeValue!=null &&    tempStr.replace(/^\s+|\s+$/gm,'')!='')
                     nodes.push(node);
                   if (node == commonAncestor)
                     break;
                }
                nodes.reverse();

                for (node = start; node; node = getNextNode(node)){
                   var tempStr=node.nodeValue;
                   if(node.nodeValue!=null &&  tempStr.replace(/^\s+|\s+$/gm,'')!='')
                     nodes.push(node);
                   if (node == end)
                    break;
                }

                for(var i=0 ; i<nodes.length ; i++){

                   var sp1 = document.createElement("span");
                   sp1.setAttribute("class", "highlight"+color );
                   var sp1_content = document.createTextNode(nodes[i].nodeValue);
                   sp1.appendChild(sp1_content);
                   var parentNode = nodes[i].parentNode;
                   parentNode.replaceChild(sp1, nodes[i]);
                }
           }