javascript用户选择突出显示

时间:2008-11-20 10:06:54

标签: javascript highlight

我正在尝试使用javascript来突出显示用户点击某个奇怪突出显示按钮时所选择的文本(如< span style =“background-color:yellow”>突出显示的文字< / span> )。它只需要与WebKit或Firefox一起使用,但它似乎几乎不可能,因为它必须在以下情况下工作:

<p>this is text</p>
<p>I eat food</p>

当用户在浏览器中选择“is text”到“I eat”时(不能只放一个跨度)。

和这种情况:

<span><span>this is text</span>middle text<span>this is text</span></span>

当用户在浏览器中选择“is text”到“this is”时(即使你可以在选择中的每个元素周围包装你的高亮显示,我想看到你试图让那个中间文本突出显示)。

这个问题似乎没有在任何地方得到解决,坦白说我怀疑这是可能的。

如果你能把你从选择中获得的Range作为一个带有html的字符串,可以被解析然后替换,这是可能的,但据我所知,你无法得到一个原始的html范围..可惜。

7 个答案:

答案 0 :(得分:49)

这个答案对你来说可能要花费几年时间,但我遇到了类似的问题,并希望在此处记录,因为它是Google上的第一个热门话题。

重申一下,问题是你想从用户选择中捕获Range对象并用样式化的div包围它,如下所示:

function highlightSelection() {
    var userSelection = window.getSelection().getRangeAt(0);
    highlightRange(userSelection);

}

function highlightRange(range) {
    var newNode = document.createElement("div");
    newNode.setAttribute(
       "style",
       "background-color: yellow; display: inline;"
    );
    range.surroundContents(newNode);
}

但正如Original Parent所说,这是不安全的。如果选择不跨越元素边界,它将起作用,但如果用户选择创建的范围是跨越HTML标记边界的不安全范围,它将抛出DOM错误。


解决方案是生成一个较小的Range对象数组,这些对象都没有单独穿过元素屏障,但它们共同覆盖了用户选择的Range。每个安全范围都可以如上突出显示。

function getSafeRanges(dangerous) {
    var a = dangerous.commonAncestorContainer;
    // Starts -- Work inward from the start, selecting the largest safe range
    var s = new Array(0), rs = new Array(0);
    if (dangerous.startContainer != a)
        for(var i = dangerous.startContainer; i != a; i = i.parentNode)
            s.push(i)
    ;
    if (0 < s.length) for(var i = 0; i < s.length; i++) {
        var xs = document.createRange();
        if (i) {
            xs.setStartAfter(s[i-1]);
            xs.setEndAfter(s[i].lastChild);
        }
        else {
            xs.setStart(s[i], dangerous.startOffset);
            xs.setEndAfter(
                (s[i].nodeType == Node.TEXT_NODE)
                ? s[i] : s[i].lastChild
            );
        }
        rs.push(xs);
    }

    // Ends -- basically the same code reversed
    var e = new Array(0), re = new Array(0);
    if (dangerous.endContainer != a)
        for(var i = dangerous.endContainer; i != a; i = i.parentNode)
            e.push(i)
    ;
    if (0 < e.length) for(var i = 0; i < e.length; i++) {
        var xe = document.createRange();
        if (i) {
            xe.setStartBefore(e[i].firstChild);
            xe.setEndBefore(e[i-1]);
        }
        else {
            xe.setStartBefore(
                (e[i].nodeType == Node.TEXT_NODE)
                ? e[i] : e[i].firstChild
            );
            xe.setEnd(e[i], dangerous.endOffset);
        }
        re.unshift(xe);
    }

    // Middle -- the uncaptured middle
    if ((0 < s.length) && (0 < e.length)) {
        var xm = document.createRange();
        xm.setStartAfter(s[s.length - 1]);
        xm.setEndBefore(e[e.length - 1]);
    }
    else {
        return [dangerous];
    }

    // Concat
    rs.push(xm);
    response = rs.concat(re);    

    // Send to Console
    return response;
}

然后可以(似乎)突出显示用户选择,使用此修改后的代码:

function highlightSelection() {
    var userSelection = window.getSelection().getRangeAt(0);
    var safeRanges = getSafeRanges(userSelection);
    for (var i = 0; i < safeRanges.length; i++) {
        highlightRange(safeRanges[i]);
    }
}

请注意,您可能需要一些更高级的CSS来使用户可以看起来很漂亮的许多不同元素。我希望最终这能帮助互联网上其他一些疲惫的灵魂!

答案 1 :(得分:8)

好吧,你可以使用DOM操作来完成它。这适用于Firefox:

var selection = window.getSelection();
var range = selection.getRangeAt(0);
var newNode = document.createElement("span");
newNode.setAttribute("style", "background-color: pink;");
range.surroundContents(newNode); 

似乎也适用于当前版本的Safari。请参阅https://developer.mozilla.org/en/DOM/range.surroundContentshttp://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html

答案 2 :(得分:7)

这是我第一次在这里发帖,但看看你的答案,不会有这样的工作吗?我在这里有一个样本: http://henriquedonati.com/projects/Extension/extension.html

function highlightSelection() {
    var userSelection = window.getSelection();
    for(var i = 0; i < userSelection.rangeCount; i++) {
        highlightRange(userSelection.getRangeAt(i));
    }

}

function highlightRange(range) {
    var newNode = document.createElement("span");
    newNode.setAttribute(
       "style",
       "background-color: yellow; display: inline;"
    );
    range.surroundContents(newNode);
}

答案 3 :(得分:7)

以下是突出显示文字的完整代码

<!DOCTYPE html>
    <html>
        <head>
            <style type="text/css">
                .highlight
                {
                    background-color: yellow;
                }
                #test-text::-moz-selection { /* Code for Firefox */

                    background: yellow;
                }

                #test-text::selection {

                    background: yellow;
                }

            </style>
        </head>

        <body>
            <div id="div1" style="border: 1px solid #000;">
                <div id="test-text">
                    <h1> Hello How are you </h1>
                    <p >
                        Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.
                    </p>
                </div>
            </div>
            <br />

        </body>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
            <script type="text/javascript">
                mouseXPosition = 0;
                $(document).ready(function () {

                    $("#test-text").mousedown(function (e1) {
                        mouseXPosition = e1.pageX;//register the mouse down position
                    });

                    $("#test-text").mouseup(function (e2) {
                        var highlighted = false;
                        var selection = window.getSelection();
                        var selectedText = selection.toString();
                        var startPoint = window.getSelection().getRangeAt(0).startOffset;
                        var endPoint = window.getSelection().getRangeAt(0).endOffset;
                        var anchorTag = selection.anchorNode.parentNode;
                        var focusTag = selection.focusNode.parentNode;
                        if ((e2.pageX - mouseXPosition) < 0) {
                            focusTag = selection.anchorNode.parentNode;
                            anchorTag = selection.focusNode.parentNode;
                        }
                        if (selectedText.length === (endPoint - startPoint)) {
                            highlighted = true;

                            if (anchorTag.className !== "highlight") {
                                highlightSelection();
                            } else {
                                var afterText = selectedText + "<span class = 'highlight'>" + anchorTag.innerHTML.substr(endPoint) + "</span>";
                                anchorTag.innerHTML = anchorTag.innerHTML.substr(0, startPoint);
                                anchorTag.insertAdjacentHTML('afterend', afterText);
                            }

                        }else{
                            if(anchorTag.className !== "highlight" && focusTag.className !== "highlight"){
                                highlightSelection();  
                                highlighted = true;
                            }

                        }


                        if (anchorTag.className === "highlight" && focusTag.className === 'highlight' && !highlighted) {
                            highlighted = true;

                            var afterHtml = anchorTag.innerHTML.substr(startPoint);
                            var outerHtml = selectedText.substr(afterHtml.length, selectedText.length - endPoint - afterHtml.length);
                            var anchorInnerhtml = anchorTag.innerHTML.substr(0, startPoint);
                            var focusInnerHtml = focusTag.innerHTML.substr(endPoint);
                            var focusBeforeHtml = focusTag.innerHTML.substr(0, endPoint);
                            selection.deleteFromDocument();
                            anchorTag.innerHTML = anchorInnerhtml;
                            focusTag.innerHTml = focusInnerHtml;
                            var anchorafterHtml = afterHtml + outerHtml + focusBeforeHtml;
                            anchorTag.insertAdjacentHTML('afterend', anchorafterHtml);


                        }

                        if (anchorTag.className === "highlight" && !highlighted) {
                            highlighted = true;
                            var Innerhtml = anchorTag.innerHTML.substr(0, startPoint);
                            var afterHtml = anchorTag.innerHTML.substr(startPoint);
                            var outerHtml = selectedText.substr(afterHtml.length, selectedText.length);
                            selection.deleteFromDocument();
                            anchorTag.innerHTML = Innerhtml;
                            anchorTag.insertAdjacentHTML('afterend', afterHtml + outerHtml);
                         }

                        if (focusTag.className === 'highlight' && !highlighted) {
                            highlighted = true;
                            var beforeHtml = focusTag.innerHTML.substr(0, endPoint);
                            var outerHtml = selectedText.substr(0, selectedText.length - beforeHtml.length);
                            selection.deleteFromDocument();
                            focusTag.innerHTml = focusTag.innerHTML.substr(endPoint);
                            outerHtml += beforeHtml;
                            focusTag.insertAdjacentHTML('beforebegin', outerHtml );


                        }
                        if (!highlighted) {
                            highlightSelection();
                        }
                        $('.highlight').each(function(){
                            if($(this).html() == ''){
                                $(this).remove();
                            }
                        });
                        selection.removeAllRanges();
                    });
                });

                function highlightSelection() {
                    var selection;

                    //Get the selected stuff
                    if (window.getSelection)
                        selection = window.getSelection();
                    else if (typeof document.selection != "undefined")
                        selection = document.selection;

                    //Get a the selected content, in a range object
                    var range = selection.getRangeAt(0);

                    //If the range spans some text, and inside a tag, set its css class.
                    if (range && !selection.isCollapsed) {
                        if (selection.anchorNode.parentNode == selection.focusNode.parentNode) {
                            var span = document.createElement('span');
                            span.className = 'highlight';
                            span.textContent = selection.toString();
                            selection.deleteFromDocument();
                            range.insertNode(span);
    //                        range.surroundContents(span);
                        }
                    }
                }

            </script>
    </html>

https://jsfiddle.net/Bilalchk123/1o4j0w2v/

答案 4 :(得分:4)

<html>
<head>
<script type="text/javascript">
function load(){
  window.document.designMode = "On";
  //run this in a button, will highlight selected text
  window.document.execCommand("hiliteColor", false, "#000");
}
</script>
</head>
<body contentEditable="true" onload="load()">
  this is text
</body>
</html>

答案 5 :(得分:1)

我今天遇到了同样的问题,突出显示多个标签上的所选标签。 解决方案:

  1. 找到一种方法来提取所选部分以及HTML标记
  2. 用span元素包装提取的部分并将其放回DOM
  3. 请参阅下面的代码,以获得进一步的说明。

    function getRangeObject(selectionObject){
        try{ 
            if(selectionObject.getRangeAt)
                return selectionObject.getRangeAt(0);
        }
        catch(ex){
            console.log(ex);
        }
    }
    document.onmousedown = function(e){
        var text;
        if (window.getSelection) {
            /* get the Selection object */
            userSelection = window.getSelection()
    
            /* get the innerText (without the tags) */ 
            text = userSelection.toString();
    
            /* Creating Range object based on the userSelection object */
            var rangeObject = getRangeObject(userSelection);
    
            /* 
               This extracts the contents from the DOM literally, inclusive of the tags. 
               The content extracted also disappears from the DOM 
            */
            contents = rangeObject.extractContents(); 
    
            var span = document.createElement("span");
            span.className = "highlight";
            span.appendChild(contents);
    
            /* Insert your new span element in the same position from where the selected text was extracted */
            rangeObject.insertNode(span);
    
        } else if (document.selection && document.selection.type != "Control") {
                text = document.selection.createRange().text;
        }
    };
    

答案 6 :(得分:1)

我刚刚完成了一个程序包的发布,该程序包是texthighlighter的打字稿端口(已弃用的库)。仅仅将其转换为打字稿就发现了一些错误,并使以后的工作变得更加容易。结帐https://www.npmjs.com/package/@funktechno/texthighlighter。这没有依赖性,并允许突出显示用户选择,合并突出显示,删除突出显示,序列化和反序列化(从数据中应用)突出显示。

请注意,您将需要使用javascript mouseup事件来正确触发它。

import { doHighlight, deserializeHighlights, serializeHighlights, removeHighlights, optionsImpl } from "@/../node_modules/@funktechno/texthighlighter/lib/index";
const domEle = document.getElementById("sandbox");
const options: optionsImpl = {};
if (this.color) options.color = this.color;
if (domEle) doHighlight(domEle, true, options);

这就是我在vue ts项目中触发它的方式

<div
     id="sandbox"
     @mouseup="runHighlight($event)"
>text to highlight</div>