javascript 在 contenteditable div 中获取插入符号索引,包括标签

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16736680/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 05:47:41  来源:igfitidea点击:

Get caret index in contenteditable div including tags

javascripthtmlcontenteditablegetcaretpos

提问by helldrain

I have a contentEditabledivin which I have multiple tags (br, b, u, i) and text.

我有一个contentEditablediv,其中有多个标签(brbui)和文本。

I need to get the caret index position relative to the div, including all the tags.

我需要获取相对于 div 的插入符号索引位置,包括所有标签。

For example:

例如:

<div id="h" contenteditable="true">abc<b>def<br>ghi</b>jkl</div>

If the cursor is between gand h, I need the caret index position to be 14. The problem is that the found methods that use a treeWalkerdo not work in this case. The bold tag is not found... probably because it isn't closed. Also I have tried several methods but still no luck.

如果光标在g和之间h,我需要插入符号索引位置为14。问题是找到的使用 a 的方法treeWalker在这种情况下不起作用。未找到粗体标记...可能是因为它没有关闭。我也尝试了几种方法,但仍然没有运气。

I need it to work in Firefox. Thank you.

我需要它在Firefox 中工作。谢谢你。

回答by Eric McCormick

Have you tried this? Get a range's start and end offset's relative to its parent container

你试过这个吗? 获取范围相对于其父容器的开始和结束偏移量

Direct link to the jsfiddle: https://jsfiddle.net/TjXEG/1/

jsfiddle 的直接链接:https://jsfiddle.net/TjXEG/1/

Function code:

功能代码:

function getCaretCharacterOffsetWithin(element) {
    var caretOffset = 0;
    if (typeof window.getSelection != "undefined") {
        var range = window.getSelection().getRangeAt(0);
        var preCaretRange = range.cloneRange();
        preCaretRange.selectNodeContents(element);
        preCaretRange.setEnd(range.endContainer, range.endOffset);
        caretOffset = preCaretRange.toString().length;
    } else if (typeof document.selection != "undefined" && document.selection.type != "Control") {
        var textRange = document.selection.createRange();
        var preCaretTextRange = document.body.createTextRange();
        preCaretTextRange.moveToElementText(element);
        preCaretTextRange.setEndPoint("EndToEnd", textRange);
        caretOffset = preCaretTextRange.text.length;
    }
    return caretOffset;
}

function showCaretPos() {
    var el = document.getElementById("test");
    var caretPosEl = document.getElementById("caretPos");
    caretPosEl.innerHTML = "Caret position: " + getCaretCharacterOffsetWithin(el);
}

document.body.onkeyup = showCaretPos;
document.body.onmouseup = showCaretPos;

回答by YangombiUmpakati

just had to do this so there is some working solution (some testing may be required)

只是必须这样做,所以有一些可行的解决方案(可能需要进行一些测试)

basic idea is to:

基本思想是:

  1. get textContent position using this method: Get caret (cursor) position in contentEditable area containing HTML content

  2. iterate through innerHTML of an element to the textContent position

  3. if html tag or entity is encountered, iterate through it until normal char, then continue

  1. 使用此方法获取 textContent 位置:在包含 HTML 内容的 contentEditable 区域中获取插入符号(光标)位置

  2. 遍历元素的 innerHTML 到 textContent 位置

  3. 如果遇到 html 标签或实体,遍历它直到正常字符,然后继续

sample code here:

示例代码在这里:

function getCaretPosition (node) {
    var range = window.getSelection().getRangeAt(0),
        preCaretRange = range.cloneRange(),
        caretPosition,
        tmp = document.createElement("div");

    preCaretRange.selectNodeContents(node);
    preCaretRange.setEnd(range.endContainer, range.endOffset);
    tmp.appendChild(preCaretRange.cloneContents());
    caretPosition = tmp.innerHTML.length;
    return caretPosition;
}

function getHTMLCaretPosition(element) {
var textPosition = getCaretPosition(element),
    htmlContent = element.innerHTML,
    textIndex = 0,
    htmlIndex = 0,
    insideHtml = false,
    htmlBeginChars = ['&', '<'],
    htmlEndChars = [';', '>'];


if (textPosition == 0) {
  return 0;
}

while(textIndex < textPosition) {

  htmlIndex++;

  // check if next character is html and if it is, iterate with htmlIndex to the next non-html character
  while(htmlBeginChars.indexOf(htmlContent.charAt(htmlIndex)) > -1) {
    // console.log('encountered HTML');
    // now iterate to the ending char
    insideHtml = true;

    while(insideHtml) {
      if (htmlEndChars.indexOf(htmlContent.charAt(htmlIndex)) > -1) {
        if (htmlContent.charAt(htmlIndex) == ';') {
          htmlIndex--; // entity is char itself
        }
        // console.log('encountered end of HTML');
        insideHtml = false;
      }
      htmlIndex++;
    }
  }
  textIndex++;
}

//console.log(htmlIndex);
//console.log(textPosition);
// in htmlIndex is caret position inside html
return htmlIndex;
}