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
     }
 }
Josh Crozier
fuente
1
Esto funcionó mejor para mí
Илья Зеленько

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);
Tim Down
fuente
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.

dimid
fuente
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>

vsync
fuente
-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 ...

Matt WD
fuente
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š