satisfecho, coloque el cursor al final del texto (navegador cruzado)


111

salida en Chrome :

<div id="content" contenteditable="true" style="border:1px solid #000;width:500px;height:40px;">
    hey
    <div>what's up?</div>
<div>
<button id="insert_caret"></button>

Creo que en FF se vería así:

hey
<br />
what's up?

y en IE :

hey
<p>what's up?</p>

desafortunadamente, no hay una buena manera de hacer que cada navegador inserte una <br />etiqueta div o p en lugar de una etiqueta p, o al menos no pude encontrar nada en línea.


DE TODOS MODOS, lo que estoy tratando de hacer ahora es, cuando presiono el botón , quiero que el símbolo de intercalación se establezca al final del texto, por lo que debería verse así:

hey
what's up?|

alguna forma de hacer esto para que funcione en todos los navegadores ?

ejemplo:

$(document).ready(function()
{
    $('#insert_caret').click(function()
    {
        var ele = $('#content');
        var length = ele.html().length;

        ele.focus();

        //set caret -> end pos
     }
 }

Respuestas:


282

La siguiente función lo hará en todos los principales navegadores:

function placeCaretAtEnd(el) {
    el.focus();
    if (typeof window.getSelection != "undefined"
            && typeof document.createRange != "undefined") {
        var range = document.createRange();
        range.selectNodeContents(el);
        range.collapse(false);
        var sel = window.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
    } else if (typeof document.body.createTextRange != "undefined") {
        var textRange = document.body.createTextRange();
        textRange.moveToElementText(el);
        textRange.collapse(false);
        textRange.select();
    }
}

placeCaretAtEnd( document.querySelector('p') );
p{ padding:.5em; border:1px solid black; }
<p contentEditable>foo bar </p>

Colocar el signo de intercalación al principio es casi idéntico: solo requiere cambiar el booleano pasado a las llamadas a collapse(). Aquí hay un ejemplo que crea funciones para colocar el símbolo de intercalación al principio y al final:

function createCaretPlacer(atStart) {
    return function(el) {
        el.focus();
        if (typeof window.getSelection != "undefined"
                && typeof document.createRange != "undefined") {
            var range = document.createRange();
            range.selectNodeContents(el);
            range.collapse(atStart);
            var sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        } else if (typeof document.body.createTextRange != "undefined") {
            var textRange = document.body.createTextRange();
            textRange.moveToElementText(el);
            textRange.collapse(atStart);
            textRange.select();
        }
    };
}

var placeCaretAtStart = createCaretPlacer(true);
var placeCaretAtEnd = createCaretPlacer(false);

No funciona con Chrome porque createTextRange no es una función estándar. Consulte stackoverflow.com/a/41743191/700206 .
Whitneyland

@Lee: funciona bien en Chrome, que admite window.getSelectiony document.createRange. La createTextRangerama es para versiones antiguas de Internet Explorer.
Tim Down

en el momento de escribir window.getSelectioneste artículo no es compatible con el 0,29% de todos los navegadores (IE> 8). ver: caniuse.com/#search=window.getSelection
w.stoettinger

@TimDown esto funciona. +1. ¿Pero también puede explicar el código agregando comentarios, por favor? Sería genial
shinobi

¿Alguien sabe cómo establecer el signo de intercalación al final (use este código) al intentar apuntar a un elemento iframe> body? ¿El elemento del cuerpo tiene editablecontent="true"...? placeCaretAtEnd(this);no funcionará para mí.
RobbTe

6

Desafortunadamente, la excelente respuesta de Tim funcionó para mí solo para colocar al final, para colocar al principio tuve que modificarla ligeramente.

function setCaret(target, isStart) {
  const range = document.createRange();
  const sel = window.getSelection();
  if (isStart){
    const newText = document.createTextNode('');
    target.appendChild(newText);
    range.setStart(target.childNodes[0], 0);
  }
  else {
    range.selectNodeContents(target);
  }
  range.collapse(isStart);
  sel.removeAllRanges();
  sel.addRange(range);
  target.focus();
  target.select();
}

No estoy seguro de que si focus()y select()están realmente se necesita.


También introdujo una biblioteca externa. Si no cree que sea necesario enfocar y seleccionar, ¿por qué no comenta las líneas y lo prueba?
dotnethaggis

Gracias, eliminó jquery. Recuerdo que obtuve algunos resultados contradictorios, intentaré aislarlos nuevamente.
dimid

1

Este ejemplo (en vivo) muestra una función simple y corta setCaretAtStartEnd, que toma dos argumentos; Un nodo (editable) para colocar el signo de intercalación y un booleano que indica dónde colocarlo (inicio o final del nodo)

const editableElm = document.querySelector('[contenteditable]');

document.querySelectorAll('button').forEach((elm, idx) => 
  elm.addEventListener('click', () => {
    editableElm.focus()
    setCaretAtStartEnd(editableElm, idx) 
  })
)

function setCaretAtStartEnd( node, atEnd ){
  const sel = document.getSelection();
  node = node.firstChild;

  if( sel.rangeCount ){
      ['Start', 'End'].forEach(pos =>
        sel.getRangeAt(0)["set" + pos](node, atEnd ? node.length : 0)
      )
  }
}
[contenteditable]{ padding:5px; border:1px solid; }
<h1 contenteditable>Place the caret anywhere</h1>
<br>
<button>Move caret to start</button>
<button>Move caret to end</button>


-1

Si está utilizando el compilador de cierre de Google, puede hacer lo siguiente (algo simplificado de la respuesta de Tim):

function placeCaretAtEnd(el) {
    el.focus();
    range = goog.dom.Range.createFromNodeContents(el);
    range.collapse(false);
    range.select();
}

Aquí es lo mismo en ClojureScript:

(defn place-caret-at-end [el] 
   (.focus el)
   (doto (.createFromNodeContents goog.dom.Range el)
         (.collapse false)
         .select))

He probado esto en Chrome, Safari y FireFox, no estoy seguro de IE ...


1
rango = goog.dom.Range.createFromNodeContents (el); ¿Qué es goog.dom?
Anh Tú

Esto funcionó para mí. @ AnhTú: goog.dom es un espacio de nombres proporcionado por la biblioteca de cierre.
David Beneš
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.