Pregunta

Necesito mover el careto al final de contenteditable nodo como en el widget de notas de gmail.

Leí hilos en stackoverflow, pero esas soluciones se basan en el uso de entradas y no funcionan con contenteditable elementos.

¿Fue útil?

Solución

También hay otro problema.

los Nico BurnsLa solución funciona si el contenteditable DIV no contiene otros elementos múltiples.

Por ejemplo, si un DIV contiene otros divs, y estos otros Divs contienen otras cosas dentro, podrían ocurrir algunos problemas.

Para resolverlos, he organizado la siguiente solución, esa es una mejora del Nicoes uno:

//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://stackoverflow.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://stackoverflow.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 manera, el cursor seguramente se posiciona al final del último elemento, finalmente anidada.

Editar #1: Para ser más genérico, la declaración While debe considerar también todas las otras etiquetas que no pueden contener texto. Estos elementos se nombran 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 regresa true Si el argumento no es un elemento vacío, la siguiente línea de código:

contentEditableElement.lastChild.tagName.toLowerCase() != 'br'

debe ser reemplazado con:

canContainText(getLastChildElement(contentEditableElement))

Editar #2: El código anterior está completamente actualizado, con cada cambio descrito y discutido

Otros consejos

La solución de Geowa4 funcionará para un textea, pero no para un elemento contento.

Esta solución es para mover el careto al final de un elemento contento. Debería funcionar en todos los navegadores que admiten contentos.

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
    }
}

Se puede usar por 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);

Si no te importan los navegadores mayores, este me hizo el truco.

// [optional] make sure focus is on the element
yourContentEditableElement.focus();
// select all the content in the element
document.execCommand('selectAll', false, null);
// collapse selection to the end
document.getSelection().collapseToEnd();

Es posible 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; 
}

Tuve un problema similar tratando de hacer un elemento editable. Era posible en Chrome y Firefox, pero en Firefox, el Caret fue al comienzo de la entrada o fue un espacio después del final de la entrada. Muy confuso para el usuario final, creo, tratando de editar el contenido.

No encontré ninguna solución intentar varias cosas. Lo único que funcionó para mí fue "dar la vuelta al problema" colocando una entrada de texto simple dentro de mi. Ahora funciona. Parece que "editable contenido" sigue siendo sangrante de la tecnología de borde, que puede o no funcionar, ya que le gustaría que funcione, dependiendo del contexto.

Moviendo el cursor hasta el final del tramo editable en respuesta al evento de enfoque:

  moveCursorToEnd(el){
    if(el.innerText && document.createRange)
    {
      window.setTimeout(() =>
        {
          let selection = document.getSelection();
          let range = document.createRange();

          range.setStart(el.childNodes[0],el.innerText.length);
          range.collapse(true);
          selection.removeAllRanges();
          selection.addRange(range);
        }
      ,1);
    }
  }

Y llamarlo en el controlador de eventos (reaccionar aquí):

onFocus={(e) => this.moveCursorToEnd(e.target)}} 
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top