Javascript 获取可编辑插入符号位置

Javascript 获取可编辑插入符号位置,javascript,contenteditable,caret,selection-api,Javascript,Contenteditable,Caret,Selection Api,关于如何在contentEditable元素中设置插入符号位置,我找到了大量优秀的跨浏览器答案,但对于如何首先获取插入符号位置,却没有一个答案 我想做的是知道keyup上div中插入符号的位置。因此,当用户键入文本时,我可以随时知道contentEditable元素中插入符号的位置 <div id="contentBox" contentEditable="true"></div> $('#contentbox').keyup(f

关于如何在
contentEditable
元素中设置插入符号位置,我找到了大量优秀的跨浏览器答案,但对于如何首先获取插入符号位置,却没有一个答案

我想做的是知道
keyup
上div中插入符号的位置。因此,当用户键入文本时,我可以随时知道
contentEditable
元素中插入符号的位置

<div id="contentBox" contentEditable="true"></div>

$('#contentbox').keyup(function() { 
    // ... ? 
});

$('#contentbox').keyup(函数(){
// ... ? 
});
注意:范围对象本身可以存储在变量中,并且可以随时重新选择,除非contenteditable div的内容发生更改

IE 8及更低版本的参考:

标准(所有其他)浏览器的参考:
(这是mozilla文档,但代码也适用于chrome、safari、opera和ie9)

以下代码假定:

  • 可编辑的
    中始终只有一个文本节点,没有其他节点
  • 可编辑的div没有将CSS
    空白
    属性设置为
    pre
如果需要更通用的方法来处理嵌套元素的内容,请尝试以下答案:

代码:

函数getCaretPosition(editableDiv){ var caretPos=0, sel,范围; if(window.getSelection){ sel=window.getSelection(); 如果(选择范围计数){ 范围=选择范围(0); if(range.commonAncestorContainer.parentNode==editableDiv){ caretPos=range.endOffset; } } }else if(document.selection&&document.selection.createRange){ range=document.selection.createRange(); if(range.parentElement()==editableDiv){ var tempEl=document.createElement(“span”); editableDiv.insertBefore(tempEl,editableDiv.firstChild); var tempRange=range.duplicate(); tempRange.moveToElementText(tempEl); setEndPoint(“EndToEnd”,range); caretPos=tempRange.text.length; } } 返回caretPos; }
#caretposition{
字体大小:粗体;
}

单击我并用键或鼠标移动光标
0
var update=函数(){
$('#caretposition').html(getCaretPosition(this));
};
$(“#contentbox”)。在(“mousedown mouseup keydown keydup”,update)上;
试试这个:

插入符号 从文本字段获取插入符号位置和偏移量

演示:

这个对我很有用:

函数getCaretCharOffset(元素){ var caretofset=0; if(window.getSelection){ var range=window.getSelection().getRangeAt(0); var precretange=range.cloneRange(); 预重排列。选择节点内容(元素); precretange.setEnd(range.endContainer,range.endOffset); CareTofset=precretange.toString().length; } else if(document.selection&&document.selection.type!=“控制”){ var textRange=document.selection.createRange(); var precarteTextRange=document.body.createTextRange(); PrecretTextRange.moveToElementText(元素); setEndPoint(“EndToEnd”,textRange); CareTofset=PrecretTextRange.text.length; } 返回caretofset; } //演示: var elm=document.querySelector(“[contenteditable]”); elm.addEventListener('click',printCaretPosition) elm.addEventListener('keydown',printCaretPosition) 函数printCaretPosition(){ log(getCaretCharOffset(elm),'length:',this.textContent.trim().length) }
此处一些文本斜体文本此处一些其他文本此处粗体文本此处文本结尾
$(“#可编辑”).on('keydown keydup mousedown mouseup',函数(e){
if($(window.getSelection().anchorNode).is($(this))){
$('#position').html('0'))
}否则{
$('#position').html(window.getSelection().anchorOffset);
}
});
正文{
填充:40px;
}
#可编辑{
高度:50px;
宽度:400px;
边框:1px实心#000;
}
#可编辑p{
保证金:0;
填充:0;
}

移动光标以查看位置
职位:

其他答案中没有提到的几个问题:

  • 元素可以包含多个级别的子节点(例如,具有子节点的子节点具有子节点…)
  • 选择可以包括不同的开始和结束位置(例如,选择多个字符)
  • 包含插入符号开始/结束的节点不能是元素或其直接子元素
  • 以下是一种将开始和结束位置作为元素textContent值偏移量的方法:

    // node_walk: walk the element tree, stop when func(node) returns false
    function node_walk(node, func) {
      var result = func(node);
      for(node = node.firstChild; result !== false && node; node = node.nextSibling)
        result = node_walk(node, func);
      return result;
    };
    
    // getCaretPosition: return [start, end] as offsets to elem.textContent that
    //   correspond to the selected portion of text
    //   (if start == end, caret is at given position and no text is selected)
    function getCaretPosition(elem) {
      var sel = window.getSelection();
      var cum_length = [0, 0];
    
      if(sel.anchorNode == elem)
        cum_length = [sel.anchorOffset, sel.extentOffset];
      else {
        var nodes_to_find = [sel.anchorNode, sel.extentNode];
        if(!elem.contains(sel.anchorNode) || !elem.contains(sel.extentNode))
          return undefined;
        else {
          var found = [0,0];
          var i;
          node_walk(elem, function(node) {
            for(i = 0; i < 2; i++) {
              if(node == nodes_to_find[i]) {
                found[i] = true;
                if(found[i == 0 ? 1 : 0])
                  return false; // all done
              }
            }
    
            if(node.textContent && !node.firstChild) {
              for(i = 0; i < 2; i++) {
                if(!found[i])
                  cum_length[i] += node.textContent.length;
              }
            }
          });
          cum_length[0] += sel.anchorOffset;
          cum_length[1] += sel.extentOffset;
        }
      }
      if(cum_length[0] <= cum_length[1])
        return cum_length;
      return [cum_length[1], cum_length[0]];
    }
    
    //node\u walk:遍历元素树,当func(node)返回false时停止
    功能节点_walk(节点,func){
    var结果=func(节点);
    对于(node=node.firstChild;result!==false&&node;node=node.nextSibling)
    结果=节点\行走(节点,func);
    返回结果;
    };
    //getCaretPosition:将[start,end]作为偏移量返回给
    //对应于文本的选定部分
    //(如果开始==结束,插入符号位于给定位置且未选择任何文本)
    函数getCaretPosition(元素){
    var sel=window.getSelection();
    var cum_length=[0,0];
    if(sel.anchorNode==elem)
    cum_length=[sel.anchorOffset,sel.extenstoffset];
    否则{
    var nodes_to_find=[sel.anchorNode,sel.extentNode];
    如果(!elem.contains(sel.anchorNode)| |!elem.contains(sel.extentNode))
    返回未定义;
    否则{
    发现的var=[0,0];
    var i;
    节点步行(要素、功能(节点){
    对于(i=0;i<2;i++){
    if(node==nodes\u to\u find[i]){
    发现[i]=正确;
    如果(找到[i==0?1:0])
    返回false;//全部完成
    }
    }
    if(node.textContent&!node.firstChild){
    对于(i=0;i<2;i++){
    如果(!找到[i])
    cum_length[i]+=node.textContent.length;
    }
    }
    });
    cum_长度[0]+=sel.anchorOffset;
    cum_长度[1]+=sel.EXTENTOFSET;
    }
    }
    
    如果(cum_length[0]有点晚到派对,但在案例a中
    function getCaretPosition() {
        var x = 0;
        var y = 0;
        var sel = window.getSelection();
        if(sel.rangeCount) {
            var range = sel.getRangeAt(0).cloneRange();
            if(range.getClientRects()) {
            range.collapse(true);
            var rect = range.getClientRects()[0];
            if(rect) {
                y = rect.top;
                x = rect.left;
            }
            }
        }
        return {
            x: x,
            y: y
        };
    }
    
    // node_walk: walk the element tree, stop when func(node) returns false
    function node_walk(node, func) {
      var result = func(node);
      for(node = node.firstChild; result !== false && node; node = node.nextSibling)
        result = node_walk(node, func);
      return result;
    };
    
    // getCaretPosition: return [start, end] as offsets to elem.textContent that
    //   correspond to the selected portion of text
    //   (if start == end, caret is at given position and no text is selected)
    function getCaretPosition(elem) {
      var sel = window.getSelection();
      var cum_length = [0, 0];
    
      if(sel.anchorNode == elem)
        cum_length = [sel.anchorOffset, sel.extentOffset];
      else {
        var nodes_to_find = [sel.anchorNode, sel.extentNode];
        if(!elem.contains(sel.anchorNode) || !elem.contains(sel.extentNode))
          return undefined;
        else {
          var found = [0,0];
          var i;
          node_walk(elem, function(node) {
            for(i = 0; i < 2; i++) {
              if(node == nodes_to_find[i]) {
                found[i] = true;
                if(found[i == 0 ? 1 : 0])
                  return false; // all done
              }
            }
    
            if(node.textContent && !node.firstChild) {
              for(i = 0; i < 2; i++) {
                if(!found[i])
                  cum_length[i] += node.textContent.length;
              }
            }
          });
          cum_length[0] += sel.anchorOffset;
          cum_length[1] += sel.extentOffset;
        }
      }
      if(cum_length[0] <= cum_length[1])
        return cum_length;
      return [cum_length[1], cum_length[0]];
    }
    
    const getSelectionCaretAndLine = () => {
        // our editable div
        const editable = document.getElementById('editable');
    
        // collapse selection to end
        window.getSelection().collapseToEnd();
    
        const sel = window.getSelection();
        const range = sel.getRangeAt(0);
    
        // get anchor node if startContainer parent is editable
        let selectedNode = editable === range.startContainer.parentNode
          ? sel.anchorNode 
          : range.startContainer.parentNode;
    
        if (!selectedNode) {
            return {
                caret: -1,
                line: -1,
            };
        }
    
        // select to top of editable
        range.setStart(editable.firstChild, 0);
    
        // do not use 'this' sel anymore since the selection has changed
        const content = window.getSelection().toString();
        const text = JSON.stringify(content);
        const lines = (text.match(/\\n/g) || []).length + 1;
    
        // clear selection
        window.getSelection().collapseToEnd();
    
        // minus 2 because of strange text formatting
        return {
            caret: text.length - 2, 
            line: lines,
        }
    } 
    
    function caretPositionIndex() {
        const range = window.getSelection().getRangeAt(0);
        const { endContainer, endOffset } = range;
    
        // get contenteditableDiv from our endContainer node
        let contenteditableDiv;
        const contenteditableSelector = "div[contenteditable]";
        switch (endContainer.nodeType) {
          case Node.TEXT_NODE:
            contenteditableDiv = endContainer.parentElement.closest(contenteditableSelector);
            break;
          case Node.ELEMENT_NODE:
            contenteditableDiv = endContainer.closest(contenteditableSelector);
            break;
        }
        if (!contenteditableDiv) return '';
    
    
        const countBeforeEnd = countUntilEndContainer(contenteditableDiv, endContainer);
        if (countBeforeEnd.error ) return null;
        return countBeforeEnd.count + endOffset;
    
        function countUntilEndContainer(parent, endNode, countingState = {count: 0}) {
          for (let node of parent.childNodes) {
            if (countingState.done) break;
            if (node === endNode) {
              countingState.done = true;
              return countingState;
            }
            if (node.nodeType === Node.TEXT_NODE) {
              countingState.count += node.length;
            } else if (node.nodeType === Node.ELEMENT_NODE) {
              countUntilEndContainer(node, endNode, countingState);
            } else {
              countingState.error = true;
            }
          }
          return countingState;
        }
      }
    
    function countUntilEndContainer(parent, endNode, offset, countingState = {count: 0}) {
        for (let node of parent.childNodes) {
            if (countingState.done) break;
            if (node === endNode) {
                countingState.done = true;
                countingState.offsetInNode = offset;
                return countingState;
            }
            if (node.nodeType === Node.TEXT_NODE) {
                countingState.offsetInNode = offset;
                countingState.count += node.length;
            } else if (node.nodeType === Node.ELEMENT_NODE) {
                countUntilEndContainer(node, endNode, offset, countingState);
            } else {
                countingState.error = true;
            }
        }
        return countingState;
    }
    
    function countUntilOffset(parent, offset, countingState = {count: 0}) {
        for (let node of parent.childNodes) {
            if (countingState.done) break;
            if (node.nodeType === Node.TEXT_NODE) {
                if (countingState.count <= offset && offset < countingState.count + node.length)
                {
                    countingState.offsetInNode = offset - countingState.count;
                    countingState.node = node; 
                    countingState.done = true; 
                    return countingState; 
                }
                else { 
                    countingState.count += node.length; 
                }
            } else if (node.nodeType === Node.ELEMENT_NODE) {
                countUntilOffset(node, offset, countingState);
            } else {
                countingState.error = true;
            }
        }
        return countingState;
    }
    
    function getCaretPosition()
    {
        let editor = document.getElementById('editor');
        let sel = window.getSelection();
        if (sel.rangeCount === 0) { return null; }
        let range = sel.getRangeAt(0);    
        let start = countUntilEndContainer(editor, range.startContainer, range.startOffset);
        let end = countUntilEndContainer(editor, range.endContainer, range.endOffset);
        let offsetsCounts = { start: start.count + start.offsetInNode, end: end.count + end.offsetInNode };
        let offsets = { start: start, end: end, offsets: offsetsCounts };
        return offsets;
    }
    
    function setCaretPosition(start, end)
    {
        let editor = document.getElementById('editor');
        let sel = window.getSelection();
        if (sel.rangeCount === 0) { return null; }
        let range = sel.getRangeAt(0);
        let startNode = countUntilOffset(editor, start);
        let endNode = countUntilOffset(editor, end);
        let newRange = new Range();
        newRange.setStart(startNode.node, startNode.offsetInNode);
        newRange.setEnd(endNode.node, endNode.offsetInNode);
        sel.removeAllRanges();
        sel.addRange(newRange);
        return true;
    }
    
    private getCaretPosition() {
       let caretRevCount = 0;
       if (window.getSelection) {
          const selection = window.getSelection();
          const currentNode = selection.focusNode.parentNode;
          caretRevCount = selection.focusOffset;
          let previousNode = currentNode.previousSibling;
          while(previousNode && previousNode.nodeName === 'SPAN') { 
          // you can check specific element
          caretRevCount += previousNode.textContent.length;
          previousNode = previousNode.previousSibling;
          }
        }
        return caretRevCount;
    }