javascript - Cómo mover el cursor al final de la entidad contenteditable
cursor-position (3)
Necesito mover el cursor al final del nodo contenteditable
como en el widget de notas de Gmail.
Leo hilos en StackOverflow, pero esas soluciones se basan en el uso de entradas y no funcionan con elementos contenteditable
.
Es posible hacer establecer el cursor hasta el final a través del rango:
setCaretToEnd(target/*: HTMLDivElement*/) {
const range = document.createRange();
const sel = window.getSelection();
range.selectNodeContents(target);
range.collapse(false);
sel.removeAllRanges();
sel.addRange(range);
target.focus();
range.detach(); // optimization
// set scroll to the end if multiline
target.scrollTop = target.scrollHeight;
}
La solución de Geowa4 funcionará para un área de texto, pero no para un elemento contento.
Esta solución es para mover el cursor al final de un elemento contento. Debería funcionar en todos los navegadores compatibles con contenteditable.
function setEndOfContenteditable(contentEditableElement)
{
var range,selection;
if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
{
range = document.createRange();//Create a range (a range is a like the selection but invisible)
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection();//get the selection object (allows you to change selection)
selection.removeAllRanges();//remove any selections already made
selection.addRange(range);//make the range you have just created the visible selection
}
else if(document.selection)//IE 8 and lower
{
range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
range.select();//Select the range (make it the visible selection
}
}
Puede ser usado por un código similar a:
elem = document.getElementById(''txt1'');//This is the element that you want to move the caret to the end of
setEndOfContenteditable(elem);
También hay otro problema.
La solución de Nico Burns funciona si el div contenteditable
no contiene otros elementos multilínea.
Por ejemplo, si un div contiene otros divs, y estos otros divs contienen otras cosas adentro, podrían ocurrir algunos problemas.
Para resolverlos, he dispuesto la siguiente solución, que es una mejora de la de Nico :
//Namespace management idea from http://enterprisejquery.com/2010/10/how-good-c-habits-can-encourage-bad-javascript-habits-part-1/
(function( cursorManager ) {
//From: http://www.w3.org/TR/html-markup/syntax.html#syntax-elements
var voidNodeTags = [''AREA'', ''BASE'', ''BR'', ''COL'', ''EMBED'', ''HR'', ''IMG'', ''INPUT'', ''KEYGEN'', ''LINK'', ''MENUITEM'', ''META'', ''PARAM'', ''SOURCE'', ''TRACK'', ''WBR'', ''BASEFONT'', ''BGSOUND'', ''FRAME'', ''ISINDEX''];
//From: https://.com/questions/237104/array-containsobj-in-javascript
Array.prototype.contains = function(obj) {
var i = this.length;
while (i--) {
if (this[i] === obj) {
return true;
}
}
return false;
}
//Basic idea from: https://.com/questions/19790442/test-if-an-element-can-contain-text
function canContainText(node) {
if(node.nodeType == 1) { //is an element node
return !voidNodeTags.contains(node.nodeName);
} else { //is not an element node
return false;
}
};
function getLastChildElement(el){
var lc = el.lastChild;
while(lc && lc.nodeType != 1) {
if(lc.previousSibling)
lc = lc.previousSibling;
else
break;
}
return lc;
}
//Based on Nico Burns''s answer
cursorManager.setEndOfContenteditable = function(contentEditableElement)
{
while(getLastChildElement(contentEditableElement) &&
canContainText(getLastChildElement(contentEditableElement))) {
contentEditableElement = getLastChildElement(contentEditableElement);
}
var range,selection;
if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
{
range = document.createRange();//Create a range (a range is a like the selection but invisible)
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection();//get the selection object (allows you to change selection)
selection.removeAllRanges();//remove any selections already made
selection.addRange(range);//make the range you have just created the visible selection
}
else if(document.selection)//IE 8 and lower
{
range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
range.select();//Select the range (make it the visible selection
}
}
}( window.cursorManager = window.cursorManager || {}));
Uso:
var editableDiv = document.getElementById("my_contentEditableDiv");
cursorManager.setEndOfContenteditable(editableDiv);
De esta forma, el cursor seguramente se posiciona al final del último elemento, finalmente anidado.
EDIT # 1 : para ser más genérico, la sentencia while debería considerar también todas las otras etiquetas que no pueden contener texto. Estos elementos se denominan elementos vacíos , y en esta pregunta hay algunos métodos sobre cómo probar si un elemento es nulo. Entonces, suponiendo que exista una función llamada canContainText
que devuelve true
si el argumento no es un elemento nulo, la siguiente línea de código:
contentEditableElement.lastChild.tagName.toLowerCase() != ''br''
debe ser reemplazado por:
canContainText(getLastChildElement(contentEditableElement))
EDIT # 2 : El código anterior está completamente actualizado, con cada cambio descrito y discutido