如何突出显示已突出显示的文本中的用户所选文本?

时间:2010-10-11 07:50:27

标签: select text highlight

我有一个页面,我在div中显示一些文本,我需要在某些部分突出显示该文本。我通过使用标记和适当的css样式包围我需要突出显示的文本来完成此操作。 例如。 <div> My text will look like this with <span class="highlight">highlighted bits</span> in it. </div>

这很好用。但是,此页面的另一个要求是用户必须能够选择文本,单击按钮,并且还必须突出显示所选文本。

我遇到的问题是在尝试识别要抓取的所选文本的范围时(使用window.getSelection.getRangeAt(0)),这为我提供了在文本中每个<span>标记之后重置的范围,而不是从文本的开头。

1 个答案:

答案 0 :(得分:1)

对于那些希望将来知道的人,我就是这样做的:

jQuery.fn.highlight = function(startOffset,endOffset,type) {
 function innerHighlight(node, startOffset,endOffset) {
     var calledStartOffset = parseInt(startOffset);
     var startOffsetNode=getChildNodeForOffset(node,parseInt(startOffset));
     var endOffsetNode=getChildNodeForOffset(node,parseInt(endOffset));
     startOffset = resizeOffsetForNode(startOffsetNode,parseInt(startOffset));

     if (startOffsetNode == endOffsetNode){
         endOffset = resizeOffsetForNode(endOffsetNode,parseInt(endOffset));
         highlightSameNode(startOffsetNode, parseInt(startOffset),parseInt(endOffset),type,calledStartOffset);
     } else {
         highlightDifferentNode(startOffsetNode,endOffsetNode,parseInt(startOffset),parseInt(endOffset),type,calledStartOffset);
     }
 }
 return this.each(function() {
     innerHighlight(this, startOffset,endOffset);
 });
};

function resizeOffsetForNode(offsetNode,offset){
 if (offsetNode.id >= 0){
     offset = parseInt(offset)-parseInt(offsetNode.id);
 } else if (offsetNode.previousSibling != null && offsetNode.previousSibling.id > 0){
     offset = parseInt(offset)-parseInt(offsetNode.previousSibling.id)-parseInt(offsetNode.previousSibling.textContent.length);
 }  
 return offset;
}

function getChildNodeForOffset(testNode,offset) {
    if (testNode.nodeType == 1 && testNode.childNodes && !/(script|style)/i.test(testNode.tagName)) {
       var offsetNode=null;
       var currentNode;
       for (var i = 0; i < testNode.childNodes.length; ++i) {
           currentNode=testNode.childNodes[i];
           if (currentNode.id >= 0 && parseInt(currentNode.id) <= parseInt(offset) && ((parseInt(currentNode.id) + parseInt(currentNode.textContent.length)) >= parseInt(offset))){
               offsetNode = currentNode;
               break;
           } else if (currentNode.id >= 0 && parseInt(currentNode.id) > parseInt(offset)){
               offsetNode = currentNode.previousSibling;
               break;
           }
        }
       if (offsetNode==null){
           offsetNode = testNode.childNodes[testNode.childNodes.length-1]; 
       }
       return offsetNode;
    }
}

function highlightSameNode(node, startOffset,endOffset,type,calledStartOffset) {
   var skip = 0;
   if (node.nodeType == 3) {
   if (startOffset >= 0) {
    var spannode = document.createElement('span');
    spannode.className = 'entity '+ type;
    spannode.id=calledStartOffset;
    var middlebit = node.splitText(startOffset);
    var endbit = middlebit.splitText(endOffset-startOffset);
    var middleclone = middlebit.cloneNode(true);
    spannode.appendChild(middleclone);
    middlebit.parentNode.replaceChild(spannode, middlebit);
   }
  } else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
    var childnode = node.childNodes[0];
    highlightSameNode(childnode, startOffset,endOffset,type,calledStartOffset);
  }
}

function highlightDifferentNode(startnode, endnode, startOffset,endOffset,type,calledStartOffset) {
   var skip = 0;
   if (startnode.nodeName == "#text") {
       if (startOffset >= 0) {
            var spannode = document.createElement('span');
            spannode.className = 'entity '+ type;
            spannode.id=calledStartOffset;
            var endbit = node.splitText(startOffset);
            var endclone = endbit.cloneNode(true);
            spannode.appendChild(endclone);
            endbit.parentNode.replaceChild(spannode, endbit);
       }
   } else if (startnode.nodeName == "SPAN") {
       if (startOffset >= 0) {
            var spannode = document.createElement('span');
            spannode.className = 'entity '+ type;
            spannode.id=calledStartOffset;
            var endTextbit = startnode.childNodes[0].splitText(startOffset);
            spannode.appendChild(endTextbit);
            startnode.parentNode.insertBefore(spannode, startnode.nextSibling);
       }
   }
   var currentTestNode=startnode.nextSibling;
   while (currentTestNode!=endnode){
       if (currentTestNode.nodeName == "#text") {
            var spannode = document.createElement('span');
            spannode.className = 'entity '+ type;
            spannode.id=parseInt(currentTestNode.previousSibling.id)+parseInt(currentTestNode.previousSibling.textContent.length);
            var currentNodeClone=currentTestNode.cloneNode(true);
            spannode.appendChild(currentNodeClone);
            endbit.parentNode.replaceChild(spannode, currentTestNode);
       } else if (currentTestNode.nodeName == "SPAN") {
            currentTestNode.className = 'entity overlap';
       }
       currentTestNode=currentTestNode.nextSibling;
   }
   var previousNodeEnd = parseInt(endnode.previousSibling.id)+parseInt(endnode.previousSibling.textContent.length);
   var spannode = document.createElement('span');
   spannode.className = 'entity '+ type;
   spannode.id=previousNodeEnd;
   if (endnode.nodeName == "#text") {
       if (endOffset >= 0) {
            //end offset here is the original end offset from the beginning of the text, not node
            var unwantedbit = endnode.splitText(parseInt(endOffset)-parseInt(previousNodeEnd));
            var endclone = endnode.cloneNode(true);
            spannode.appendChild(endclone);
            endnode.parentNode.replaceChild(spannode, endnode);
       }
   } else if (endnode.nodeName == "SPAN") {
       if (endOffset >= 0) {
            var wantTextbit = endnode.childNodes[0].splitText(parseInt(endOffset)-parseInt(previousNodeEnd));
            spannode.appendChild(wantTextbit);
            wantTextbit.parentNode.parentNode.insertBefore(spannode, endnode);
       }
   }
   if (startnode.textContent.length < 1){
    startnode.parentNode.removeChild(startnode);
   }
   if (endnode.textContent.length < 1){
       endnode.parentNode.removeChild(endnode);
   }
}

jQuery.fn.removeHighlight = function() {
 return this.find("span.entity").each(function() {
  this.parentNode.firstChild.nodeName;
  with (this.parentNode) {
   replaceChild(this.firstChild, this);
   normalize();
  }
 }).end();
};

function contains(a, b){
      return a.contains ? a != b && a.contains(b) : !!(a.compareDocumentPosition(b) & 16);
}