Get a range's start and end offset's relative to its parent container
假设我有这个HTML元素:
1 2 3 4 | Hello everyone! This is my home page <p> Bye! </p> |
并且用户用他的鼠标选择"家"。
我希望能够确定他的选择开始到
更新
正如评论中指出的那样,我的原始答案(如下)仅返回选择的结尾或插入符号的位置。修改代码以返回开始和结束偏移量是很容易的。这是一个这样做的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | function getSelectionCharacterOffsetWithin(element) { var start = 0; var end = 0; var doc = element.ownerDocument || element.document; var win = doc.defaultView || doc.parentWindow; var sel; if (typeof win.getSelection !="undefined") { sel = win.getSelection(); if (sel.rangeCount > 0) { var range = win.getSelection().getRangeAt(0); var preCaretRange = range.cloneRange(); preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.startContainer, range.startOffset); start = preCaretRange.toString().length; preCaretRange.setEnd(range.endContainer, range.endOffset); end = preCaretRange.toString().length; } } else if ( (sel = doc.selection) && sel.type !="Control") { var textRange = sel.createRange(); var preCaretTextRange = doc.body.createTextRange(); preCaretTextRange.moveToElementText(element); preCaretTextRange.setEndPoint("EndToStart", textRange); start = preCaretTextRange.text.length; preCaretTextRange.setEndPoint("EndToEnd", textRange); end = preCaretTextRange.text.length; } return { start: start, end: end }; } function reportSelection() { var selOffsets = getSelectionCharacterOffsetWithin( document.getElementById("editor") ); document.getElementById("selectionLog").innerHTML ="Selection offsets:" + selOffsets.start +"," + selOffsets.end; } window.onload = function() { document.addEventListener("selectionchange", reportSelection, false); document.addEventListener("mouseup", reportSelection, false); document.addEventListener("mousedown", reportSelection, false); document.addEventListener("keyup", reportSelection, false); }; |
1 2 3 4 | #editor { padding: 5px; border: solid green 1px; } |
1 2 3 | Select something in the content below: A wombat is a marsupial native to Australia |
这是一个函数,将获取指定元素内插入符号的字符偏移量;但是,这是一个幼稚的实现,几乎肯定会与换行符不一致,并且不会尝试处理通过CSS隐藏的文本(我怀疑IE会正确地忽略此类文本,而其他浏览器则不会)。正确处理所有这些东西将很棘手。我现在已经在我的Rangy库中尝试过它。
实时示例:http://jsfiddle.net/TjXEG/900/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | function getCaretCharacterOffsetWithin(element) { var caretOffset = 0; var doc = element.ownerDocument || element.document; var win = doc.defaultView || doc.parentWindow; var sel; if (typeof win.getSelection !="undefined") { sel = win.getSelection(); if (sel.rangeCount > 0) { var range = win.getSelection().getRangeAt(0); var preCaretRange = range.cloneRange(); preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.endContainer, range.endOffset); caretOffset = preCaretRange.toString().length; } } else if ( (sel = doc.selection) && sel.type !="Control") { var textRange = sel.createRange(); var preCaretTextRange = doc.body.createTextRange(); preCaretTextRange.moveToElementText(element); preCaretTextRange.setEndPoint("EndToEnd", textRange); caretOffset = preCaretTextRange.text.length; } return caretOffset; } |
我知道这已经一岁了,但是这篇文章是关于找到Caret职位的许多问题的最佳搜索结果,我发现这很有用。
在内容可编辑div中将元素从一个位置拖放到另一个位置后,我试图使用上述Tim的出色脚本来查找新的光标位置。它在FF和IE中完美运行,但在Chrome中,拖动动作会突出显示拖动开始和结束之间的所有内容,从而导致返回的
我在第一条if语句中添加了几行,以检查是否已选择文本并相应地调整结果。新的声明如下。原谅我在此处添加此内容是否不合适,因为这不是OP想要做的,但是正如我所说,对与Caret职位相关的信息进行的多次搜索将我带到了这篇文章,因此(希望)可能会帮助其他人。
蒂姆的第一条if语句,增加了行(*):
1 2 3 4 5 6 7 8 9 10 11 12 13 | if (typeof window.getSelection !="undefined") { var range = window.getSelection().getRangeAt(0); var selected = range.toString().length; // * var preCaretRange = range.cloneRange(); preCaretRange.selectNodeContents(element); preCaretRange.setEnd(range.endContainer, range.endOffset); if(selected){ // * caretOffset = preCaretRange.toString().length - selected; // * } else { // * caretOffset = preCaretRange.toString().length; } // * } |
经过几天的试验,我发现了一种很有前途的方法。因为
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 | var editor = null; var output = null; const getTextSelection = function (editor) { const selection = window.getSelection(); if (selection != null && selection.rangeCount > 0) { const range = selection.getRangeAt(0); return { start: getTextLength(editor, range.startContainer, range.startOffset), end: getTextLength(editor, range.endContainer, range.endOffset) }; } else return null; } const getTextLength = function (parent, node, offset) { var textLength = 0; if (node.nodeName == '#text') textLength += offset; else for (var i = 0; i < offset; i++) textLength += getNodeTextLength(node.childNodes[i]); if (node != parent) textLength += getTextLength(parent, node.parentNode, getNodeOffset(node)); return textLength; } const getNodeTextLength = function (node) { var textLength = 0; if (node.nodeName == 'BR') textLength = 1; else if (node.nodeName == '#text') textLength = node.nodeValue.length; else if (node.childNodes != null) for (var i = 0; i < node.childNodes.length; i++) textLength += getNodeTextLength(node.childNodes[i]); return textLength; } const getNodeOffset = function (node) { return node == null ? -1 : 1 + getNodeOffset(node.previousSibling); } window.onload = function () { editor = document.querySelector('.editor'); output = document.querySelector('#output'); document.addEventListener('selectionchange', handleSelectionChange); } const handleSelectionChange = function () { if (isEditor(document.activeElement)) { const textSelection = getTextSelection(document.activeElement); if (textSelection != null) { const text = document.activeElement.innerText; const selection = text.slice(textSelection.start, textSelection.end); print(`Selection: [${selection}] (Start: ${textSelection.start}, End: ${textSelection.end})`); } else print('Selection is null!'); } else print('Select some text above'); } const isEditor = function (element) { return element != null && element.classList.contains('editor'); } const print = function (message) { if (output != null) output.innerText = message; else console.log('output is null!'); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | * { font-family: 'Georgia', sans-serif; padding: 0; margin: 0; } body { margin: 16px; } .p { font-size: 16px; line-height: 24px; padding: 0 2px; } .editor { border: 1px solid #0000001e; border-radius: 2px; white-space: pre-wrap; } #output { margin-top: 16px; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <script src="./script.js" async> <link href="./stylesheet.css" rel="stylesheet"> Caret Position </head> <body> <p class="editor" contenteditable="true">Writesome awesome text here... </p> <p id="output">Select some text above </p> </body> </html> |