26

ユーザーが奇妙なハイライト ボタン (<span style="background-color:yellow">ハイライトされたテキスト</span>のように) をクリックしたときに選択したテキストをハイライト表示する JavaScript を使用する方法を見つけようとしています。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」を選択すると (ハイライト スパンを選択範囲内の各要素にラップすることはできますが、中央のテキストをハイライト表示するようにしてください)。

この問題はどこにも解決されていないようです。率直に言って、それが可能であるとは思えません。

解析してから置き換えることができる html を含む文字列として、選択範囲から取得した Range を取得できれば可能ですが、私が知る限り、Range の生の html を取得することはできません..残念。

4

8 に答える 8

71

この回答はおそらく数年遅すぎますが、同様の問題に直面し、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);
}

しかし、元の親が述べているように、これは安全ではありません。選択が要素の境界を越えない場合は機能しますが、ユーザー選択によって作成された範囲が 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 が必要になることに注意してください。最終的に、これがインターネット上の他の疲れた魂に役立つことを願っています!

于 2012-10-10T15:59:07.950 に答える
11

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を参照してください。

于 2008-11-25T13:53:30.620 に答える
9

ここに投稿するのは初めてですが、あなたの回答を見て、このようなものはうまくいきませんか? ここにサンプルがあります: 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);
}
于 2016-07-31T08:35:42.520 に答える
6

    function load(){
      window.document.designMode = "On";
      //run this in a button, will highlight selected text
      window.document.execCommand("hiliteColor", false, "#768");
    }
   
    <html>
    <head>

    </head>
    <body contentEditable="true" onload="load()">
      this is text
    </body>
    </html>

于 2008-11-20T23:02:24.490 に答える
3

texthighlighter (非推奨のライブラリ) の typescript ポートであるパッケージのリリースを完了しました。typescript に変換するだけで、いくつかのバグがキャッチされ、今後の作業が容易になりました。チェックアウト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>
于 2020-07-12T03:01:26.130 に答える
2

今日も同じ問題が発生し、選択したタグが複数のタグにまたがって強調表示されました。ソリューション:

  1. 選択した部分を HTML タグと一緒に抽出する方法を見つけてください。
  2. 抽出した部分を 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;
    }
};
于 2015-12-31T09:39:04.833 に答える