tag - Javascript seleccionado texto resaltando problema
title of page html (6)
En lugar de reinventar la rueda, Rangy las capacidades de resaltado de Rangy .
Bifurqué el violín que RGraham creó y creó un nuevo violín que muestra cómo funciona. Así es como se hace:
var applier = rangy.createClassApplier("highlight");
var highlighter = rangy.createHighlighter();
highlighter.addClassApplier(applier);
document.getElementById(''execute'').addEventListener(''click'', function() {
highlighter.removeAllHighlights();
highlighter.highlightSelection("highlight");
});
Lo que esto hace es crear un resaltador que establecerá la clase de highlight
en elementos que están totalmente dentro de la selección, y creará intervalos con la clase de highlight
según sea necesario para los elementos que se encuentran entre la selección. Cuando se hace clic en el botón con la execute
ID, se eliminan los resaltados antiguos y se aplican los resaltados nuevos.
La funcionalidad del resaltador es parte del lanzamiento de Rangy que se considera "alfa". Sin embargo, he estado usando sistemáticamente los lanzamientos alfa de Rangy durante algunos años, pero ha sido extremadamente raro encontrar un problema con mi aplicación que pudiera rastrear hasta Rangy. Y las pocas veces que encontré un problema con Rangy, Tim Down (su autor) fue bastante receptivo.
Tengo una página html con contenido de texto. Al seleccionar cualquier texto y presionar el botón de resaltar, puedo cambiar el estilo del texto seleccionado para resaltar el mismo. Para implementar esta característica, he escrito el siguiente método.
sel = window.getSelection();
var range = sel.getRangeAt(0);
var span = document.createElement(''span'');
span.className = "highlight" + color;
range.surroundContents(span);
Esto funciona bien si elige un texto sin etiqueta html, pero cuando el texto tiene alguna etiqueta html en medio, está dando un error
Error al ejecutar ''surroundContents'' en ''Range'': el Range ha seleccionado parcialmente un nodo que no es de texto.
Cómo resolver este problema. ¿Es posible resaltar lo mismo por separado para cada parte (dividido por etiquetas html)?
Esta solución es un poco complicada, pero me parece suficiente.
Cuando verás de cerca en el objeto de selección que obtenemos a través de la llamada
window.getSelection().getRangeAt(0)
Verá que hay 4 propiedades: startContainer
, startOffset
, endContainer
, endOffset
.
Así que ahora necesita comenzar con startContainer
con startOffset
y comenzar a colocar sus nodos de startOffset
necesarios desde allí.
Si ahora endContainer
es un nodo diferente, entonces necesita comenzar a atravesar nodos desde startContainer
hasta endContainer
Para atravesar debe verificar si hay nodos secundarios y nodos hermanos que puede obtener de los objetos DOM. Por lo tanto, primero pase por startContainer
, startContainer
todo su hijo y compruebe si el nodo hijo está en el elemento en línea, luego aplique la etiqueta de separación alrededor de él, y luego deberá escribir algunos códigos para varios casos de esquina.
La solución es realmente complicada. De alguna manera encuentro una solución. Ver mi fiddle
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]);
}
}
Ver 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>
prueba esto:
newNode.appendChild(range.extractContents())
según Range.extractContents :
Los nodos seleccionados parcialmente se clonan para incluir las etiquetas principales necesarias para que el fragmento de documento sea válido.
Mientras que Range.surroundContents :
Sin embargo, se lanzará una excepción si el Rango divide un nodo que no es de Texto con solo uno de sus puntos de límite. Es decir, a diferencia de la alternativa anterior, si hay nodos seleccionados parcialmente, no se clonarán y, en cambio, la operación fallará.
No probé, pero ...
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]);
}
}