149

contentEditable='on' <div> がフォーカスを取り戻したときに、カーソル/キャレットの位置を最後の既知の位置に設定する、決定的なクロスブラウザー ソリューションを求めています。コンテンツの編集可能な div のデフォルトの機能は、クリックするたびにキャレット/カーソルを div 内のテキストの先頭に移動することですが、これは望ましくありません。

divのフォーカスを離れるときに現在のカーソル位置を変数に保存し、再びフォーカスを持ったときにこれを再設定する必要があると思いますが、まとめたり、作業を見つけたりすることはできませんでしたコードサンプルはまだです。

誰かが何か考え、動作するコード スニペット、またはサンプルを持っている場合は、喜んでそれらを参照してください。

私は実際にはまだコードを持っていませんが、ここに私が持っているものがあります:

<script type="text/javascript">
// jQuery
$(document).ready(function() {
   $('#area').focus(function() { .. }  // focus I would imagine I need.
}
</script>
<div id="area" contentEditable="true"></div>

PS。このリソースを試しましたが、<div> では機能しないようです。おそらくテキストエリアのみ(カーソルをコンテンツ編集可能なエンティティの最後に移動する方法

4

8 に答える 8

101

このソリューションは、すべての主要なブラウザーで機能します。

saveSelection()はdivのonmouseupおよびイベントにアタッチされ、選択内容を変数 に保存します。onkeyupsavedRange

restoreSelection()onfocusdivのイベントにアタッチされ、で保存された選択を再選択しsavedRangeます。

これは、ユーザーが div をクリックしたときに選択を復元する必要がない限り、完全に機能します (これは、通常、クリックした場所にカーソルが移動すると予想されるため、少し直感的ではありませんが、完全を期すためにコードが含まれています)。

これを実現するためにonclick、イベントをキャンセルするためのクロス ブラウザー関数であるonmousedown関数によってイベントがキャンセルされます。クリック イベントがキャンセルされると div がフォーカスを受け取らず、この関数が実行されない限り何も選択されないため、関数も関数を実行しcancelEvent()ますcancelEvent()restoreSelection()

変数isInFocusは、フォーカスされているかどうかを格納し、 "false"onblurおよび "true"に変更されますonfocus。これにより、div がフォーカスされていない場合にのみ、クリック イベントをキャンセルできます (そうでない場合は、選択をまったく変更できません)。

クリックで div がフォーカスされたときに選択を変更し、選択を復元しないonclickようにする場合 (およびプログラムで要素にフォーカスが与えられた場合にのみ、または同様の操作を行ってから、 andイベントdocument.getElementById("area").focus();を削除します。イベントとand関数は、このような状況でも安全に取り外すことができます。onclickonmousedownonbluronDivBlur()cancelEvent()

すぐにテストしたい場合は、このコードを HTML ページの本文に直接ドロップすると機能するはずです。

<div id="area" style="width:300px;height:300px;" onblur="onDivBlur();" onmousedown="return cancelEvent(event);" onclick="return cancelEvent(event);" contentEditable="true" onmouseup="saveSelection();" onkeyup="saveSelection();" onfocus="restoreSelection();"></div>
<script type="text/javascript">
var savedRange,isInFocus;
function saveSelection()
{
    if(window.getSelection)//non IE Browsers
    {
        savedRange = window.getSelection().getRangeAt(0);
    }
    else if(document.selection)//IE
    { 
        savedRange = document.selection.createRange();  
    } 
}

function restoreSelection()
{
    isInFocus = true;
    document.getElementById("area").focus();
    if (savedRange != null) {
        if (window.getSelection)//non IE and there is already a selection
        {
            var s = window.getSelection();
            if (s.rangeCount > 0) 
                s.removeAllRanges();
            s.addRange(savedRange);
        }
        else if (document.createRange)//non IE and no selection
        {
            window.getSelection().addRange(savedRange);
        }
        else if (document.selection)//IE
        {
            savedRange.select();
        }
    }
}
//this part onwards is only needed if you want to restore selection onclick
var isInFocus = false;
function onDivBlur()
{
    isInFocus = false;
}

function cancelEvent(e)
{
    if (isInFocus == false && savedRange != null) {
        if (e && e.preventDefault) {
            //alert("FF");
            e.stopPropagation(); // DOM style (return false doesn't always work in FF)
            e.preventDefault();
        }
        else {
            window.event.cancelBubble = true;//IE stopPropagation
        }
        restoreSelection();
        return false; // false = IE style
    }
}
</script>
于 2010-07-24T04:37:51.590 に答える
59

これは標準ベースのブラウザーと互換性がありますが、おそらく IE では失敗します。出発点として提供しています。IE は DOM 範囲をサポートしていません。

var editable = document.getElementById('editable'),
    selection, range;

// Populates selection and range variables
var captureSelection = function(e) {
    // Don't capture selection outside editable region
    var isOrContainsAnchor = false,
        isOrContainsFocus = false,
        sel = window.getSelection(),
        parentAnchor = sel.anchorNode,
        parentFocus = sel.focusNode;

    while(parentAnchor && parentAnchor != document.documentElement) {
        if(parentAnchor == editable) {
            isOrContainsAnchor = true;
        }
        parentAnchor = parentAnchor.parentNode;
    }

    while(parentFocus && parentFocus != document.documentElement) {
        if(parentFocus == editable) {
            isOrContainsFocus = true;
        }
        parentFocus = parentFocus.parentNode;
    }

    if(!isOrContainsAnchor || !isOrContainsFocus) {
        return;
    }

    selection = window.getSelection();

    // Get range (standards)
    if(selection.getRangeAt !== undefined) {
        range = selection.getRangeAt(0);

    // Get range (Safari 2)
    } else if(
        document.createRange &&
        selection.anchorNode &&
        selection.anchorOffset &&
        selection.focusNode &&
        selection.focusOffset
    ) {
        range = document.createRange();
        range.setStart(selection.anchorNode, selection.anchorOffset);
        range.setEnd(selection.focusNode, selection.focusOffset);
    } else {
        // Failure here, not handled by the rest of the script.
        // Probably IE or some older browser
    }
};

// Recalculate selection while typing
editable.onkeyup = captureSelection;

// Recalculate selection after clicking/drag-selecting
editable.onmousedown = function(e) {
    editable.className = editable.className + ' selecting';
};
document.onmouseup = function(e) {
    if(editable.className.match(/\sselecting(\s|$)/)) {
        editable.className = editable.className.replace(/ selecting(\s|$)/, '');
        captureSelection();
    }
};

editable.onblur = function(e) {
    var cursorStart = document.createElement('span'),
        collapsed = !!range.collapsed;

    cursorStart.id = 'cursorStart';
    cursorStart.appendChild(document.createTextNode('—'));

    // Insert beginning cursor marker
    range.insertNode(cursorStart);

    // Insert end cursor marker if any text is selected
    if(!collapsed) {
        var cursorEnd = document.createElement('span');
        cursorEnd.id = 'cursorEnd';
        range.collapse();
        range.insertNode(cursorEnd);
    }
};

// Add callbacks to afterFocus to be called after cursor is replaced
// if you like, this would be useful for styling buttons and so on
var afterFocus = [];
editable.onfocus = function(e) {
    // Slight delay will avoid the initial selection
    // (at start or of contents depending on browser) being mistaken
    setTimeout(function() {
        var cursorStart = document.getElementById('cursorStart'),
            cursorEnd = document.getElementById('cursorEnd');

        // Don't do anything if user is creating a new selection
        if(editable.className.match(/\sselecting(\s|$)/)) {
            if(cursorStart) {
                cursorStart.parentNode.removeChild(cursorStart);
            }
            if(cursorEnd) {
                cursorEnd.parentNode.removeChild(cursorEnd);
            }
        } else if(cursorStart) {
            captureSelection();
            var range = document.createRange();

            if(cursorEnd) {
                range.setStartAfter(cursorStart);
                range.setEndBefore(cursorEnd);

                // Delete cursor markers
                cursorStart.parentNode.removeChild(cursorStart);
                cursorEnd.parentNode.removeChild(cursorEnd);

                // Select range
                selection.removeAllRanges();
                selection.addRange(range);
            } else {
                range.selectNode(cursorStart);

                // Select range
                selection.removeAllRanges();
                selection.addRange(range);

                // Delete cursor marker
                document.execCommand('delete', false, null);
            }
        }

        // Call callbacks here
        for(var i = 0; i < afterFocus.length; i++) {
            afterFocus[i]();
        }
        afterFocus = [];

        // Register selection again
        captureSelection();
    }, 10);
};
于 2009-07-28T08:31:07.370 に答える
20

アップデート

以下に投稿したコードの改良版を組み込んだ、Rangyと呼ばれるクロスブラウザー範囲および選択ライブラリーを作成しました。この特定の質問には、選択の保存と復元のモジュールを使用できますが、プロジェクトで選択を行う必要がなく、プロジェクトの大部分を必要としない場合は、@NicoBurnsの回答のようなものを使用したくなるでしょう。図書館。

前の回答

IERange(http://code.google.com/p/ierange/)を使用して、IEのTextRangeをDOM Rangeのようなものに変換し、まぶたのない開始点のようなものと組み合わせて使用​​できます。個人的には、すべてを使用するのではなく、Range<->TextRange変換を行うIERangeのアルゴリズムのみを使用します。また、IEの選択オブジェクトにはfocusNodeプロパティとanchorNodeプロパティがありませんが、代わりに選択から取得したRange/TextRangeを使用できるはずです。

私はこれを行うために何かをまとめるかもしれません、私がそうするなら、そしていつでもここに投稿します。

編集:

これを行うスクリプトのデモを作成しました。まだ調べる時間がなかったOpera9のバグを除いて、これまでに試したすべての機能で動作します。それが動作するブラウザは、IE 5.5、6および7、Chrome 2、Firefox 2、3および3.5、およびSafari4です。すべてWindows上にあります。

http://www.timdown.co.uk/code/selections/

フォーカスノードが選択の開始位置にあり、右または左のカーソルキーを押すと、選択の開始位置を基準にした位置にキャレットが移動するように、ブラウザで選択を逆方向に行うことができることに注意してください。選択範囲を復元するときにこれを複製することはできないと思うので、フォーカスノードは常に選択範囲の最後にあります。

これについては、近いうちに完全に書き留めます。

于 2009-08-14T17:39:16.020 に答える
7

Nico Burns の回答を参考にして、jQuery を使用して作成しました。

  • ジェネリック: すべてdiv contentEditable="true"
  • より短い

jQuery 1.6 以降が必要です。

savedRanges = new Object();
$('div[contenteditable="true"]').focus(function(){
    var s = window.getSelection();
    var t = $('div[contenteditable="true"]').index(this);
    if (typeof(savedRanges[t]) === "undefined"){
        savedRanges[t]= new Range();
    } else if(s.rangeCount > 0) {
        s.removeAllRanges();
        s.addRange(savedRanges[t]);
    }
}).bind("mouseup keyup",function(){
    var t = $('div[contenteditable="true"]').index(this);
    savedRanges[t] = window.getSelection().getRangeAt(0);
}).on("mousedown click",function(e){
    if(!$(this).is(":focus")){
        e.stopPropagation();
        e.preventDefault();
        $(this).focus();
    }
});

savedRanges = new Object();
$('div[contenteditable="true"]').focus(function(){
    var s = window.getSelection();
    var t = $('div[contenteditable="true"]').index(this);
    if (typeof(savedRanges[t]) === "undefined"){
        savedRanges[t]= new Range();
    } else if(s.rangeCount > 0) {
        s.removeAllRanges();
        s.addRange(savedRanges[t]);
    }
}).bind("mouseup keyup",function(){
    var t = $('div[contenteditable="true"]').index(this);
    savedRanges[t] = window.getSelection().getRangeAt(0);
}).on("mousedown click",function(e){
    if(!$(this).is(":focus")){
        e.stopPropagation();
        e.preventDefault();
        $(this).focus();
    }
});
div[contenteditable] {
    padding: 1em;
    font-family: Arial;
    outline: 1px solid rgba(0,0,0,0.5);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div contentEditable="true"></div>
<div contentEditable="true"></div>
<div contentEditable="true"></div>

于 2013-04-22T19:50:43.867 に答える
5

遊んだ後、上記のまぶたのない答えを変更し、jQueryプラグインにしたので、次のいずれかを実行できます。

var html = "The quick brown fox";
$div.html(html);

// Select at the text "quick":
$div.setContentEditableSelection(4, 5);

// Select at the beginning of the contenteditable div:
$div.setContentEditableSelection(0);

// Select at the end of the contenteditable div:
$div.setContentEditableSelection(html.length);

長いコード投稿を許してください、しかしそれは誰かを助けるかもしれません:

$.fn.setContentEditableSelection = function(position, length) {
    if (typeof(length) == "undefined") {
        length = 0;
    }

    return this.each(function() {
        var $this = $(this);
        var editable = this;
        var selection;
        var range;

        var html = $this.html();
        html = html.substring(0, position) +
            '<a id="cursorStart"></a>' +
            html.substring(position, position + length) +
            '<a id="cursorEnd"></a>' +
            html.substring(position + length, html.length);
        console.log(html);
        $this.html(html);

        // Populates selection and range variables
        var captureSelection = function(e) {
            // Don't capture selection outside editable region
            var isOrContainsAnchor = false,
                isOrContainsFocus = false,
                sel = window.getSelection(),
                parentAnchor = sel.anchorNode,
                parentFocus = sel.focusNode;

            while (parentAnchor && parentAnchor != document.documentElement) {
                if (parentAnchor == editable) {
                    isOrContainsAnchor = true;
                }
                parentAnchor = parentAnchor.parentNode;
            }

            while (parentFocus && parentFocus != document.documentElement) {
                if (parentFocus == editable) {
                    isOrContainsFocus = true;
                }
                parentFocus = parentFocus.parentNode;
            }

            if (!isOrContainsAnchor || !isOrContainsFocus) {
                return;
            }

            selection = window.getSelection();

            // Get range (standards)
            if (selection.getRangeAt !== undefined) {
                range = selection.getRangeAt(0);

                // Get range (Safari 2)
            } else if (
                document.createRange &&
                selection.anchorNode &&
                selection.anchorOffset &&
                selection.focusNode &&
                selection.focusOffset
            ) {
                range = document.createRange();
                range.setStart(selection.anchorNode, selection.anchorOffset);
                range.setEnd(selection.focusNode, selection.focusOffset);
            } else {
                // Failure here, not handled by the rest of the script.
                // Probably IE or some older browser
            }
        };

        // Slight delay will avoid the initial selection
        // (at start or of contents depending on browser) being mistaken
        setTimeout(function() {
            var cursorStart = document.getElementById('cursorStart');
            var cursorEnd = document.getElementById('cursorEnd');

            // Don't do anything if user is creating a new selection
            if (editable.className.match(/\sselecting(\s|$)/)) {
                if (cursorStart) {
                    cursorStart.parentNode.removeChild(cursorStart);
                }
                if (cursorEnd) {
                    cursorEnd.parentNode.removeChild(cursorEnd);
                }
            } else if (cursorStart) {
                captureSelection();
                range = document.createRange();

                if (cursorEnd) {
                    range.setStartAfter(cursorStart);
                    range.setEndBefore(cursorEnd);

                    // Delete cursor markers
                    cursorStart.parentNode.removeChild(cursorStart);
                    cursorEnd.parentNode.removeChild(cursorEnd);

                    // Select range
                    selection.removeAllRanges();
                    selection.addRange(range);
                } else {
                    range.selectNode(cursorStart);

                    // Select range
                    selection.removeAllRanges();
                    selection.addRange(range);

                    // Delete cursor marker
                    document.execCommand('delete', false, null);
                }
            }

            // Register selection again
            captureSelection();
        }, 10);
    });
};
于 2013-08-17T08:12:29.013 に答える
0

o_div.childNodes[0]Firefox では、子ノード ( ) に div のテキストがある場合があります。

var range = document.createRange();

range.setStart(o_div.childNodes[0],last_caret_pos);
range.setEnd(o_div.childNodes[0],last_caret_pos);
range.collapse(false);

var sel = window.getSelection(); 
sel.removeAllRanges();
sel.addRange(range);
于 2011-06-30T08:14:21.527 に答える