Подсветка пользовательского выбора javascript

Я пытаюсь найти способ с javascript, чтобы выделить текст, который пользователь выбирает, когда он нажимает какую-то нечетную кнопку выделения (как в < span style = "background-color: yellow" > выделенный текст </span> ). Он должен работать только с WebKit или Firefox, но, похоже, он почти невозможен, поскольку он должен работать в следующих случаях:

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

Когда пользователь выбирает из "текст" через "Я ем" в браузере (не могу просто поместить туда провал).

и этот случай:

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

Когда пользователь выбирает от "is text" до "this is" в браузере (даже если вы можете обернуть ваши выделенные промежутки вокруг каждого элемента в выделенном фрагменте, я хотел бы, чтобы вы попытались выделить выделенный средний текст).

Эта проблема, кажется, нигде не решается, честно говоря, я сомневаюсь, что это возможно.

Было бы возможно, если бы вы могли получить Range, который вы получаете из выделения, в виде строки, полной html, которая может быть проанализирована, а затем заменена, но, насколько я могу судить, вы не можете получить необработанный html Диапазон.. жалость.

Ответ 1

Этот ответ, вероятно, несколько лет для вас слишком поздно, но я столкнулся с аналогичной проблемой и хотел записать его здесь, так как это первый хит в 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);
}

Но в качестве исходных состояний родителя это небезопасно. Он будет работать, если выбор не пересекает границы элементов, но он вызовет ошибку DOM, если диапазон, созданный выбором пользователя, является небезопасным диапазоном, который пересекает границы тегов HTML.


Решение состоит в том, чтобы создать массив меньших объектов 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 Fancier, чтобы сделать множество разрозненных элементов удобным для пользователя. Надеюсь, что в конечном итоге это поможет кому-то еще утомиться в Интернете!

Ответ 2

Ну, вы можете сделать это, используя 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.surroundContents и http://www.w3.org/TR/2000/REC-DOM-Level-2-Traversal-Range-20001113/ranges.html

Ответ 3

<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>

Ответ 4

Это моя первая публикация здесь, но, просматривая ваши ответы, не будет что-то вроде этой работы? У меня есть образец здесь: 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);
}

Ответ 5

Вот полный код для подсветки текста и подчеркивания текста

<!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 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/

Ответ 6

У меня была такая же проблема сегодня, выделив выбранные теги, начиная с нескольких тегов. Решение:

  • Найдите способ извлечь выбранную часть вместе с тегами HTML.
  • Оберните извлеченную часть элементом span и верните его в DOM.

Обратитесь к приведенному ниже коду, для уточнения.

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