Angenommen, ich habe dieses HTML-Element:
<div id="parent">
Hello everyone! <a>This is my home page</a>
<p>Bye!</p>
</div>
Und der Benutzer wählt mit seiner Maus "Zuhause".
Ich möchte bestimmen können, wie viele Zeichen in #parent
seiner Auswahl beginnen (und wie viele Zeichen am Ende #parent
seiner Auswahl enden). Dies sollte auch dann funktionieren, wenn er ein HTML-Tag auswählt. (Und ich brauche es, um in allen Browsern zu funktionieren)
range.startOffset
sieht vielversprechend aus, ist jedoch nur ein Versatz relativ zum unmittelbaren Container des Bereichs und nur dann ein Zeichenversatz, wenn der Container ein Textknoten ist.
javascript
textrange
Tom Lehman
quelle
quelle
#parent
, enthält seine Auswahl einige HTML-Tags (<a> und <p>)Antworten:
AKTUALISIEREN
Wie in den Kommentaren erwähnt, gibt meine ursprüngliche Antwort (unten) nur das Ende der Auswahl oder die Caret-Position zurück. Es ist ziemlich einfach, den Code anzupassen, um einen Start- und einen Endversatz zurückzugeben. Hier ist ein Beispiel dafür:
Code-Snippet anzeigen
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); };
#editor { padding: 5px; border: solid green 1px; }
Select something in the content below: <div id="editor" contenteditable="true">A <i>wombat</i> is a marsupial native to <b>Australia</b></div> <div id="selectionLog"></div>
Hier ist eine Funktion, mit der der Zeichenversatz des Carets innerhalb des angegebenen Elements ermittelt wird. Dies ist jedoch eine naive Implementierung, die mit ziemlicher Sicherheit Inkonsistenzen mit Zeilenumbrüchen aufweist und keinen Versuch unternimmt, mit über CSS verborgenem Text umzugehen (ich vermute, dass der IE diesen Text korrekt ignoriert, während andere Browser dies nicht tun). Es wäre schwierig, mit all diesen Dingen richtig umzugehen. Ich habe es jetzt für meine Rangy- Bibliothek versucht .
Live-Beispiel: http://jsfiddle.net/TjXEG/900/
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; }
quelle
tinyMCE.execCommand('mceInsertContent', false, newContent);
den ich hier gefunden habe . Aber +1 für die Hilfe!win.getSelection().rangeCount > 0
vor dem Ausführen eine Prüfung hinzuzufügenwin.getSelection().getRangeAt(0)
, um Fehler zu vermeiden, wie unter stackoverflow.com/questions/22935320/… beschrieben . Ich bin selbst auf das Problem gestoßen, der RangeCount-Check hat es behobenelement.document
war für IE 5 und 5.5.Ich weiß, dass dies ein Jahr alt ist, aber dieser Beitrag ist ein Top-Suchergebnis für viele Fragen zum Finden der Caret-Position und ich fand dies nützlich.
Ich habe versucht, Tims hervorragendes Skript oben zu verwenden, um die neue Cursorposition zu finden, nachdem ich ein Element in einem inhaltsbearbeitbaren Div von einer Position zur anderen gezogen habe. In FF und IE funktionierte es perfekt, aber in Chrome wurden beim Ziehen alle Inhalte zwischen dem Beginn und dem Ende des Ziehens hervorgehoben, was dazu führte, dass die Rückgabe
caretOffset
zu groß oder zu klein war (gemessen an der Länge des ausgewählten Bereichs).Ich habe der ersten if-Anweisung einige Zeilen hinzugefügt, um zu überprüfen, ob Text ausgewählt wurde, und das Ergebnis entsprechend anzupassen. Die neue Aussage ist unten. Verzeihen Sie mir, wenn es unangemessen ist, dies hier hinzuzufügen, da es nicht das ist, was das OP versucht hat, aber wie gesagt, mehrere Suchanfragen nach Informationen in Bezug auf die Caret-Position haben mich zu diesem Beitrag geführt, sodass es (hoffentlich) wahrscheinlich jemand anderem hilft .
Tims erste if-Anweisung mit hinzugefügten Zeilen (*):
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; } // * }
quelle
start
Zeichenversatz undstop
Zeichenversatz) einfügen und das Wort hervorheben kann. Bitte beraten.selected
bevor Sie ihn subtrahieren? Wenn es 0 ist, ändern Sie das nicht,caretOffset
indem Sie 0 subtrahieren, oder?null
gleichbedeutend mit der Verwendung0
(aber nicht sicher, ob dies in allen Javascript-Engines zutrifft). :)Nachdem ich einige Tage experimentiert hatte, fand ich einen Ansatz, der vielversprechend aussieht. Da
selectNodeContents()
nicht verarbeitet<br>
Tags korrekt, schrieb ich einen benutzerdefinierten Algorithmus , um die Textlänge jeder bestimmennode
innerhalb eincontenteditable
. Um zB den Auswahlstart zu berechnen, fasse ich die Textlängen aller vorhergehenden Knoten zusammen. Auf diese Weise kann ich (mehrere) Zeilenumbrüche verarbeiten:Code-Snippet anzeigen
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!'); }
* { 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; }
<!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></script> <link href="./stylesheet.css" rel="stylesheet"> <title>Caret Position</title> </head> <body> <p class="editor" contenteditable="true"><em>Write<br></em><br>some <br>awesome <b><em>text </em></b>here...</p> <p id="output">Select some text above</p> </body> </html>
quelle
if (node != null)
. Zusätzlich habe ich einen Check für eine Nullnode.parentNode
nachif (node != parent)
document.activeElement
funktionierte aus irgendeinem Grund nicht, also benutzte ich die einzelne Div, mit der ich arbeitete.Diese Lösung zählt die Länge des Textinhalts früherer Geschwister, die zum übergeordneten Container zurückkehren. Es deckt wahrscheinlich nicht alle Randfälle ab, obwohl es verschachtelte Tags beliebiger Tiefe verarbeitet, aber es ist ein guter, einfacher Ausgangspunkt, wenn Sie einen ähnlichen Bedarf haben.
calculateTotalOffset(node, offset) { let total = offset let curNode = node while (curNode.id != 'parent') { if(curNode.previousSibling) { total += curNode.previousSibling.textContent.length curNode = curNode.previousSibling } else { curNode = curNode.parentElement } } return total } // after selection let start = calculateTotalOffset(range.startContainer, range.startOffset) let end = calculateTotalOffset(range.endContainer, range.endOffset)
quelle