Ich bin auf der Suche nach einer endgültigen, browserübergreifenden Lösung, um die Cursor-/Caret-Position auf die letzte bekannte Position zu setzen, wenn ein contentEditable=”on”
Ich glaube, ich müsste die aktuelle Cursorposition in einer Variablen speichern, wenn sie den Fokus des div verlassen, und diese dann neu einstellen, wenn sie wieder im Fokus sind, aber ich konnte sie nicht zusammenstellen oder eine funktionierende finden Codebeispiel noch.
Wenn jemand Ideen, funktionierende Codeschnipsel oder Beispiele hat, würde ich mich freuen, sie zu sehen.
Ich habe noch keinen Code, aber hier ist, was ich habe:
<script type="text/javascript">
// jQuery
$(document).ready(function() {
$('#area').focus(function() { .. } // focus I would imagine I need.
}
</script>
<div id="area" contentEditable="true"></div>
PS. Ich habe diese Ressource ausprobiert, aber es scheint, dass sie für ein
Nico Burns
Diese Lösung funktioniert in allen gängigen Browsern:
saveSelection()
hängt an der onmouseup
und onkeyup
Ereignisse des div und speichert die Auswahl in der Variablen savedRange
.
restoreSelection()
hängt an der onfocus
Ereignis des div und wählt die gespeicherte Auswahl erneut aus savedRange
.
Dies funktioniert perfekt, es sei denn, Sie möchten, dass die Auswahl wiederhergestellt wird, wenn der Benutzer auf das div klickt (was etwas unintuitiv ist, da Sie normalerweise erwarten, dass der Cursor dorthin geht, wo Sie klicken, aber der Vollständigkeit halber Code enthalten ist).
Um dies zu erreichen, ist die onclick
und onmousedown
Ereignisse werden durch die Funktion abgebrochen cancelEvent()
Dies ist eine browserübergreifende Funktion zum Abbrechen des Ereignisses. Die cancelEvent()
Funktion läuft auch die restoreSelection()
Funktion, da das div beim Abbrechen des Click-Ereignisses keinen Fokus erhält und daher überhaupt nichts ausgewählt wird, es sei denn, diese Funktion wird ausgeführt.
Die Variable isInFocus
speichert, ob es im Fokus ist und auf “false” geändert wird onblur
und wahr” onfocus
. Dadurch können Klickereignisse nur abgebrochen werden, wenn das div nicht im Fokus ist (andernfalls könnten Sie die Auswahl überhaupt nicht ändern).
Wenn Sie möchten, dass die Auswahl geändert wird, wenn das Div durch einen Klick fokussiert wird, und die Auswahl nicht wiederhergestellt wird onclick
(und nur, wenn der Fokus auf das Element programmgesteuert mit document.getElementById("area").focus();
o.ä. dann einfach die entfernen onclick
und onmousedown
Veranstaltungen. Die onblur
Veranstaltung und die onDivBlur()
und cancelEvent()
Funktionen können unter diesen Umständen auch sicher entfernt werden.
Dieser Code sollte funktionieren, wenn er direkt in den Hauptteil einer HTML-Seite eingefügt wird, wenn Sie ihn schnell testen möchten:
<div id="area" style="width:300px;height:300px;" onblur="onDivBlur();" onmousedown="return cancelEvent(event);" onclick="return cancelEvent(event);" contentEditable="true" onmouseup="saveSelection();" onkeyup="saveSelection();" onfocus="restoreSelection();"></div>
<script type="text/javascript">
var savedRange,isInFocus;
function saveSelection()
{
if(window.getSelection)//non IE Browsers
{
savedRange = window.getSelection().getRangeAt(0);
}
else if(document.selection)//IE
{
savedRange = document.selection.createRange();
}
}
function restoreSelection()
{
isInFocus = true;
document.getElementById("area").focus();
if (savedRange != null) {
if (window.getSelection)//non IE and there is already a selection
{
var s = window.getSelection();
if (s.rangeCount > 0)
s.removeAllRanges();
s.addRange(savedRange);
}
else if (document.createRange)//non IE and no selection
{
window.getSelection().addRange(savedRange);
}
else if (document.selection)//IE
{
savedRange.select();
}
}
}
//this part onwards is only needed if you want to restore selection onclick
var isInFocus = false;
function onDivBlur()
{
isInFocus = false;
}
function cancelEvent(e)
{
if (isInFocus == false && savedRange != null) {
if (e && e.preventDefault) {
//alert("FF");
e.stopPropagation(); // DOM style (return false doesn't always work in FF)
e.preventDefault();
}
else {
window.event.cancelBubble = true;//IE stopPropagation
}
restoreSelection();
return false; // false = IE style
}
}
</script>
-
Danke das funktioniert tatsächlich! Getestet in IE, Chrome und FF spätestens. Sorry für die super verspätete Antwort =)
– GONEALE
6. September 2010 um 1:02 Uhr
-
Gewohnheit
if (window.getSelection)...
nur testen, ob der Browser dies unterstütztgetSelection
nicht, ob es eine Auswahl gibt oder nicht?– Sandy Gifford
25. April 2018 um 15:04 Uhr
-
@Sandy Ja genau. Dieser Teil des Codes entscheidet, ob der Standard verwendet wird
getSelection
api oder das Vermächtnisdocument.selection
API, die von älteren Versionen von IE verwendet wird. Je spätergetRangeAt (0)
Anruf kommt zurücknull
wenn keine Auswahl vorhanden ist, was in der Wiederherstellungsfunktion geprüft wird.– Nico Burns
5. Mai 2018 um 19:45 Uhr
-
@NicoBurns richtig, aber der Code im zweiten Bedingungsblock (
else if (document.createRange)
) ist das, was ich sehe. Es wird nur aufgerufen, wennwindow.getSelection
existiert nicht, wird aber verwendetwindow.getSelection
– Sandy Gifford
5. Mai 2018 um 20:10 Uhr
-
@NicoBurns Außerdem glaube ich nicht, dass Sie einen Browser finden würden
window.getSelection
aber nichtdocument.createRange
– was bedeutet, dass der zweite Block niemals verwendet werden würde …– Sandy Gifford
5. Mai 2018 um 20:11 Uhr
Dies ist mit den standardbasierten Browsern kompatibel, wird aber wahrscheinlich im IE fehlschlagen. Ich stelle es als Ausgangspunkt zur Verfügung. IE unterstützt keinen DOM-Bereich.
var editable = document.getElementById('editable'),
selection, range;
// Populates selection and range variables
var captureSelection = function(e) {
// Don't capture selection outside editable region
var isOrContainsAnchor = false,
isOrContainsFocus = false,
sel = window.getSelection(),
parentAnchor = sel.anchorNode,
parentFocus = sel.focusNode;
while(parentAnchor && parentAnchor != document.documentElement) {
if(parentAnchor == editable) {
isOrContainsAnchor = true;
}
parentAnchor = parentAnchor.parentNode;
}
while(parentFocus && parentFocus != document.documentElement) {
if(parentFocus == editable) {
isOrContainsFocus = true;
}
parentFocus = parentFocus.parentNode;
}
if(!isOrContainsAnchor || !isOrContainsFocus) {
return;
}
selection = window.getSelection();
// Get range (standards)
if(selection.getRangeAt !== undefined) {
range = selection.getRangeAt(0);
// Get range (Safari 2)
} else if(
document.createRange &&
selection.anchorNode &&
selection.anchorOffset &&
selection.focusNode &&
selection.focusOffset
) {
range = document.createRange();
range.setStart(selection.anchorNode, selection.anchorOffset);
range.setEnd(selection.focusNode, selection.focusOffset);
} else {
// Failure here, not handled by the rest of the script.
// Probably IE or some older browser
}
};
// Recalculate selection while typing
editable.onkeyup = captureSelection;
// Recalculate selection after clicking/drag-selecting
editable.onmousedown = function(e) {
editable.className = editable.className + ' selecting';
};
document.onmouseup = function(e) {
if(editable.className.match(/\sselecting(\s|$)/)) {
editable.className = editable.className.replace(/ selecting(\s|$)/, '');
captureSelection();
}
};
editable.onblur = function(e) {
var cursorStart = document.createElement('span'),
collapsed = !!range.collapsed;
cursorStart.id = 'cursorStart';
cursorStart.appendChild(document.createTextNode('—'));
// Insert beginning cursor marker
range.insertNode(cursorStart);
// Insert end cursor marker if any text is selected
if(!collapsed) {
var cursorEnd = document.createElement('span');
cursorEnd.id = 'cursorEnd';
range.collapse();
range.insertNode(cursorEnd);
}
};
// Add callbacks to afterFocus to be called after cursor is replaced
// if you like, this would be useful for styling buttons and so on
var afterFocus = [];
editable.onfocus = function(e) {
// Slight delay will avoid the initial selection
// (at start or of contents depending on browser) being mistaken
setTimeout(function() {
var cursorStart = document.getElementById('cursorStart'),
cursorEnd = document.getElementById('cursorEnd');
// Don't do anything if user is creating a new selection
if(editable.className.match(/\sselecting(\s|$)/)) {
if(cursorStart) {
cursorStart.parentNode.removeChild(cursorStart);
}
if(cursorEnd) {
cursorEnd.parentNode.removeChild(cursorEnd);
}
} else if(cursorStart) {
captureSelection();
var range = document.createRange();
if(cursorEnd) {
range.setStartAfter(cursorStart);
range.setEndBefore(cursorEnd);
// Delete cursor markers
cursorStart.parentNode.removeChild(cursorStart);
cursorEnd.parentNode.removeChild(cursorEnd);
// Select range
selection.removeAllRanges();
selection.addRange(range);
} else {
range.selectNode(cursorStart);
// Select range
selection.removeAllRanges();
selection.addRange(range);
// Delete cursor marker
document.execCommand('delete', false, null);
}
}
// Call callbacks here
for(var i = 0; i < afterFocus.length; i++) {
afterFocus[i]();
}
afterFocus = [];
// Register selection again
captureSelection();
}, 10);
};
-
Danke, Auge, ich habe Ihre Lösung ausprobiert, ich war etwas in Eile, aber nachdem ich sie verkabelt habe, wird die “-” -Position nur am letzten Fokuspunkt platziert (was eine Debug-Markierung zu sein scheint?) Und dann verlieren wir Fokus, es scheint den Cursor/das Caretzeichen nicht wiederherzustellen, wenn ich zurückklicke (zumindest nicht in Chrome, ich werde es mit FF versuchen), es geht einfach bis zum Ende des div. Also werde ich Nicos Lösung akzeptieren, weil ich weiß, dass sie mit allen Browsern kompatibel ist und gut funktioniert. Trotzdem vielen Dank für deine Mühe.
– GONEALE
6. September 2010 um 1:16 Uhr
-
Weißt du was, vergiss meine letzte Antwort, nachdem ich sowohl deine als auch Nicos weiter untersucht habe, ist deine nicht das, wonach ich in meiner Beschreibung gefragt habe, aber es ist das, was ich bevorzuge und erkannt hätte, dass ich es brauche. Deine richtig setzt die Position des Cursors, auf die Sie klicken, wenn Sie den Fokus zurück zum
aktivieren, wie bei einem normalen Textfeld. Die Wiederherstellung des Fokus auf den letzten Punkt reicht nicht aus, um ein benutzerfreundliches Eingabefeld zu erstellen. Ich gebe dir die Punkte.– GONEALE
6. September 2010 um 1:24 Uhr
-
Funktioniert super! Hier ist ein jsfiddle der obigen Lösung: jsfiddle.net/s5xAr/3
– vaughn
3. September 2011 um 2:45 Uhr
-
Vielen Dank, dass Sie echtes JavaScript gepostet haben, obwohl das OP lahmgelegt ist und ein Framework verwenden wollte.
– John
19. Oktober 2014 um 15:46 Uhr
-
cursorStart.appendChild(document.createTextNode('\u0002'));
ist unserer Meinung nach ein sinnvoller Ersatz. für das — Zeichen. Danke für den Code– Zweibob
29. Februar 2016 um 17:07 Uhr
Tim unten
Aktualisieren
Ich habe eine Cross-Browser Range and Selection Library namens geschrieben Rangy das eine verbesserte Version des Codes enthält, den ich unten gepostet habe. Du kannst den … benutzen Auswahl Modul speichern und wiederherstellen für diese spezielle Frage, obwohl ich versucht wäre, so etwas wie die Antwort von @Nico Burns zu verwenden, wenn Sie nichts anderes mit Auswahlen in Ihrem Projekt tun und nicht den Großteil einer Bibliothek benötigen.
Vorherige Antwort
Sie können IERange (http://code.google.com/p/ierange/), um den TextRange des IE in so etwas wie einen DOM-Bereich umzuwandeln und ihn in Verbindung mit so etwas wie dem Startpunkt der Augenlidlosigkeit zu verwenden. Persönlich würde ich nur die Algorithmen von IERange verwenden, die die Range <-> TextRange-Konvertierungen durchführen, anstatt das Ganze zu verwenden. Und das Auswahlobjekt von IE hat nicht die Eigenschaften focusNode und anchorNode, aber Sie sollten stattdessen einfach den aus der Auswahl erhaltenen Range/TextRange verwenden können.
Ich könnte etwas zusammenstellen, um dies zu tun, werde hier wieder posten, wenn und wenn ich das tue.
BEARBEITEN:
Ich habe eine Demo eines Skripts erstellt, das dies tut. Es funktioniert in allem, was ich bisher ausprobiert habe, mit Ausnahme eines Fehlers in Opera 9, den ich noch nicht untersucht habe. Browser, in denen es funktioniert, sind IE 5.5, 6 und 7, Chrome 2, Firefox 2, 3 und 3.5 und Safari 4, alle unter Windows.
http://www.timdown.co.uk/code/selections/
Beachten Sie, dass Auswahlen in Browsern rückwärts vorgenommen werden können, sodass der Fokusknoten am Anfang der Auswahl steht und das Drücken der rechten oder linken Cursortaste das Caretzeichen an eine Position relativ zum Start der Auswahl bewegt. Ich glaube nicht, dass es möglich ist, dies beim Wiederherstellen einer Auswahl zu replizieren, daher befindet sich der Fokusknoten immer am Ende der Auswahl.
Ich werde das irgendwann in Kürze komplett aufschreiben.
Ich hatte eine verwandte Situation, in der ich die Cursorposition speziell auf das ENDE eines contenteditable div setzen musste. Ich wollte keine vollwertige Bibliothek wie Rangy verwenden, und viele Lösungen waren viel zu schwergewichtig.
Am Ende habe ich mir diese einfache jQuery-Funktion ausgedacht, um die Karat-Position an das Ende eines contenteditable div zu setzen:
$.fn.focusEnd = function() {
$(this).focus();
var tmp = $('<span />').appendTo($(this)),
node = tmp.get(0),
range = null,
sel = null;
if (document.selection) {
range = document.body.createTextRange();
range.moveToElementText(node);
range.select();
} else if (window.getSelection) {
range = document.createRange();
range.selectNode(node);
sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
tmp.remove();
return this;
}
Die Theorie ist einfach: Hängen Sie eine Spanne an das Ende des bearbeitbaren Elements an, wählen Sie sie aus und entfernen Sie dann die Spanne – und hinterlassen Sie einen Cursor am Ende des div. Sie können diese Lösung anpassen, um die Spanne an einer beliebigen Stelle einzufügen und so den Cursor an einer bestimmten Stelle zu platzieren.
Die Verwendung ist einfach:
$('#editable').focusEnd();
Das ist es!
Gatsbimantik
Ich habe die Antwort von Nico Burns genommen und sie mit jQuery erstellt:
- Allgemein: Für alle
div contentEditable="true"
- Kürzer
Sie benötigen jQuery 1.6 oder höher:
savedRanges = new Object();
$('div[contenteditable="true"]').focus(function(){
var s = window.getSelection();
var t = $('div[contenteditable="true"]').index(this);
if (typeof(savedRanges
savedRanges
} else if(s.rangeCount > 0) {
s.removeAllRanges();
s.addRange(savedRanges
}
}).bind("mouseup keyup",function(){
var t = $('div[contenteditable="true"]').index(this);
savedRanges
}).on("mousedown click",function(e){
if(!$(this).is(":focus")){
e.stopPropagation();
e.preventDefault();
$(this).focus();
}
});
savedRanges = new Object();
$('div[contenteditable="true"]').focus(function(){
var s = window.getSelection();
var t = $('div[contenteditable="true"]').index(this);
if (typeof(savedRanges
savedRanges
} else if(s.rangeCount > 0) {
s.removeAllRanges();
s.addRange(savedRanges
}
}).bind("mouseup keyup",function(){
var t = $('div[contenteditable="true"]').index(this);
savedRanges
}).on("mousedown click",function(e){
if(!$(this).is(":focus")){
e.stopPropagation();
e.preventDefault();
$(this).focus();
}
});
div[contenteditable] {
padding: 1em;
font-family: Arial;
outline: 1px solid rgba(0,0,0,0.5);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div contentEditable="true"></div>
<div contentEditable="true"></div>
<div contentEditable="true"></div>
-
@salivan Ich weiß, es ist spät, es zu aktualisieren, aber ich denke, es funktioniert jetzt. Grundsätzlich habe ich eine neue Bedingung hinzugefügt und von der Verwendung der Element-ID zum Element-Index gewechselt, der immer vorhanden sein sollte 🙂
– Gatsbimantic
28. Januar 2015 um 23:14 Uhr
mkaj
Nachdem ich herumgespielt habe, habe ich die Antwort von eyelidlessness oben geändert und daraus ein jQuery-Plugin gemacht, sodass Sie einfach eines der folgenden tun können:
var html = "The quick brown fox";
$div.html(html);
// Select at the text "quick":
$div.setContentEditableSelection(4, 5);
// Select at the beginning of the contenteditable div:
$div.setContentEditableSelection(0);
// Select at the end of the contenteditable div:
$div.setContentEditableSelection(html.length);
Entschuldigen Sie den langen Code-Post, aber es kann jemandem helfen:
$.fn.setContentEditableSelection = function(position, length) {
if (typeof(length) == "undefined") {
length = 0;
}
return this.each(function() {
var $this = $(this);
var editable = this;
var selection;
var range;
var html = $this.html();
html = html.substring(0, position) +
'<a id="cursorStart"></a>' +
html.substring(position, position + length) +
'<a id="cursorEnd"></a>' +
html.substring(position + length, html.length);
console.log(html);
$this.html(html);
// Populates selection and range variables
var captureSelection = function(e) {
// Don't capture selection outside editable region
var isOrContainsAnchor = false,
isOrContainsFocus = false,
sel = window.getSelection(),
parentAnchor = sel.anchorNode,
parentFocus = sel.focusNode;
while (parentAnchor && parentAnchor != document.documentElement) {
if (parentAnchor == editable) {
isOrContainsAnchor = true;
}
parentAnchor = parentAnchor.parentNode;
}
while (parentFocus && parentFocus != document.documentElement) {
if (parentFocus == editable) {
isOrContainsFocus = true;
}
parentFocus = parentFocus.parentNode;
}
if (!isOrContainsAnchor || !isOrContainsFocus) {
return;
}
selection = window.getSelection();
// Get range (standards)
if (selection.getRangeAt !== undefined) {
range = selection.getRangeAt(0);
// Get range (Safari 2)
} else if (
document.createRange &&
selection.anchorNode &&
selection.anchorOffset &&
selection.focusNode &&
selection.focusOffset
) {
range = document.createRange();
range.setStart(selection.anchorNode, selection.anchorOffset);
range.setEnd(selection.focusNode, selection.focusOffset);
} else {
// Failure here, not handled by the rest of the script.
// Probably IE or some older browser
}
};
// Slight delay will avoid the initial selection
// (at start or of contents depending on browser) being mistaken
setTimeout(function() {
var cursorStart = document.getElementById('cursorStart');
var cursorEnd = document.getElementById('cursorEnd');
// Don't do anything if user is creating a new selection
if (editable.className.match(/\sselecting(\s|$)/)) {
if (cursorStart) {
cursorStart.parentNode.removeChild(cursorStart);
}
if (cursorEnd) {
cursorEnd.parentNode.removeChild(cursorEnd);
}
} else if (cursorStart) {
captureSelection();
range = document.createRange();
if (cursorEnd) {
range.setStartAfter(cursorStart);
range.setEndBefore(cursorEnd);
// Delete cursor markers
cursorStart.parentNode.removeChild(cursorStart);
cursorEnd.parentNode.removeChild(cursorEnd);
// Select range
selection.removeAllRanges();
selection.addRange(range);
} else {
range.selectNode(cursorStart);
// Select range
selection.removeAllRanges();
selection.addRange(range);
// Delete cursor marker
document.execCommand('delete', false, null);
}
}
// Register selection again
captureSelection();
}, 10);
});
};
-
@salivan Ich weiß, es ist spät, es zu aktualisieren, aber ich denke, es funktioniert jetzt. Grundsätzlich habe ich eine neue Bedingung hinzugefügt und von der Verwendung der Element-ID zum Element-Index gewechselt, der immer vorhanden sein sollte 🙂
– Gatsbimantic
28. Januar 2015 um 23:14 Uhr
Zoonmann
Sie können nutzen selectNodeContents die von modernen Browsern unterstützt wird.
var el = document.getElementById('idOfYoursContentEditable');
var selection = window.getSelection();
var range = document.createRange();
selection.removeAllRanges();
range.selectNodeContents(el);
range.collapse(false);
selection.addRange(range);
el.focus();
-
Ist es möglich, diesen Code zu ändern, damit der Endbenutzer das Caretzeichen immer noch an jede gewünschte Position verschieben kann?
– Zabs
16. Februar 2018 um 12:52 Uhr
-
Jawohl. Sie sollten die Methoden setStart & setEnd für das Bereichsobjekt verwenden. developer.mozilla.org/en-US/docs/Web/API/Range/setStart
– Zoonmann
17. Februar 2018 um 14:01 Uhr
Ich wusste es nicht
contentEditable
funktionierte in Nicht-IE-Browsern o_o– Aditya
25. Juli 2009 um 15:14 Uhr
Ja, es tut Aditya.
– GONEALE
27. Juli 2009 um 1:39 Uhr
aditya, Safari 2+, Firefox 3+ denke ich.
– Lidlosigkeit
28. Juli 2009 um 6:46 Uhr
Versuchen Sie, tabindex=”0″ für das div zu setzen. Das sollte es in den meisten Browsern fokussierbar machen.
– Tokimon
14. Juni 2010 um 13:24 Uhr