用span展开选定的文本节点

我想将选定的文本包装在一个带有跨度的div容器中,这有可能吗?

用户将select一个文本,并将点击一个button,在button点击事件我想用span元素包装选定的文本。 我可以使用window.getSelection()获取选定的文本,但如何知道它在DOM结构中的确切位置?

如果select完全包含在单个文本节点中,则可以使用从select中获得的范围的surroundContents()方法执行此操作。 但是,这是非常脆弱的:如果select无法在逻辑上被包围在单个元素中(通常,如果该范围跨越节点边界,尽pipe这不是精确的定义 ),则不起作用。 要在一般情况下做到这一点,您需要一个更复杂的方法。

此外,IE浏览器<9中不支持DOM Rangewindow.getSelection() 。这些浏览器需要再次使用另一种方法。 您可以使用我自己的Rangy之类的库来规范化浏览器行为,您可能会发现类应用程序模块对此问题有用。

简单的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); } } } 
 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" /> 

有可能的。 您需要使用范围API和Range.surroundContents()方法。 它将内容所在的节点放置在指定范围的开始处。 请参阅https://developer.mozilla.org/en/DOM/range.surroundContents

如果您的select仅包含文本且不包含HTML,则SurroundContents仅适用。 这是一个更灵活的,以及跨浏览器的解决scheme。 这将插入一个这样的跨度:

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

跨度在select之前插入,在最近的开始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(); } 

请find下面的代码将有用的所有types的标签包装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'; } } } } } }