9

この回答に示されている手法を使用して、Webページの選択範囲を単語の境界まで拡張しています。

function snapSelectionToWord() {
    var sel;

    // Check for existence of window.getSelection() and that it has a
    // modify() method. IE 9 has both selection APIs but no modify() method.
    if (window.getSelection && (sel = window.getSelection()).modify) {
        sel = window.getSelection();
        if (!sel.isCollapsed) {

            // Detect if selection is backwards
            var range = document.createRange();
            range.setStart(sel.anchorNode, sel.anchorOffset);
            range.setEnd(sel.focusNode, sel.focusOffset);
            var backwards = range.collapsed;
            range.detach();

            // modify() works on the focus of the selection
            var endNode = sel.focusNode, endOffset = sel.focusOffset;
            sel.collapse(sel.anchorNode, sel.anchorOffset);
            if (backwards) {
                sel.modify("move", "forward", "word");
                sel.extend(endNode, endOffset);
                sel.modify("extend", "backward", "word");

            } else {
                sel.modify("move", "backward", "word");
                sel.extend(endNode, endOffset);
                sel.modify("extend", "forward", "word");
            }
        }
    } else if ( (sel = document.selection) && sel.type != "Control") {
        var textRange = sel.createRange();
        if (textRange.text) {
            textRange.expand("word");
            // Move the end back to not include the word's trailing space(s),
            // if necessary
            while (/\s$/.test(textRange.text)) {
                textRange.moveEnd("character", -1);
            }
            textRange.select();
        }
    }
}​

ここまでは順調ですね。ただしsnapSelectionToWord、選択範囲で関数を複数回呼び出すと、呼び出しごとに双方向に1語ずつ拡張されます。これは、テキストが選択されているときに関数を複数回呼び出す場合には適していません。

これは、問題を示す「スナップ」ボタンを繰り返しクリックできるライブjsFiddleの例です。

元のソリューションを修正して、すでに単語の境界にある場合に選択範囲が拡張されないようにするにはどうすればよいですか?

  • 元の解決策についてコメントを残したいのですが、残念ながら、StackOverflowカルマ旅団から十分なカルマがまだ与えられていません。それ以外の場合は、そこで質問します。また、問題を解決する方法がわからないため、元のソリューションを編集しません。

編集:リクエストごとにコードスニペットを追加

4

3 に答える 3

9

I wrote that sample. I've never been happy with it, for the reason you point out, and also because it doesn't work consistently in all browsers (or at all in Opera).

I've been working on a cross-browser solution to this for my Rangy library. The current release is described as an alpha but it works pretty well. Here's a demo:

http://rangy.googlecode.com/svn/trunk/demos/textrange.html

And here's your demo, modified to use Rangy:

http://jsfiddle.net/timdown/RgZ8r/

The crucial line is

rangy.getSelection().expand("word");

If you don't want to use something as heavyweight as Rangy (it's something like 50KB of code to use the TextRange module) then it's possible to improve the original code (as Matt M has in his answer) but it will still have limitations.

于 2012-06-09T22:50:30.840 に答える
7

単語にスナップする前に、いずれかの方向に文字をポップしてみてください。

        if (backwards) {
            sel.modify("move", "backward", "character");
            sel.modify("move", "forward", "word");
            sel.extend(endNode, endOffset);
            sel.modify("extend", "forward", "character");
            sel.modify("extend", "backward", "word");

        } else {
            sel.modify("move", "forward", "character");
            sel.modify("move", "backward", "word");
            sel.extend(endNode, endOffset);
            sel.modify("extend", "backward", "character");
            sel.modify("extend", "forward", "word");
        }

http://jsfiddle.net/3RAkZ/

于 2012-06-09T21:33:11.027 に答える
1

あなたのコードはアラビア語のテキストでは正しく動作しません。代わりにこのスニペットを試すことができます

function snapSelectionToWord() {
    var sel;

    // Check for existence of window.getSelection() and that it has a
    // modify() method. IE 9 has both selection APIs but no modify() method.
    if (window.getSelection && (sel = window.getSelection()).modify) {
        sel = window.getSelection();
        if (sel.isCollapsed) {
           
           var rng2 = sel.getRangeAt(0);
           var startOffset = rng2.startOffset;
           startOffset = 0
           for (var i = rng2.startOffset; i >= 0; i--) {
              if (rng2.startContainer.data[i].match(/\S/) != null) {
                 startOffset++;
              } else
                 break;
           }
           var endOffset = rng2.endOffset;
           endOffset = 0;
           for (var i = rng2.endOffset; i < rng2.endContainer.data.length; i++)
              if (rng2.endContainer.data[i].match(/\S/)) {
                 endOffset++;
              } else
                 break;

           startOffset = rng2.startOffset - startOffset;
           startOffset = startOffset < 0 ? 0 : startOffset;

           endOffset = rng2.endOffset + endOffset;
           endOffset = endOffset >= rng2.endContainer.data.length ? rng2.endContainer.data.length - 1 : endOffset;

           rng2.setStart(rng2.startContainer, startOffset);
           rng2.setEnd(rng2.endContainer, endOffset);
           sel.removeAllRanges();
           sel.addRange(rng2);
           
        }
    } else if ( (sel = document.selection) && sel.type != "Control") {
        var textRange = sel.createRange();
        if (textRange.text) {
            textRange.expand("word");
            // Move the end back to not include the word's trailing space(s),
            // if necessary
            while (/\s$/.test(textRange.text)) {
                textRange.moveEnd("character", -1);
            }
            textRange.select();
        }
    }
}

于 2015-01-29T15:32:06.627 に答える