reemplace innerHTML en contenteditable div

Necesito implementar resaltar para los números (en el futuro, añadiré reglas más complejas) en el div satisfactorio. El problema es cuando inserto contenido nuevo con javascript replace, cambios de DOM y satisfacción de div perdida. Lo que necesito es mantener el foco en div con cuidado en la posición actual, para que los usuarios puedan simplemente escribir sin ningún problema y mi función resalte los números. Buscando en Google decido que la biblioteca Rangy es la mejor solución. Tengo el siguiente código:

function formatText() { var savedSel = rangy.saveSelection(); el = document.getElementById('pad'); el.innerHTML = el.innerHTML.replace(/(]+)>)/ig,""); el.innerHTML = el.innerHTML.replace(/([0-9])/ig,"$1"); rangy.restreSelection(savedSel); } 

El problema es después de que la función finaliza el trabajo, el foco está volviendo al div, pero siempre apunto al comienzo del div y puedo escribir en cualquier lugar, exceptuando el comienzo de div. También los tipos de Rangy warning: Module SaveRestore: Marker element has been removed. Cannot restre selection. siguientes Rangy warning: Module SaveRestore: Marker element has been removed. Cannot restre selection. Rangy warning: Module SaveRestore: Marker element has been removed. Cannot restre selection. Por favor, ayúdame a implementar este funcional. Estoy abierto para otra solución, no solo biblioteca rangy. ¡Gracias!

http://jsfiddle.net/2rTA5/ Esto es jsfiddle, pero no funciona correctamente (no pasa nada cuando escribí números en mi div), tal vez no sea yo (código postal por primera vez a través de jsfiddle) o el recurso no es compatible con contenteditable. UPD * Leí problemas similares en stackoverflow, pero las soluciones no se adaptan a mi caso 🙁

El problema es que el módulo de selección guardar / restaurar de Rangy funciona insertando elementos marcadores invisibles en el DOM donde están los límites de selección y luego su código elimina todas las tags HTML, incluidos los elementos marcadores de Rangy (como sugiere el mensaje de error). Tienes dos opciones:

  1. Vaya a una solución de cruce DOM para colorear los números en lugar de innerHTML . Esto será más confiable pero más complicado.
  2. Implemente una recuperación y restauración de selección basada en el índice de caracteres alternativos. En general, esto sería frágil, pero hará lo que quiera en este caso.

ACTUALIZAR

He recortado una selección / restauración basada en el índice de caracteres para Rangy (opción 2 anterior). Es un poco difícil, pero hace el trabajo para este caso. Funciona al atravesar nodos de texto. Puedo agregar esto a Rangy de alguna forma. ( ACTUALIZADO el 5 de junio de 2012: ahora lo he implementado, de manera más confiable, para Rangy ) .

jsFiddle: http://jsfiddle.net/2rTA5/2/

Código:

 function saveSelection(containerEl) { var charIndex = 0, start = 0, end = 0, foundStart = false, stop = {}; var sel = rangy.getSelection(), range; function traverseTextNodes(node, range) { if (node.nodeType == 3) { if (!foundStart && node == range.startContainer) { start = charIndex + range.startOffset; foundStart = true; } if (foundStart && node == range.endContainer) { end = charIndex + range.endOffset; throw stop; } charIndex += node.length; } else { for (var i = 0, len = node.childNodes.length; i < len; ++i) { traverseTextNodes(node.childNodes[i], range); } } } if (sel.rangeCount) { try { traverseTextNodes(containerEl, sel.getRangeAt(0)); } catch (ex) { if (ex != stop) { throw ex; } } } return { start: start, end: end }; } function restoreSelection(containerEl, savedSel) { var charIndex = 0, range = rangy.createRange(), foundStart = false, stop = {}; range.collapseToPoint(containerEl, 0); function traverseTextNodes(node) { if (node.nodeType == 3) { var nextCharIndex = charIndex + node.length; if (!foundStart && savedSel.start >= charIndex && savedSel.start <= nextCharIndex) { range.setStart(node, savedSel.start - charIndex); foundStart = true; } if (foundStart && savedSel.end >= charIndex && savedSel.end <= nextCharIndex) { range.setEnd(node, savedSel.end - charIndex); throw stop; } charIndex = nextCharIndex; } else { for (var i = 0, len = node.childNodes.length; i < len; ++i) { traverseTextNodes(node.childNodes[i]); } } } try { traverseTextNodes(containerEl); } catch (ex) { if (ex == stop) { rangy.getSelection().setSingleRange(range); } else { throw ex; } } } function formatText() { var el = document.getElementById('pad'); var savedSel = saveSelection(el); el.innerHTML = el.innerHTML.replace(/(<([^>]+)>)/ig,""); el.innerHTML = el.innerHTML.replace(/([0-9])/ig,"$1"); // Restore the original selection restreSelection(el, savedSel); } 

Me gustaría agradecer a Tim por la función que compartió aquí con nosotros, fue muy importante para un proyecto en el que estoy trabajando. Invidé su función un pequeño complemento jQuery al que se puede acceder aquí: https://jsfiddle.net/sh5tboL8/

 $.fn.get_selection_start = function(){ var result = this.get(0).selectionStart; if (typeof(result) == 'undefined') result = this.get_selection_range().selection_start; return result; } $.fn.get_selection_end = function(){ var result = this.get(0).selectionEnd; if (typeof(result) == 'undefined') result = this.get_selection_range().selection_end; return result; } $.fn_get_selected_text = function(){ var value = this.get(0).value; if (typeof(value) == 'undefined'){ var result = this.get_selection_range().selected_text; }else{ var result = value.substring(this.selectionStart, this.selectionEnd); } return result; } $.fn.get_selection_range = function(){ var range = window.getSelection().getRangeAt(0); var cloned_range = range.cloneRange(); cloned_range.selectNodeContents(this.get(0)); cloned_range.setEnd(range.startContainer, range.startOffset); var selection_start = cloned_range.toString().length; var selected_text = range.toString(); var selection_end = selection_start + selected_text.length; var result = { selection_start: selection_start, selection_end: selection_end, selected_text: selected_text } return result; } $.fn.set_selection = function(selection_start, selection_end){ var target_element = this.get(0); selection_start = selection_start || 0; if (typeof(target_element.selectionStart) == 'undefined'){ if (typeof(selection_end) == 'undefined') selection_end = target_element.innerHTML.length; var character_index = 0; var range = document.createRange(); range.setStart(target_element, 0); range.collapse(true); var node_stack = [target_element]; var node = null; var start_found = false; var stop = false; while (!stop && (node = node_stack.pop())) { if (node.nodeType == 3){ var next_character_index = character_index + node.length; if (!start_found && selection_start >= character_index && selection_start <= next_character_index){ range.setStart(node, selection_start - character_index); start_found = true; } if (start_found && selection_end >= character_index && selection_end <= next_character_index){ range.setEnd(node, selection_end - character_index); stop = true; } character_index = next_character_index; }else{ var child_counter = node.childNodes.length; while (child_counter --){ node_stack.push(node.childNodes[child_counter]); } } } var selection = window.getSelection(); selection.removeAllRanges(); selection.addRange(range); }else{ if (typeof(selection_end) == 'undefined') selection_end = target_element.value.length; target_element.focus(); target_element.selectionStart = selection_start; target_element.selectionEnd = selection_end; } } 

el complemento solo hace lo que necesitaba, selecciona el texto seleccionado y configura la selección de texto personalizado. También funciona en textboxes y contentEditable divs.