tab - Resalta el rango de texto usando JavaScript
title html (4)
A continuación se muestra una función para establecer la selección en un par de desplazamientos 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 (ya sea por CSS o por estar dentro de un elemento <script>
o <style>
, por ejemplo) y puede tener discrepancias en el 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 espacios espaciales consecutivos que colapsan en un espacio visible en la página). Sin embargo, funciona para su ejemplo en todos los principales navegadores.
Para la otra parte, destacar, sugeriría usar document.execCommand()
para eso. Puede usar mi función a continuación para establecer la selección y luego llamar a document.execCommand()
. Tendrá que hacer que el documento sea editable temporalmente en navegadores que no sean IE para que el comando funcione. Ver mi respuesta aquí para el código: getSelection & surroundContents en varias etiquetas
Aquí hay un ejemplo jsFiddle que muestra todo, que funciona en todos los principales navegadores: http://jsfiddle.net/8mdX4/1211/
Y el código de configuración de 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();
}
}
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.
Ejemplo:
<div>abcd<em>efg</em>hij</div>
highlight(2, 6)
necesita resaltar "cdef
" sin quitar la etiqueta.
Ya intenté usar un objeto TextRange, pero sin éxito.
¡Gracias por adelantado!
Basado en las ideas del plugin jQuery.highlight .
private highlightRange(selector: JQuery, start: number, end: number): void {
let cur = 0;
let replacements: { node: Text; pos: number; len: number }[] = [];
let dig = function (node: Node): void {
if (node.nodeType === 3) {
let nodeLen = (node as Text).data.length;
let next = cur + nodeLen;
if (next > start && cur < end) {
let pos = cur >= start ? cur : start;
let len = (next < end ? next : end) - pos;
if (len > 0) {
if (!(pos === cur && len === nodeLen && node.parentNode &&
node.parentNode.childNodes && node.parentNode.childNodes.length === 1 &&
(node.parentNode as Element).tagName === ''SPAN'' && (node.parentNode as Element).className === ''highlight1'')) {
replacements.push({
node: node as Text,
pos: pos - cur,
len: len,
});
}
}
}
cur = next;
}
else if (node.nodeType === 1) {
let childNodes = node.childNodes;
if (childNodes && childNodes.length) {
for (let i = 0; i < childNodes.length; i++) {
dig(childNodes[i]);
if (cur >= end) {
break;
}
}
}
}
};
selector.each(function (index, element): void {
dig(element);
});
for (let i = 0; i < replacements.length; i++) {
let replacement = replacements[i];
let highlight = document.createElement(''span'');
highlight.className = ''highlight1'';
let wordNode = replacement.node.splitText(replacement.pos);
wordNode.splitText(replacement.len);
let wordClone = wordNode.cloneNode(true);
highlight.appendChild(wordClone);
wordNode.parentNode.replaceChild(highlight, wordNode);
}
}
La siguiente solución no funciona para IE, necesitará aplicar objetos TextRange, etc. para eso. Como esto utiliza selecciones para realizar esto, no debería romper el HTML en casos normales, por ejemplo:
<div>abcd<span>efg</span>hij</div>
Con highlight(3,6);
productos:
<div>abc<em>d<span>ef</span></em><span>g</span>hij</div>
Tome nota de cómo envuelve el primer personaje fuera del tramo en un em
, y luego el resto dentro del span
en uno nuevo. Donde como si simplemente lo abriera en el carácter 3 y terminara en el carácter 6, daría un marcado inválido 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 funcionar sin él, así que simplemente lo cambié a
if ($.browser.webkit) {
s.modify("move", "backward", "documentboundary");
}
editar como Tim Puntuado, modificar solo está disponible desde 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 propio 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/
Podrías echarle un vistazo a cómo funciona esta poderosa herramienta de JavaScript que admite selección sobre múltiples elementos DOM:
MASHA (abreviatura de Mark & Share) le permite marcar partes interesantes del contenido de la página web y compartirlo
http://mashajs.com/index_eng.html
También está en GitHub https://github.com/SmartTeleMax/MaSha
Funciona incluso en Mobile Safari e IE!