contenteditable, imposta il cursore alla fine del testo (cross-browser)


111

output in 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>

Credo che in FF sarebbe simile a questo:

hey
<br />
what's up?

e in IE :

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

sfortunatamente, non c'è un modo carino per farlo in modo che ogni browser inserisca un <br />invece di un div- o p-tag, o almeno non sono riuscito a trovare nulla online.


IN OGNI MODO, quello che sto cercando di fare ora è che, quando premo il pulsante , voglio che il cursore sia impostato alla fine del testo, quindi dovrebbe assomigliare a questo:

hey
what's up?|

qualche modo per farlo in modo che funzioni in tutti i browser ?

esempio:

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

        ele.focus();

        //set caret -> end pos
     }
 }

Risposte:


282

La seguente funzione lo farà in tutti i principali browser:

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>

Posizionare il cursore all'inizio è quasi identico: è sufficiente cambiare il valore booleano passato nelle chiamate a collapse(). Ecco un esempio che crea funzioni per posizionare il cursore all'inizio e alla fine:

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);

Non funziona con Chrome perché createTextRange non è una funzione standard. Vedi stackoverflow.com/a/41743191/700206 .
Whitneyland

@Lee: funziona bene in Chrome, che supporta window.getSelectione document.createRange. Il createTextRangeramo è per le vecchie versioni di Internet Explorer.
Tim Down

al momento della scrittura window.getSelectionnon è supportato dallo 0,29% di tutti i browser (IE> 8). vedi: caniuse.com/#search=window.getSelection
w.stoettinger

@TimDown funziona. +1. Ma puoi spiegare il codice anche aggiungendo commenti per favore? Sarebbe fantastico
shinobi

Qualcuno sa come impostare il cursore alla fine (usa questo codice) quando cerca di scegliere come target un elemento iframe> body? L'elemento del corpo ha editablecontent="true"...? placeCaretAtEnd(this);non funzionerà per me.
RobbTe

6

Purtroppo l'ottima risposta di Tim ha funzionato per me solo per il piazzamento alla fine, per il piazzamento in partenza ho dovuto modificarlo leggermente.

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();
}

Non sono sicuro però se focus()e select()siano effettivamente necessari.


Hai anche introdotto una libreria esterna. Se non pensi che la messa a fuoco e la selezione siano necessarie, perché non commentare le linee e testarle?
dotnethaggis

Grazie, jquery rimosso. Ricordo di aver avuto dei risultati contraddittori, cercherò di isolarli di nuovo.
dimid

1

Questo esempio (live) mostra una breve funzione semplice,, setCaretAtStartEndche accetta due argomenti; Un nodo (modificabile) per posizionare il cursore su & un booleano che indica dove posizionarlo (inizio o fine 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

Se stai usando il compilatore di chiusura di Google, puoi fare quanto segue (in qualche modo semplificato dalla risposta di Tim):

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

Ecco la stessa cosa in ClojureScript:

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

L'ho testato in Chrome, Safari e FireFox, non sono sicuro di IE ...


1
range = goog.dom.Range.createFromNodeContents (el); cos'è goog.dom?
Anh Tú

Questo ha funzionato per me. @ AnhTú: goog.dom è uno spazio dei nomi fornito da closing-library.
David Beneš
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.