簡體   English   中英

在contentEditable <div>上設置光標位置

[英]Set cursor position on contentEditable <div>

我正在尋找一個明確的跨瀏覽器解決方案,當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>。 也許只適用於textarea( 如何將光標移動到可信實體的末尾

此解決方案適用於所有主流瀏覽器:

saveSelection()附加到div的onmouseuponkeyup事件,並將選擇保存到變量savedRange

restoreSelection()附加到div的onfocus事件,並重新選擇保存在savedRange的選擇。

這非常有效,除非您希望在用戶單擊div時恢復選擇(這與您通常希望光標移動到您單擊的位置但包含完整性的代碼一樣有點不直觀)

為實現此目的, onclickonmousedown事件由cancelEvent()函數取消,該函數是用於取消事件的跨瀏覽器函數。 cancelEvent()函數還運行restoreSelection()函數,因為當click事件被取消時,div不會獲得焦點,因此除非運行此函數,否則根本不會選擇任何內容。

變量isInFocus存儲它是否處於焦點並且更改為“false” onblur和“true” onfocus 這允許僅在div未處於焦點時取消單擊事件(否則您將無法更改選擇)。

如果您希望在通過單擊聚焦div時更改選擇,而不是恢復選擇onclick (並且僅當使用document.getElementById("area").focus();以編程方式對元素進行聚焦時.focus document.getElementById("area").focus();或者類似的,只需刪除onclickonmousedown事件。在這些情況下,也可以安全地刪除onblur事件和onDivBlur()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>

這與基於標准的瀏覽器兼容,但在IE中可能會失敗。 我提供它作為起點。 IE不支持DOM Range。

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

更新

我編寫了一個名為Rangy的跨瀏覽器范圍和選擇庫,其中包含我在下面發布的代碼的改進版本。 你可以使用選擇保存和恢復模塊來解決這個問題,盡管如果你在項目中沒有做任何其他選擇而且不需要大量的東西,我會想要使用類似@Nico Burns的答案 。圖書館。

以前的答案

您可以使用IERange( http://code.google.com/p/ierange/ )將IE的TextRange轉換為類似DOM范圍的內容,並將其與eyelidlessness的起點結合使用。 就個人而言,我只會使用IERange中執行Range < - > TextRange轉換的算法,而不是使用整個事物。 IE的選擇對象沒有focusNode和anchorNode屬性,但您應該只能使用從選擇中獲取的Range / TextRange。

我可能會把一些東西放在一起做這件事,如果我這樣做,我會在這里回復。

編輯:

我已經創建了一個腳本演示來實現這一點。 它適用於我迄今為止嘗試過的所有內容,除了Opera 9中的一個錯誤,我還沒來得及查看。 它適用的瀏覽器是IE 5.5,6和7,Chrome 2,Firefox 2,3和3.5,以及Safari 4,所有這些都在Windows上。

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

注意,可以在瀏覽器中向后進行選擇,使得焦點節點位於選擇的開始處,並且點擊右或左光標鍵將把插入符移動到相對於選擇開始的位置。 我不認為在恢復選擇時可以復制它,因此焦點節點始終位於選擇的末尾。

我很快就會寫完這篇文章。

我有一個相關的情況,我特別需要將光標位置設置為一個contenteditable div的END。 我不想使用像Rangy這樣的完整的圖書館,而且很多解決方案都太重量級了。

最后,我想出了這個簡單的jQuery函數,將克拉位置設置為一個可信的div的末尾:

$.fn.focusEnd = function() {
    $(this).focus();
    var tmp = $('<span />').appendTo($(this)),
        node = tmp.get(0),
        range = null,
        sel = null;

    if (document.selection) {
        range = document.body.createTextRange();
        range.moveToElementText(node);
        range.select();
    } else if (window.getSelection) {
        range = document.createRange();
        range.selectNode(node);
        sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    }
    tmp.remove();
    return this;
}

理論很簡單:在可編輯的末尾添加一個跨度,選擇它,然后刪除跨度 - 讓我們在div的末尾添加一個光標。 您可以調整此解決方案以在任何位置插入跨度,從而將光標放在特定位置。

用法很簡單:

$('#editable').focusEnd();

而已!

我接受了Nico Burns的回答並使用jQuery創建了它:

  • 通用:對於每個div contentEditable="true"

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> 

玩完之后我修改了上面的eyelidlessness回答,並把它變成了一個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);
    });
};

您可以利用現代瀏覽器支持的selectNodeContents

var el = document.getElementById('idOfYoursContentEditable');
var selection = window.getSelection();
var range = document.createRange();
selection.removeAllRanges();
range.selectNodeContents(el);
range.collapse(false);
selection.addRange(range);
el.focus();

在Firefox中,您可能在子節點中具有div的文本( o_div.childNodes[0]

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM