首页 文章

如何在contenteditable元素(div)中设置插入符号(光标)?

提问于
浏览
143

我以这个简单的HTML为例:

<div id="editable" contenteditable="true">
  text text text<br>
  text text text<br>
  text text text<br>
</div>
<button id="button">focus</button>

我想要简单的事情 - 当我点击按钮时,我想将插入符号(光标)放入可编辑div中的特定位置 . 通过网络搜索,我将这个JS附加到按钮点击,但它不起作用(FF,Chrome):

var range = document.createRange();
var myDiv = document.getElementById("editable");
range.setStart(myDiv, 5);
range.setEnd(myDiv, 5);

是否可以像这样设置手动插入位置?

5 回答

  • 36

    当你有像(p)(span)等高级元素时,它是非常难以设置的插入符号 . 目标是得到(对象文本):

    <div id="editable" contenteditable="true">dddddddddddddddddddddddddddd<p>dd</p>psss<p>dd</p><p>dd</p>
        <p>text text text</p>
         </div>
        <p id='we'></p>
        <button onclick="set_mouse()">focus</button>
         <script>
    
    function set_mouse() {
        var as = document.getElementById("editable");
        el=as.childNodes[1].childNodes[0];//goal is to get ('we') id to write (object Text) because it work only in object text
      var range = document.createRange();
         var sel = window.getSelection();
    range.setStart(el, 1);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    
    document.getElementById("we").innerHTML=el;// see out put of we id
    }
    </script>
    
  • 2

    在大多数浏览器中,您需要RangeSelection对象 . 您将每个选择边界指定为节点和该节点内的偏移量 . 例如,要将插入符号设置为第二行文本的第五个字符,您需要执行以下操作:

    var el = document.getElementById("editable");
    var range = document.createRange();
    var sel = window.getSelection();
    range.setStart(el.childNodes[2], 5);
    range.collapse(true);
    sel.removeAllRanges();
    sel.addRange(range);
    

    IE <9的工作方式完全不同 . 如果您需要支持这些浏览器,则需要不同的代码 .

    jsFiddle示例:http://jsfiddle.net/timdown/vXnCM/

  • 2

    您在可信任的光标定位上找到的大多数答案都相当简单,因为它们只能满足普通文本的输入 . 一旦在容器中使用html元素,输入的文本就会被分割成节点并在树结构中自由分布 .

    要设置光标位置,我有这个函数循环所提供节点内的所有子文本节点,并设置从初始节点的开始到chars.count字符的范围:

    function createRange(node, chars, range) {
        if (!range) {
            range = document.createRange()
            range.selectNode(node);
            range.setStart(node, 0);
        }
    
        if (chars.count === 0) {
            range.setEnd(node, chars.count);
        } else if (node && chars.count >0) {
            if (node.nodeType === Node.TEXT_NODE) {
                if (node.textContent.length < chars.count) {
                    chars.count -= node.textContent.length;
                } else {
                    range.setEnd(node, chars.count);
                    chars.count = 0;
                }
            } else {
               for (var lp = 0; lp < node.childNodes.length; lp++) {
                    range = createRange(node.childNodes[lp], chars, range);
    
                    if (chars.count === 0) {
                        break;
                    }
                }
            }
        } 
    
        return range;
    };
    

    然后我用这个函数调用例程:

    function setCurrentCursorPosition(chars) {
        if (chars >= 0) {
            var selection = window.getSelection();
    
            range = createRange(document.getElementById("test").parentNode, { count: chars });
    
            if (range) {
                range.collapse(false);
                selection.removeAllRanges();
                selection.addRange(range);
            }
        }
    };
    

    range.collapse(false)将光标设置为范围的结尾 . 我已经使用最新版本的Chrome,IE,Mozilla和Opera进行了测试,它们都运行良好 .

    PS . 如果有人有兴趣,我会使用以下代码获取当前光标位置:

    function isChildOf(node, parentId) {
        while (node !== null) {
            if (node.id === parentId) {
                return true;
            }
            node = node.parentNode;
        }
    
        return false;
    };
    
    function getCurrentCursorPosition(parentId) {
        var selection = window.getSelection(),
            charCount = -1,
            node;
    
        if (selection.focusNode) {
            if (isChildOf(selection.focusNode, parentId)) {
                node = selection.focusNode; 
                charCount = selection.focusOffset;
    
                while (node) {
                    if (node.id === parentId) {
                        break;
                    }
    
                    if (node.previousSibling) {
                        node = node.previousSibling;
                        charCount += node.textContent.length;
                    } else {
                         node = node.parentNode;
                         if (node === null) {
                             break
                         }
                    }
               }
          }
       }
    
        return charCount;
    };
    

    代码与set函数相反 - 它获取当前window.getSelection() . focusNode和focusOffset,并向后计算遇到的所有文本字符,直到它到达id为containerId的父节点 . isChildOf函数只是在运行之前检查所提供的节点实际上是所提供的parentId的子节点 .

    代码应该可以直接工作而不需要更改,但我刚刚从我开发的jQuery插件中获取它,所以已经破解了一些这样的 - 让我知道如果有什么不起作用!

  • 0

    如果您不想使用jQuery,可以尝试这种方法:

    public setCaretPosition() {
        const editableDiv = document.getElementById('contenteditablediv');
        const lastLine = this.input.nativeElement.innerHTML.replace(/.*?(<br>)/g, '');
        const selection = window.getSelection();
        selection.collapse(editableDiv.childNodes[editableDiv.childNodes.length - 1], lastLine.length);
    }
    

    editableDiv 你可编辑的元素,别忘了为它设置一个 id . 然后你需要从元素中获取 innerHTML 并切断所有制动线 . 然后用下一个参数设置崩溃 .

  • 201

    我正在编写语法高亮显示器(和基本代码编辑器),我需要知道如何自动键入单引号字符并将插入符号移回(就像现在的许多代码编辑器一样) .

    这是我的解决方案的片段,感谢这个主题,MDN文档以及很多moz控制台观看的大量帮助 .

    //onKeyPress event
    
    if (evt.key === "\"") {
        let sel = window.getSelection();
        let offset = sel.focusOffset;
        let focus = sel.focusNode;
    
        focus.textContent += "\""; //setting div's innerText directly creates new
        //nodes, which invalidate our selections, so we modify the focusNode directly
    
        let range = document.createRange();
        range.selectNode(focus);
        range.setStart(focus, offset);
    
        range.collapse(true);
        sel.removeAllRanges();
        sel.addRange(range);
    }
    
    //end onKeyPress event
    

    这是一个令人满意的div元素

    我把它留在这里作为感谢,意识到已经有一个公认的答案 .

相关问题