2011-06-13 18 views
29

我想將選定的文本包裝在具有範圍的div容器中,是否有可能?使用範圍包裝選定的文本節點

用戶將選擇一個文本,並點擊一個按鈕,在按鈕點擊事件我想用span元素來包裝選定的文本。我可以使用window.getSelection()得到選定的文本,但是如何知道它在DOM結構中的確切位置?

回答

42

如果所選內容完全包含在單個文本節點中,則可以使用從選擇中獲得的範圍的surroundContents()方法執行此操作。但是,這非常脆弱:如果選擇無法邏輯地包含在單個元素中(通常,如果範圍跨越節點邊界,雖然這不是precise definition),但它不起作用。要在一般情況下這樣做,您需要更復雜的方法。

另外,DOM Rangewindow.getSelection()在IE <中不受支持9.您需要爲這些瀏覽器再次使用另一種方法。您可以使用我自己的Rangy等庫來標準化瀏覽器行爲,並且您可能會發現class applier module對此問題很有用。

簡單surroundContents()例子的jsfiddle:http://jsfiddle.net/VRcvn/

代碼:只有

function surroundSelection(element) { 
    if (window.getSelection) { 
     var sel = window.getSelection(); 
     if (sel.rangeCount) { 
      var range = sel.getRangeAt(0).cloneRange(); 
      range.surroundContents(element); 
      sel.removeAllRanges(); 
      sel.addRange(range); 
     } 
    } 
} 
+1

如果所選文本跨越節點邊界,我們如何用span選擇文本進行封裝?任何解決方案(無論多麼複雜)都將不勝感激。 – 2016-06-15 15:33:44

+1

@JoshGrinberg:這是不可能的,因爲您的標籤不匹配(例如'foo bar baz')。可能您首先必須操作已有的標籤,以便您要包裝的區域只包含在一個節點中。 – rvighne 2016-07-20 18:12:02

+0

@Tim Down我正在使用你的rangy庫在選定的文本上創建註釋。我已經差不多完成了,但遇到了一個問題,我在選定的文本之前顯示了一個點,但是在多個元素的情況下,當我嘗試創建註釋時,它多次顯示點,因爲創建了多個span標記,原因是多個元素。我如何才能在選定文本的第一個節點上應用任何一個類? – 2017-04-19 09:33:51

12

function wrapSelectedText() {  
 
    var selection= window.getSelection().getRangeAt(0); 
 
    var selectedText = selection.extractContents(); 
 
    var span= document.createElement("span"); 
 
    span.style.backgroundColor = "yellow"; 
 
    span.appendChild(selectedText); 
 
    selection.insertNode(span); 
 
}
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam rhoncus gravida magna, quis interdum magna mattis quis. Fusce tempor sagittis varius. Nunc at augue at erat suscipit bibendum id nec enim. Sed eu odio quis turpis hendrerit sagittis id sit amet justo. Cras ac urna purus, non rutrum nunc. Aenean nec vulputate ante. Morbi scelerisque sagittis hendrerit. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nulla tristique ligula fermentum tortor semper at consectetur erat aliquam. Sed gravida consectetur sollicitudin. 
 

 
<input type="button" onclick="wrapSelectedText();" value="Highlight" />

JS Fiddle

+13

只要選擇不跨越多個塊級元素,並且只要您不介意可能獲取嵌套跨度,這就好了。此外,如果鏈接的頁面不工作,只包含jsFiddle鏈接的答案根本就沒用,所以最好至少在答案中描述方法。 – 2011-06-13 09:56:40

3

surroundContents如果您選擇只包含文本和HTML沒有工作。這是一個更靈活的跨瀏覽器解決方案。這將插入一個這樣的跨度:

<span id="new_selection_span"><!--MARK--></span> 

跨度插入選擇之前,在最近的開始HTML標記的前面。

var span = document.createElement("span"); 
span.id = "new_selection_span"; 
span.innerHTML = '<!--MARK-->'; 

if (window.getSelection) { //compliant browsers 
    //obtain the selection 
    sel = window.getSelection(); 
    if (sel.rangeCount) { 
     //clone the Range object 
     var range = sel.getRangeAt(0).cloneRange(); 
     //get the node at the start of the range 
     var node = range.startContainer; 
     //find the first parent that is a real HTML tag and not a text node 
     while (node.nodeType != 1) node = node.parentNode; 
     //place the marker before the node 
     node.parentNode.insertBefore(span, node); 
     //restore the selection 
     sel.removeAllRanges(); 
     sel.addRange(range); 
    } 
} else { //IE8 and lower 
    sel = document.selection.createRange(); 
    //place the marker before the node 
    var node = sel.parentElement(); 
    node.parentNode.insertBefore(span, node); 
    //restore the selection 
    sel.select(); 
} 
+0

它不包圍http://jsfiddle.net/kjntoj1m/ – KeepMove 2016-05-11 11:24:06

+0

如果您想圍繞不包含HTML元素的選擇,則接受的解決方案效果最好。此解決方案將通過在目標元素之前放置標記跨度來幫助您識別包含選定文本的現有節點。 – 2016-05-16 13:53:18

0

請找到下面的代碼將爲所有類型的標籤包裝span標籤是有用的。請仔細閱讀代碼並使用您的實施邏輯。

getSelectedText(this); 
addAnnotationElement(this, this.parent); 

function getSelectedText(this) { 
    this.range = window.getSelection().getRangeAt(0); 
    this.parent = this.range.commonAncestorContainer; 
    this.frag = this.range.cloneContents(); 
    this.clRange = this.range.cloneRange(); 
    this.start = this.range.startContainer; 
    this.end = this.range.endContainer; 
} 


function addAnnotationElement(this, elem) { 
    var text, textParent, origText, prevText, nextText, childCount, 
     annotationTextRange, 
     span = this.htmlDoc.createElement('span'); 

    if (elem.nodeType === 3) { 
     span.setAttribute('class', this.annotationClass); 
     span.dataset.name = this.annotationName; 
     span.dataset.comment = ''; 
     span.dataset.page = '1'; 
     origText = elem.textContent;    
     annotationTextRange = validateTextRange(this, elem); 
     if (annotationTextRange == 'textBeforeRangeButIntersect') { 
      text = origText.substring(0, this.range.endOffset); 
      nextText = origText.substring(this.range.endOffset); 
     } else if (annotationTextRange == 'textAfterRangeButIntersect') { 
      prevText = origText.substring(0, this.range.startOffset); 
      text = origText.substring(this.range.startOffset); 
     } else if (annotationTextRange == 'textExactlyInRange') { 
      text = origText 
     } else if (annotationTextRange == 'textWithinRange') { 
      prevText = origText.substring(0, this.range.startOffset); 
      text = origText.substring(this.range.startOffset,this.range.endOffset); 
      nextText = origText.substring(this.range.endOffset); 
     } else if (annotationTextRange == 'textNotInRange') { 
      return; 
     } 
     span.textContent = text; 
     textParent = elem.parentElement; 
     textParent.replaceChild(span, elem); 
     if (prevText) { 
      var prevDOM = this.htmlDoc.createTextNode(prevText); 
      textParent.insertBefore(prevDOM, span); 
     } 
     if (nextText) { 
      var nextDOM = this.htmlDoc.createTextNode(nextText); 
      textParent.insertBefore(nextDOM, span.nextSibling); 
     } 
     return; 
    } 
    childCount = elem.childNodes.length; 
    for (var i = 0; i < childCount; i++) { 
     var elemChildNode = elem.childNodes[i]; 
     if(Helper.isUndefined(elemChildNode.tagName) || 
      ! (elemChildNode.tagName.toLowerCase() === 'span' && 
      elemChildNode.classList.contains(this.annotationClass))) { 
      addAnnotationElement(this, elem.childNodes[i]); 
     } 
     childCount = elem.childNodes.length; 
    } 
} 

    function validateTextRange(this, elem) { 
    var textRange = document.createRange(); 

    textRange.selectNodeContents (elem); 
    if (this.range.compareBoundaryPoints (Range.START_TO_END, textRange) <= 0) { 
     return 'textNotInRange'; 
    } 
    else { 
     if (this.range.compareBoundaryPoints (Range.END_TO_START, textRange) >= 0) { 
      return 'textNotInRange'; 
     } 
     else { 
      var startPoints = this.range.compareBoundaryPoints (Range.START_TO_START, textRange), 
       endPoints = this.range.compareBoundaryPoints (Range.END_TO_END, textRange); 

      if (startPoints < 0) { 
       if (endPoints < 0) { 
        return 'textBeforeRangeButIntersect'; 
       } 
       else { 
        return "textExactlyInRange"; 
       } 
      } 
      else { 
       if (endPoints > 0) { 
        return 'textAfterRangeButIntersect'; 
       } 
       else { 
        if (startPoints === 0 && endPoints === 0) { 
         return "textExactlyInRange"; 
        } 
        else { 
         return 'textWithinRange'; 
        } 
       } 
      } 
     } 
    } 
} 
+1

歡迎來到StackOverflow!一個很好的答案解釋了代碼在做什麼,以便人們可以從中學習,而不僅僅是一個巨大的代碼塊。您可以通過突出顯示重要部分來改進答案。 – iblamefish 2017-01-05 12:33:31