2011-06-05 12 views
30

Me gustaría resaltar (aplicar css a) un cierto rango de texto, denotado por su posición inicial y final. Esto es más difícil de lo que parece, ya que puede haber otras etiquetas dentro del texto que deben ignorarse.Margen de texto resaltado usando JavaScript

Ejemplo:..

<div>abcd<em>efg</em>hij</div> 

highlight(2, 6) necesidades para resaltar "cdef "sin quitar la etiqueta

He tratado ya con un objeto TextRange, pero sin éxito

Gracias de antemano

!
+0

¿Puedes quitar las etiquetas en una cadena temporal y luego subsergir desde esa cadena? – mc10

+0

No puede ignorar las etiquetas, de lo contrario terminará con html no válido: 'ab cd ef f'. Usted tendría que hacer algo como 'ab cd ef g' – serg

+0

Por supuesto que no puedo ignorar las etiquetas, pero sería bueno si el navegador podría resolver esos problemas para mí de alguna manera. – Vincent

Respuesta

48

A continuación se muestra una función para establecer la selección de un par de compensaciones de caracteres dentro de un elemento en particular.Esta es una implementación ingenua: no tiene en cuenta ningún texto que pueda hacerse invisible (por CSS o por estar dentro de un elemento <script> o <style>, por ejemplo) y puede tener discrepancias del navegador (IE versus todo lo demás) con saltos de línea, y no tiene en cuenta el espacio en blanco colapsado (como 2 o más caracteres espaciales consecutivos que colapsan en un espacio visible en la página). Sin embargo, funciona para su ejemplo en todos los principales navegadores.

Por la otra parte, lo más destacado, sugiero usar document.execCommand() para eso. Puede usar mi función a continuación para configurar la selección y luego llamar al document.execCommand(). Tendrá que hacer que el documento sea editable temporalmente en navegadores que no sean IE para que el comando funcione. Véase mi respuesta aquí por código: getSelection & surroundContents across multiple tags

Aquí está un ejemplo jsFiddle mostrando todo el asunto, trabajando en todos los principales navegadores: http://jsfiddle.net/8mdX4/1211/

Y el código de ajuste de la selección:

function getTextNodesIn(node) { 
    var textNodes = []; 
    if (node.nodeType == 3) { 
     textNodes.push(node); 
    } else { 
     var children = node.childNodes; 
     for (var i = 0, len = children.length; i < len; ++i) { 
      textNodes.push.apply(textNodes, getTextNodesIn(children[i])); 
     } 
    } 
    return textNodes; 
} 

function setSelectionRange(el, start, end) { 
    if (document.createRange && window.getSelection) { 
     var range = document.createRange(); 
     range.selectNodeContents(el); 
     var textNodes = getTextNodesIn(el); 
     var foundStart = false; 
     var charCount = 0, endCharCount; 

     for (var i = 0, textNode; textNode = textNodes[i++];) { 
      endCharCount = charCount + textNode.length; 
      if (!foundStart && start >= charCount 
        && (start < endCharCount || 
        (start == endCharCount && i <= textNodes.length))) { 
       range.setStart(textNode, start - charCount); 
       foundStart = true; 
      } 
      if (foundStart && end <= endCharCount) { 
       range.setEnd(textNode, end - charCount); 
       break; 
      } 
      charCount = endCharCount; 
     } 

     var sel = window.getSelection(); 
     sel.removeAllRanges(); 
     sel.addRange(range); 
    } else if (document.selection && document.body.createTextRange) { 
     var textRange = document.body.createTextRange(); 
     textRange.moveToElementText(el); 
     textRange.collapse(true); 
     textRange.moveEnd("character", end); 
     textRange.moveStart("character", start); 
     textRange.select(); 
    } 
} 
+2

¡Solución brillante @ tim-down! Su código fue adaptado para consolidar texto formateado HTML anidado. http://stackoverflow.com/questions/16226671/consolidate-stacked-dom-formatting-elements-contenteditable-div –

+0

Cuando ejecuto este código, se agrega una etiqueta span a todo el texto resaltado, ¿Puedo agregar una clase y un evento de clic a esa etiqueta span? – Prateek

+0

@prateek: ¿Supongo que te refieres al código de resaltado? Como el servidor agrega automáticamente los intervalos, no es fácil encontrarlos y modificarlos. Puede usar el [módulo aplicador de clases] (https://code.google.com/p/rangy/wiki/CSSClassApplierModule) de mi biblioteca [Rangy] (https://code.google.com/p/rangy) en su lugar . –

0

La siguiente solución no funciona para IE, necesitará aplicar objetos TextRange, etc. para eso. Este utiliza selecciones para realizar esto, no debe romper el código HTML en los casos normales, por ejemplo:

<div>abcd<span>efg</span>hij</div> 

Con highlight(3,6);

salidas:

<div>abc<em>d<span>ef</span></em><span>g</span>hij</div> 

tomar nota de cómo se envuelve la primera personaje fuera del lapso en em, y luego el resto dentro del span en uno nuevo. Donde como si acaba de abrir al carácter 3 y terminará a las de carácter 6, daría marcas inválidas como:

<div>abc<em>d<span>ef</em>g</span>hij</div> 

El código:

var r = document.createRange(); 
var s = window.getSelection() 

r.selectNode($('div')[0]); 
s.removeAllRanges(); 
s.addRange(r); 

// not quite sure why firefox has problems with this 
if ($.browser.webkit) { 
    s.modify("move", "backward", "documentboundary"); 
} 

function highlight(start,end){ 
    for(var st=0;st<start;st++){ 
     s.modify("move", "forward", "character"); 
    } 

    for(var st=0;st<(end-start);st++){ 
     s.modify("extend", "forward", "character"); 
    } 
} 

highlight(2,6); 

var ra = s.getRangeAt(0); 
var newNode = document.createElement("em"); 
newNode.appendChild(ra.extractContents()); 
ra.insertNode(newNode); 

Ejemplo: http://jsfiddle.net/niklasvh/4NDb9/

editar Parece que al menos mi FF4 tuvo algunos problemas con

s.modify("move", "backward", "documentboundary"); 

pero al mismo tiempo, parece que funciona sin él, así que sólo lo cambió a

if ($.browser.webkit) { 
     s.modify("move", "backward", "documentboundary"); 
} 

edición como Tim señalado, modificar solo están disponibles en FF4 en adelante , así que tomé un enfoque diferente para obtener la selección, que no necesita el método de modificación, con la esperanza de que sea un poco más compatible con el navegador (IE aún necesita su propia solución).

El código:

var r = document.createRange(); 
var s = window.getSelection() 

var pos = 0; 

function dig(el){ 
    $(el).contents().each(function(i,e){ 
     if (e.nodeType==1){ 
      // not a textnode 
     dig(e); 
     }else{ 
      if (pos<start){ 
       if (pos+e.length>=start){ 
       range.setStart(e, start-pos); 
       } 
      } 

      if (pos<end){ 
       if (pos+e.length>=end){ 
       range.setEnd(e, end-pos); 
       } 
      }    

      pos = pos+e.length; 
     } 
    }); 
} 
var start,end, range; 

function highlight(element,st,en){ 
    range = document.createRange(); 
    start = st; 
    end = en; 
    dig(element); 
    s.addRange(range); 

} 
highlight($('div'),3,6); 

var ra = s.getRangeAt(0); 

var newNode = document.createElement("em"); 
newNode.appendChild(ra.extractContents()); 
ra.insertNode(newNode); 

ejemplo: http://jsfiddle.net/niklasvh/4NDb9/

+0

Firefox solo implementa 'Selection.modify()' desde Firefox 4.0 y no admite todas las configuraciones de granularidad que hace WebKit. Específicamente, no es compatible con "documentboundary". Consulte https://developer.mozilla.org/en/DOM/selection/modify –

+0

Esto parece funcionar solo si no hay nada más en la página. Ver por ejemplo: http://jsfiddle.net/4NDb9/89/. Aunque Hello world está fuera del div, se está resaltando, en lugar del texto dentro del div. – Vincent

+0

@Tim Down Muy buen punto. Reescribí una buena parte de él para deshacerme ahora del método modify(). – Niklas

2

Usted puede echar un vistazo cómo funciona esta poderosa utilidad de JavaScript que admite selección sobre múltiples elementos DOM:

MASHA (abreviatura de Mark) Compartir) le permiten marcar partes interesantes del contenido de la página web y compartirlo

http://mashajs.com/index_eng.html

Es también en GitHub https://github.com/SmartTeleMax/MaSha

funciona incluso en Mobile Safari e IE!

Cuestiones relacionadas