Seleccionar y reemplazar texto no único – JavaScript – SitePoint Forums
Estoy tratando de seleccionar y reemplazar texto no único en mi página web y luego reemplazarlos. Por ejemplo, supongamos que tengo varios elementos HTML que son hermanos y contienen el mismo texto. Si uso Regex para seleccionar texto en uno de ellos y luego trato de modificarlo (es decir, envuélvalo en etiquetas de intervalo, reemplácelo con texto diferente, elimínelo, etc.), Regex solo mostrará la primera coincidencia que encuentre. Puede o no ser el texto que seleccioné.
Investigué un poco sobre el uso de la API de Treewalker para esto, pero los ejemplos que vi no fueron muy útiles. Ayude a sugerir la mejor solución para manejar situaciones en las que el mismo texto seleccionado está presente en varios elementos HTML.
¿Suena esto como lo que tienes en mente?
Le vendría bien un poco de limpieza/refactorización
const getDuplicates = function() {
// a map of existing text content
// e.g. { 'Some other content': true }
const exists = {}
const isNewLine = function (node) {
return /^[\n\r]+\s*/.test(node.textContent)
}
return document.createNodeIterator(
document.body,
NodeFilter.SHOW_TEXT,
(node) => {
// ignore new line text nodes e.g. '\n '
if (isNewLine(node)) return NodeFilter.FILTER_SKIP
// if node with same text content already exists
// return duplicate
if (exists[node.textContent]) {
return NodeFilter.FILTER_ACCEPT
}
// first appearance then store in exists
exists[node.textContent] = true
return NodeFilter.FILTER_SKIP
},
false
);
}
// get the iterator
const duplicatesIterator = getDuplicates()
let currentNode
let duplicateNodes = []
// iterate and store in an array
while (currentNode = duplicatesIterator.nextNode()) {
duplicateNodes.push(currentNode)
}
// creates a span around a text element
const createSpan = function(textContent) {
const span = document.createElement('span')
span.className="duplicate"
span.textContent = textContent
return span
}
// loop through duplicates and wrap a span
duplicateNodes.forEach(
(node) => node.replaceWith(createSpan(node.textContent))
)
Si bien estoy seguro de que el código RPG funciona muy bien…
¿Por qué estás haciendo esto con Javascript? Parece que nos falta parte de la declaración de la situación/problema aquí…
Si esta es su página web… vaya al HTML en el editor de HTML de su elección, encuentre los duplicados y… ¿haga lo que quiera hacer con ellos?
1 Me gusta
¡Eso sería tan fácil!
Me alegro de haber hecho esta versión en ese entonces también.
const walkTheDom = function (node, callback) {
callback(node)
node = node.firstChild
while (node) {
walkTheDom(node, callback)
node = node.nextSibling
}
}
const isValidTextNode = function (node) {
const isNewLine = /^[\n\r]+\s*/
return (node.nodeType === 3 && !isNewLine.test(node.textContent))
}
const getDuplicates = function (root = document.body) {
const exists = {}
const duplicates = []
walkTheDom(root, (node) => {
if (!isValidTextNode(node)) return
// if node with same text content already exists
// return duplicate
if (exists[node.textContent]) duplicates.push(node)
// first appearance then store in exists
exists[node.textContent] = true
})
return duplicates
}
// creates a span around a text element
const createSpan = function(textContent) {
const span = document.createElement('span')
span.className="duplicate"
span.textContent = textContent
return span
}
// loop through duplicates and wrap a span
getDuplicates(document.body).forEach(
(node) => node.replaceWith(createSpan(node.textContent))
)
Hola a todos, Gracias por sus respuestas, especialmente rpg_digital. El motivo de la creación de esta funcionalidad es para fines de presentación. Digamos que estoy dando una presentación de videoconferencia a un grupo de personas y quiero resaltar rápidamente alguna información en mi página, si puedo resaltar o aumentar el tamaño de cualquier información (especialmente la información cargada dinámicamente). considerado importante para el grupo, esto sería muy útil ya que podría llamar la atención de todos los participantes de la reunión sobre la información resaltada.
body.innerHTML = body.innerHTML.replaceAll(new RegExp(window.getSelection().toString(),"ig"),"$0")
?
Si el texto que selecciona no coincide con el nombre de la clase o el componente HTML…
Creo que hay un problema con el cambio de body.innerHTML que perderá todos los eventListeners agregados en la página.
Hay algunas peculiaridades sobre eso y terminar con elementos huérfanos: tendré que investigar eso.
De todos modos, tomamos getSelection y replaceAll de m_hutley y los adaptamos.
const walkTheDom = function (node, callback) {
callback(node)
node = node.firstChild
while (node) {
walkTheDom(node, callback)
node = node.nextSibling
}
}
const isValidTextNode = function (node) {
const isNewLine = /^[\n\r]+\s*/
return (node.nodeType === 3 && !isNewLine.test(node.textContent))
}
const getTextParentNodes = function (root = document.body) {
const parents = []
walkTheDom(root, (node) => {
if (isValidTextNode(node)) parents.push(node.parentElement)
})
return parents
}
const highlightDuplicates = function (strgToMatch) {
const parents = getTextParentNodes()
const strgRx = new RegExp(`\\b${strgToMatch}\\b`, 'ig')
parents.forEach((parent) => {
parent.innerHTML = parent.innerHTML.replaceAll(
strgRx, `${strgToMatch}`
)
})
}
const removeHighlights = function (strgToMatch) {
const spans = document.querySelectorAll('.wrapper span.emphasise')
spans.forEach((span) => {
if (span.textContent !== strgToMatch) return
span.replaceWith(document.createTextNode(strgToMatch))
})
}
const highlightHandler = function () {
const selection = window.getSelection()
const selectedString = selection.toString().trim()
if (selectedString === '') return
const anchorNode = selection.anchorNode
// if selection is wrapped in a span, remove spans for that word/words
if (anchorNode.parentElement.matches('span.emphasise')) {
removeHighlights(anchorNode.textContent)
} else {
highlightDuplicates(selectedString)
}
}
const highlightBtn = document.querySelector('#toggle-highlight')
highlightBtn.addEventListener('click', highlightHandler)
Al código le vendría bien una refactorización. Hay un signo de interrogación en cómo selecciono los nodos. Pero por ahora, aquí hay una publicación de código.
Actualmente, debe seleccionar todas las palabras para resaltar todos los patrones; también puede seleccionar varias palabras.
El botón de resaltado es un interruptor, por lo que si selecciona la palabra resaltada o parte de ella, esto eliminará las etiquetas de espacio para las palabras duplicadas. ¿Tiene sentido?
Estoy seguro de que todavía necesita trabajo.
1 Me gusta
Esto se convirtió en un pequeño proyecto para mí.
Primero quería encajar en la selección de texto en lugar de presionar un botón. También quería que la selección se expandiera automáticamente a palabras completas.
Intenté usar interno elegir cambiar, pero eligió usar bucles while para recortar y expandir las opciones al final. Específicamente, quería hacer coincidir las opciones con espacios iniciales o finales en una palabra, en lugar de agarrar palabras vecinas.
Este fue un proyecto en sí mismo.
Ampliar la selección a palabras completas
Tengo la sensación de que esto podría lograrse en unas pocas líneas de código.
Luego repite el script que expande la opción de compatibilidad.
Resalta el texto apropiado
- Las opciones funcionan hacia adelante y hacia atrás e incluyen elementos anidados, espaciado, cursiva y más. incluye
- Seleccionar selecciones que ya contienen texto resaltado elimina el espaciado interno y rodea esa selección con un nuevo espaciado de resaltado.
- Seleccionar dentro del texto resaltado elimina el resaltado de todas las coincidencias
- El doble clic está actualmente deshabilitado debido a algunas peculiaridades. Muy extrañamente en Firefox, si selecciona dentro de un elemento en línea, window.getSelection en realidad selecciona el hermano anterior. Si le da a ese elemento en línea una visualización de bloque en línea, se seleccionará correctamente.
Sólo un poco de diversión.