Problème de sélection de texte sélectionné Javascript

J'ai une page html avec du contenu texte. Lorsque vous sélectionnez un texte et que vous appuyez sur le bouton de surbrillance, je peux changer le style du texte sélectionné pour le mettre en surbrillance. Pour implémenter cette fonctionnalité, j'ai écrit la méthode suivante.

sel = window.getSelection(); var range = sel.getRangeAt(0); var span = document.createElement('span'); span.className = "highlight" + color; range.surroundContents(span); 

Cela fonctionne bien si vous choisissez un texte sans balise html, mais lorsque le texte a une balise html entre les deux, il donne une erreur

Impossible d'exécuter 'surroundContents' sur 'Range': The Range a partiellement sélectionné un nœud non-Texte.

Comment résoudre ce problème. Est-il possible de mettre en évidence le même séparément pour chaque partie (divisé par les balises html)?

Voir Range.extractContents :

 document.getElementById('execute').addEventListener('click', function() { var range = window.getSelection().getRangeAt(0), span = document.createElement('span'); span.className = 'highlight'; span.appendChild(range.extractContents()); range.insertNode(span); }); 
 .highlight { background-color: yellow; } 
 <div id="test"> Select any part of <b>this text and</b> then click 'Run'. </div> <button id="execute">Run</button> 

Plutôt que de réinventer la roue, j'utiliserais les fonctionnalités de mise en surbrillance de Rangy .

J'ai creusé le violon que RGraham a créé et créé un nouveau violon qui montre comment cela fonctionne. Voici comment cela se fait:

 var applier = rangy.createClassApplier("highlight"); var highlighter = rangy.createHighlighter(); highlighter.addClassApplier(applier); document.getElementById('execute').addEventListener('click', function() { highlighter.removeAllHighlights(); highlighter.highlightSelection("highlight"); }); 

Ce qui fait, c'est créer un surligneur qui définira la classe de highlight sur des éléments qui sont entièrement à l'intérieur de la sélection, et crée des extensions avec la classe de highlight au besoin pour les éléments qui dépassent la sélection. Lorsque le bouton avec l' execute id est cliqué, les anciens faits saillants sont supprimés et les nouveaux faits saillants sont appliqués.

La fonctionnalité du surligneur fait partie de la sortie de Rangy qui sont considérés comme "alpha". Cependant, j'ai utilisé constamment des versions alpha de Rangy depuis quelques années, mais il a été extrêmement rare que j'ai trouvé un problème avec ma demande que je puisse retrouver à Rangy. Et les quelques fois où j'ai trouvé un problème avec Rangy, Tim Down (son auteur) était très réactif.

essaye ça:

 newNode.appendChild(range.extractContents()) 

Selon MDN :

Les nœuds partiellement sélectionnés sont clones pour inclure les balises parentales nécessaires pour rendre le fragment de document valide.

Alors que Range.surroundContents :

Une exception sera lancée, cependant, si la plage divise un noeud non-texte avec un seul de ses points limites. Autrement dit, contrairement à l'alternative ci-dessus, s'il y a des noeuds partiellement sélectionnés, ils ne seront pas clonés et l'opération échouera.

N'a pas testé, mais …

Cette solution est un peu délicate, mais je trouve qu'elle serait suffisante

Lorsque vous verrez de près dans l'objet de sélection que nous obtenons en appelant window.getSelection (). GetRangeAt (0)

Vous verrez qu'il existe 4 propriétés: startContainer startOffset endContainer endOffset

Donc, maintenant, vous devez commencer avec startContainer avec startOffset et commencer à mettre vos nœuds d'extension nécessaires à partir de là. Si maintenant, endContainer est un nœud différent, vous devez commencer à parcourir les nœuds de startContainer à endContainer. Pour le déplacement, vous devez vérifier les noeuds enfants et les nœuds frères et soeurs que vous pouvez obtenir des objets DOM, alors commencez par startContainer, passez par tous ses enfants et Vérifiez si le noeud enfant est un élément en ligne, puis appliquez l'étiquette de portée autour de celui-ci, puis vous devez écrire quelques codages pour différents cas d'angle

La solution est vraiment délicate. Je trouve en quelque sorte une solution de contournement. Voir mon violon

 function highlight() { var range = window.getSelection().getRangeAt(0), parent = range.commonAncestorContainer, start = range.startContainer, end = range.endContainer; var startDOM = (start.parentElement == parent) ? start.nextSibling : start.parentElement; var currentDOM = startDOM.nextElementSibling; var endDOM = (end.parentElement == parent) ? end : end.parentElement; //Process Start Element highlightText(startDOM, 'START', range.startOffset); while (currentDOM != endDOM && currentDOM != null) { highlightText(currentDOM); currentDOM = currentDOM.nextElementSibling; } //Process End Element highlightText(endDOM, 'END', range.endOffset); } function highlightText(elem, offsetType, idx) { if (elem.nodeType == 3) { var span = document.createElement('span'); span.setAttribute('class', 'highlight'); var origText = elem.textContent, text, prevText, nextText; if (offsetType == 'START') { text = origText.substring(idx); prevText = origText.substring(0, idx); } else if (offsetType == 'END') { text = origText.substring(0, idx); nextText = origText.substring(idx); } else { text = origText; } span.textContent = text; var parent = elem.parentElement; parent.replaceChild(span, elem); if (prevText) { var prevDOM = document.createTextNode(prevText); parent.insertBefore(prevDOM, span); } if (nextText) { var nextDOM = document.createTextNode(nextText); parent.appendChild(nextDOM); } return; } var childCount = elem.childNodes.length; for (var i = 0; i < childCount; i++) { if (offsetType == 'START' && i == 0) highlightText(elem.childNodes[i], 'START', idx); else if (offsetType == 'END' && i == childCount - 1) highlightText(elem.childNodes[i], 'END', idx); else highlightText(elem.childNodes[i]); } } 
 if (window.getSelection) { var sel = window.getSelection(); if (!sel) { return; } var range = sel.getRangeAt(0); var start = range.startContainer; var end = range.endContainer; var commonAncestor = range.commonAncestorContainer; var nodes = []; var node; for (node = start.parentNode; node; node = node.parentNode){ var tempStr=node.nodeValue; if(node.nodeValue!=null && tempStr.replace(/^\s+|\s+$/gm,'')!='') nodes.push(node); if (node == commonAncestor) break; } nodes.reverse(); for (node = start; node; node = getNextNode(node)){ var tempStr=node.nodeValue; if(node.nodeValue!=null && tempStr.replace(/^\s+|\s+$/gm,'')!='') nodes.push(node); if (node == end) break; } for(var i=0 ; i<nodes.length ; i++){ var sp1 = document.createElement("span"); sp1.setAttribute("class", "highlight"+color ); var sp1_content = document.createTextNode(nodes[i].nodeValue); sp1.appendChild(sp1_content); var parentNode = nodes[i].parentNode; parentNode.replaceChild(sp1, nodes[i]); } }