2011-01-02 18 views
6

Tengo un contentEditable div, el innerHTML del cual se puede actualizar a través de AJAX durante la edición. El problema es que cuando cambias el contenido del div, mueve el cursor al final del div (o pierde el foco dependiendo del navegador). ¿Qué es una buena solución entre navegadores para almacenar la posición de intercalación antes de cambiar innerHTML y luego restaurarla?Guardar y restaurar la posición de intercalación para contentEditable div

Respuesta

8

Puede usar Rangy, mi rango de explorador cruzado y la biblioteca de selección. Tiene un selection save and restore module que parece adecuado para sus necesidades.

El enfoque no es complicado: inserta elementos marcadores al principio y al final de cada rango seleccionado y utiliza esos elementos marcadores para restaurar los límites del rango nuevamente más tarde, lo que podría implementarse sin Rangy en poco código (y podría incluso adaptar Rangy's own code). La principal ventaja de Rangy es soporte para IE < = 8.

+0

fantástico. Tuve cierta inquietud sobre el uso de una biblioteca aleatoria de un tipo en SO, pero hizo lo que quería en 2 líneas de código. ¡Gracias! – thedayturns

+3

@thedayturns: Esa es la actitud correcta para tener, así que no te culpo :) Me alegro de que haya sido útil. –

+0

@TimDown ¿Rangy es compatible con múltiples divs contentos? Al igual, la posición de guardado del cursor sobre tres divs diferentes. Por el hecho de ser, quiero usar 1 editor para 3 campos diferentes. – PhyCoMath

6

Sé que esto es un hilo antiguo, pero pensé que iba a proporcionar una solución alternativa no-biblioteca

http://jsfiddle.net/6jbwet9q/9/

Probado en cromo, FF , e IE10 + le permite cambiar, eliminar y restaurar html mientras retiene la posición/selección del cursor.

HTML

<div id=bE contenteditable=true></div> 

JS

function saveRangePosition() 
    { 
    var range=window.getSelection().getRangeAt(0); 
    var sC=range.startContainer,eC=range.endContainer; 

    A=[];while(sC!==bE){A.push(getNodeIndex(sC));sC=sC.parentNode} 
    B=[];while(eC!==bE){B.push(getNodeIndex(eC));eC=eC.parentNode} 

    return {"sC":A,"sO":range.startOffset,"eC":B,"eO":range.endOffset}; 
    } 

function restoreRangePosition(rp) 
    { 
    bE.focus(); 
    var sel=window.getSelection(),range=sel.getRangeAt(0); 
    var x,C,sC=bE,eC=bE; 

    C=rp.sC;x=C.length;while(x--)sC=sC.childNodes[C[x]]; 
    C=rp.eC;x=C.length;while(x--)eC=eC.childNodes[C[x]]; 

    range.setStart(sC,rp.sO); 
    range.setEnd(eC,rp.eO); 
    sel.removeAllRanges(); 
    sel.addRange(range) 
    } 

function getNodeIndex(n){var i=0;while(n=n.previousSibling)i++;return i} 
+1

Parece que se está convirtiendo cada límite de rango de selección en una ruta y viceversa. Este es un gran enfoque siempre que la estructura del DOM sea la misma antes y después de los cambios 'innerHTML', que no se garantiza que sea cierto. –

+0

¿Es posible arreglar este código para varios divs contentibles? Para poder seleccionar, digamos, 1 de 3 div contented, y luego recuperar la posición de donde quiero insertar. – PhyCoMath

11

de nuevo a 2016 :)
Después me encontré con esta solución, y no me conviene, porque mi DOM reemplazó completamente después de cada tipeo Hago más reasech y vengo con una solución simple que guarda el cursor por la posición del personaje que funciona perfecto para mí.

La idea es muy simple.

  1. encuentra la longitud de los caracteres antes del cursor y guárdalo.
  2. cambiar el DOM.
  3. usando TreeWalker caminar justo en text nodes de context node y contando caracteres hasta que llegamos a la derecha text node y la posición dentro de él

Dos caso extremo:

  1. contenido eliminado por completo lo que no hay text node:
    so: mover el cursor al inicio del nodo de contexto

  2. hay menos contenido entonces el index señaló en:
    así: mueve el cursor hasta el final del último nodo

function saveCaretPosition(context){ 
 
    var selection = window.getSelection(); 
 
    var range = selection.getRangeAt(0); 
 
    range.setStart( context, 0); 
 
    var len = range.toString().length; 
 

 
    return function restore(){ 
 
     var pos = getTextNodeAtPosition(context, len); 
 
     selection.removeAllRanges(); 
 
     var range = new Range(); 
 
     range.setStart(pos.node ,pos.position); 
 
     selection.addRange(range); 
 

 
    } 
 
} 
 

 
function getTextNodeAtPosition(root, index){ 
 
    var lastNode = null; 
 

 
    var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT,function next(elem) { 
 
     if(index >= elem.textContent.length){ 
 
      index -= elem.textContent.length; 
 
      lastNode = elem; 
 
      return NodeFilter.FILTER_REJECT 
 
     } 
 
     return NodeFilter.FILTER_ACCEPT; 
 
    }); 
 
    var c = treeWalker.nextNode(); 
 
    return { 
 
     node: c? c: root, 
 
     position: c? index: 0 
 
    }; 
 
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/prism.min.js"></script> 
 
<link href="https://rawgit.com/PrismJS/prism/gh-pages/themes/prism.css" rel="stylesheet"/> 
 
<style> 
 
    *{ 
 
    outline:none 
 
    } 
 
</style> 
 
<h3>Edit the CSS Snippet </H3> 
 
<pre> 
 
    <code class="language-css" contenteditable=true >p { color: red }</code> 
 
</pre> 
 

 
<script > 
 
    var code = document.getElementsByTagName('code')[0]; 
 
    
 
    code.addEventListener('input',function() { 
 
     var restore = saveCaretPosition(this); 
 
     Prism.highlightElement(this); 
 
     restore(); 
 
    }) 
 
</script>

+1

¿Qué sucede si tiene saltos de línea y otros elementos de formato dentro del elemento editable? –

+0

te refieres como
de ? todavía debería funcionar. ese código se compila para el editor de texto enriquecido que cambia elementos alrededor del cursor cuando el usuario escribe –

+1

Tengo un contenido editable con roturas y tal y funciona. Si lo usa para una función de deshacer, almacene la última pulsación de tecla en un manejador onKeyDown y use 'range.setStart (pos.node, pos.position- (lastKeypress == 13? 0: 1));' para evitar que el cursor se active en un paseo :-) – Tschallacka

Cuestiones relacionadas