代码之家  ›  专栏  ›  技术社区  ›  GONeale

在contentEditable<div>

  •  137
  • GONeale  · 技术社区  · 17 年前

    我想要的是一个明确的跨浏览器解决方案,即当contentEditable='on'<部门>重新聚焦。内容可编辑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>
    

    How to move cursor to end of contenteditable entity )

    8 回复  |  直到 9 年前
        1
  •  100
  •   zmo    13 年前

    此解决方案适用于所有主要浏览器:

    saveSelection() 附在 onmouseup onkeyup 事件并将所选内容保存到变量 savedRange .

    restoreSelection() 附在 onfocus 萨维德兰奇 .

    为了实现这一点 onclick onmousedown 事件由函数取消 cancelEvent() 这是用于取消事件的跨浏览器功能。这个 cancelEvent() 函数还运行 恢复选择()

    变量 isInFocus 存储它是否处于焦点并更改为“false” onblur “真的” 聚焦事件 . 这仅允许在div未处于焦点时取消单击事件(否则您将根本无法更改选择)。

    如果您希望在通过单击聚焦div时更改选择,而不是恢复选择 onclick document.getElementById("area").focus(); 或类似,然后只需移除 昂穆斯敦 事件 事件和 onDivBlur()

    <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>
    
        2
  •  59
  •   eyelidlessness    17 年前

    这与基于标准的浏览器兼容,但在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);
    };
    
        3
  •  20
  •   Community Mohan Dere    9 年前

    更新

    Rangy 这包含了我在下面发布的代码的改进版本。你可以使用 selection save and restore module @Nico Burns's answer 如果您没有对项目中的选择执行任何其他操作,并且不需要大量的库。

    先前的答复

    http://code.google.com/p/ierange/ )将IE的TextRange转换为类似DOM的范围,并将其与类似Epiless's Start point的内容结合使用。就我个人而言,我只会使用iRange的算法来计算范围<-&燃气轮机;文本范围转换,而不是使用整个内容。IE的选择对象没有focusNode和anchorNode属性,但您应该能够使用从选择中获得的Range/TextRange。

    我可能会把一些东西放在一起做这件事,如果我这样做了,我会在这里发帖。

    编辑:

    我已经创建了一个脚本的演示。到目前为止,除了Opera 9中的一个bug,它在我尝试过的所有东西中都有效,我还没有时间去研究它。它使用的浏览器有IE 5.5、6和7、Chrome 2、Firefox 2、3和3.5以及Safari 4,所有这些都在Windows上。

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

    请注意,可以在浏览器中向后进行选择,以使焦点节点位于选择的开始位置,并且点击向右或向左光标键将插入符号移动到相对于选择开始位置的位置。我认为在恢复选择时不可能复制这个,因此焦点节点始终位于选择的末尾。

    我将在不久的某个时候完整地写下来。

        4
  •  16
  •   Zane Claes    13 年前

    我有一个相关的情况,我特别需要将光标位置设置到contenteditable div的末尾。我不想使用像Rangy这样成熟的库,而且很多解决方案都太重了。

    最后,我提出了一个简单的jQuery函数,将克拉位置设置为contenteditable 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;
    }
    

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

    就这样!

        5
  •  7
  •   Gatsbimantico    11 年前

    我接受了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>
        6
  •  5
  •   mkaj    12 年前

    玩过之后,我修改了上面的Epilloss的答案,并将其作为一个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);
        });
    };
    
        7
  •  3
  •   zoonman    8 年前

    你可以利用 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();
    
        8
  •  0
  •   akjoshi HCP    13 年前

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