Améliorer la plage de texte à l'aide de JavaScript

Je voudrais souligner (appliquer css à) une certaine plage de texte, désignée par sa position de début et de fin. Cela est plus difficile à ce qu'il semble, car il peut y avoir d'autres balises dans le texte, qui doivent être ignorées.

Exemple:

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

highlight(2, 6) doit mettre en surbrillance "cdef " sans supprimer la balise.

J'ai déjà essayé d'utiliser un objet TextRange, mais sans succès.

Merci d'avance!

Vous trouverez ci-dessous une fonction pour définir la sélection dans une paire de décalages de caractères dans un élément particulier. Il s'agit d'une implémentation naïve: il ne prend pas en compte tout texte qui peut être rendu invisible (soit par CSS, soit par un élément <script> ou <style> , par exemple) et peut avoir des divergences de navigateur (IE contre tout le reste) Avec des sauts de ligne, et ne tient pas compte des espaces en collapsus (par exemple, 2 ou plus de caractères d'espace consécutifs s'effondrant dans un espace visible sur la page). Cependant, cela fonctionne pour votre exemple dans tous les principaux navigateurs.

Pour l'autre partie, la mise en surbrillance, je suggérerais d'utiliser document.execCommand() pour cela. Vous pouvez utiliser ma fonction ci-dessous pour définir la sélection, puis appeler document.execCommand() . Vous devrez rendre le document temporairement modifiable dans les navigateurs non IE pour que la commande fonctionne. Voir ma réponse ici pour le code: getSelection & SurroundContents sur plusieurs balises

Voici un jsFiddle exemple montrant l'ensemble, en travaillant dans tous les principaux navigateurs: http://jsfiddle.net/8mdX4/1211/

Et le code de réglage de la sélection:

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

Vous pouvez jeter un oeil à la façon dont fonctionne ce puissant utilitaire JavaScript qui supporte la sélection sur plusieurs éléments DOM:

MASHA (abréviation de Mark & ​​Share) vous permet de marquer des parties intéressantes du contenu de la page Web et de le partager

http://mashajs.com/index_fra.html

C'est aussi sur GitHub https://github.com/SmartTeleMax/MaSha

Fonctionne même sur Mobile Safari et IE!

La solution suivante ne fonctionne pas pour IE, vous devrez appliquer des objets TextRange, etc. pour cela. Comme cela utilise des sélections pour effectuer cela, il ne faut pas casser le code HTML dans des cas normaux, par exemple:

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

Avec le highlight(3,6);

les sorties:

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

Prenez note de la façon dont il enveloppe le premier caractère en dehors de la portée en un, puis le reste dans la span en un nouveau. Où, comme s'il allait l'ouvrir au caractère 3 et se terminant au caractère 6, il donnerait un marquage invalide comme:

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

Le code:

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

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

Modifier On dirait au moins que mon FF4 a eu des problèmes avec

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

Mais en même temps, il semble fonctionner sans elle, alors je l'ai simplement changé

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

Modifier comme Tim Appuyé, la modification est seulement disponible à partir de FF4 en avant, donc j'ai pris une approche différente pour obtenir la sélection, qui n'a pas besoin de la méthode de modification, dans l'espoir de rendre un peu plus compatible avec le navigateur (IE a toujours besoin de son propre Solution).

Le code:

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

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

Découvrez https://github.com/mir3z/texthighlighter qui fait très bien.