DIV Contenteditable: cómo puedo determinar si el cursor está al principio o al final del contenido

Tengo un div satisfactorio que contiene el típico editor wysiwyg html (negrita, anclajes, listas).

Necesito determinar si el cursor actual es, onKeyDown, al inicio y al final de div. El motivo es que, en función de la posición del cursor y de la tecla presionada, podría fusionar este div con el div anterior en un espacio posterior, o crear un nuevo div siguiente en enter.

He estado jugueteando con los rangos, pero cuando trabajas con html dentro del elemento, las cosas se vuelven bastante complicadas.

Espero estar pasando por alto alguna solución simple.

¿Existe una forma relativamente simple de determinar esto? Estoy abierto a usar una biblioteca como Rangy.

¡Gracias!

Editar: estoy pensando algo así:

$('.mycontenteditable').bind('keydown', handle_keydown) handle_keydown = function(e) { range = window.getSelection().getRangeAt(0) start_range = document.createRange() start_range.selectNodeContents(this.firstChild) start_range.collapse(true) // collapse to start is_start = start_range.compareBoundaryPoints(Range.START_TO_START,range) end_range = document.createRange() end_range.selectNodeContents(this.lastChild) end_range.collapse(false) is_end = end_range.compareBoundaryPoints(Range.END_TO_END,range) } 

¿Me voy a encontrar con algún problema extraño con algo como esto?

Utilizaría un enfoque similar al suyo excepto que use el método toString() de los objetos Range lugar de cloneContents() para evitar la clonación innecesaria. Además, en IE <9 (que no admite rangos), puede usar un enfoque similar con la propiedad de text de TextRange .

Tenga en cuenta que esto tendrá problemas cuando existan saltos de línea principales y / o finales en el contenido porque el método toString() de un rango funciona igual que la propiedad de contenido de texto de un nodo y solo considera los nodos de texto, por lo tanto, no tiene en cuenta los saltos de línea implicado por elementos de bloque o bloque. Además, CSS no se tiene en cuenta: por ejemplo, texto dentro de elementos que están ocultos a través de la display: none está incluido.

Aquí hay un ejemplo:

Demostración en vivo: http://jsfiddle.net/YA3Pu/1/

Código:

 function getSelectionTextInfo(el) { var atStart = false, atEnd = false; var selRange, testRange; if (window.getSelection) { var sel = window.getSelection(); if (sel.rangeCount) { selRange = sel.getRangeAt(0); testRange = selRange.cloneRange(); testRange.selectNodeContents(el); testRange.setEnd(selRange.startContainer, selRange.startOffset); atStart = (testRange.toString() == ""); testRange.selectNodeContents(el); testRange.setStart(selRange.endContainer, selRange.endOffset); atEnd = (testRange.toString() == ""); } } else if (document.selection && document.selection.type != "Control") { selRange = document.selection.createRange(); testRange = selRange.duplicate(); testRange.moveToElementText(el); testRange.setEndPoint("EndToStart", selRange); atStart = (testRange.text == ""); testRange.moveToElementText(el); testRange.setEndPoint("StartToEnd", selRange); atEnd = (testRange.text == ""); } return { atStart: atStart, atEnd: atEnd }; } 

Así es como terminé resolviendo esto. La solución propuesta anteriormente funcionaba a veces pero había muchos casos extremos, así que terminé considerando cuánto texto había antes o después del cursor, y si eso era 0 caracteres, entonces estaba al principio o al final:

 handle_keydown = function(e) { // Get the current cusor position range = window.getSelection().getRangeAt(0) // Create a new range to deal with text before the cursor pre_range = document.createRange(); // Have this range select the entire contents of the editable div pre_range.selectNodeContents(this); // Set the end point of this range to the start point of the cursor pre_range.setEnd(range.startContainer, range.startOffset); // Fetch the contents of this range (text before the cursor) this_text = pre_range.cloneContents(); // If the text's length is 0, we're at the start of the div. at_start = this_text.textContent.length === 0; // Rinse and repeat for text after the cursor to determine if we're at the end. post_range = document.createRange(); post_range.selectNodeContents(this); post_range.setStart(range.endContainer, range.endOffset); next_text = post_range.cloneContents(); at_end = next_text.textContent.length === 0; } 

Todavía no estoy del todo seguro de que existan otros casos extremos, ya que no estoy completamente seguro de cómo probar esto en una unidad, ya que requiere la interacción del mouse; probablemente haya una biblioteca para tratar esto en alguna parte.