Javascript 如何用元素包裹/环绕突出显示的文本

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6328718/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 21:15:58  来源:igfitidea点击:

How To Wrap / Surround Highlighted Text With An Element

javascriptdomselection

提问by coure2011

I want to wrap a selected text in a div container with span, is it possible?

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

A user will select a text and will click a button, on button click event I want to wrap that selected text with span element. I can get the selected text using window.getSelection()but how to know its exact position in DOM structure?

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

回答by Tim Down

If the selection is completely contained within a single text node, you can do this using the surroundContents()method of the range you obtain from the selection. However, this is very brittle: it does not work if the selection cannot logically be surrounded in a single element (generally, if the range crosses node boundaries, although this is not the precise definition). To do this in the general case, you need a more complicated approach.

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

Also, DOM Rangeand window.getSelection()are not supported in IE < 9. You'll need another approach again for those browsers. You can use a library such as my own Rangyto normalize browser behaviour and you may find the class applier moduleuseful for this question.

此外,DOMRangewindow.getSelection()IE < 9 不支持。对于这些浏览器,您将再次需要另一种方法。您可以使用诸如我自己的Rangy的库来规范浏览器行为,并且您可能会发现class applier模块对这个问题很有用。

Simple surroundContents()example jsFiddle: http://jsfiddle.net/VRcvn/

简单的surroundContents()例子jsFiddle:http: //jsfiddle.net/VRcvn/

Code:

代码:

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);
        }
    }
}

回答by amal

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.

JS小提琴

回答by JanD

it is possible. You need to use the range API and the Range.surroundContents() method. It places the node the content is wrapped in at the start of the specified range. see https://developer.mozilla.org/en/DOM/range.surroundContents

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

回答by Ali Gangji

surroundContents only works if your selection contains only text and no HTML. Here is a more flexible, as well as cross-browser solution. This will insert a span like this:

仅当您的选择仅包含文本而不包含 HTML 时, aroundContents 才有效。这是一个更灵活的跨浏览器解决方案。这将插入一个像这样的跨度:

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

The span is inserted before the selection, in front of the nearest opening HTML tag.

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

回答by sathiya

Please find the below code will be helpfull for wrapping the span tag for all kind of tags. Please go through the code and use the logic for your implementation.

请找到以下代码将有助于为所有类型的标签包装 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';
                    }
                }
            }
        }
    }
}