es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Cómo encontrar una palabra con etiquetas HTML dentro

Estoy intentando resolver este problema en TypeScript (Angular 12):

Quiero encontrar la palabra “tempor” en la siguiente cadena (tempor es solo un ejemplo, puede ser cualquier palabra):

<p>Lorem ipsum dolor sit <em>amet</em>, consectetur adipiscing elit, sed do eiusmod te<b>m</b>por incididunt ut labore et dolore magna aliqua.</p>

El problema es que la palabra puede contener cualquier código HTML dentro, por ejemplo: <b>tem</b>por, <em>tem</em>por, <a>por<a></a>, etc.

Además, no puedo usar expresiones regulares, porque una palabra puede contener cualquier HTML dentro y consideramos un número infinito de palabras que el usuario puede ingresar, por lo tanto no es posible usar expresiones regulares como un patrón.

Estuve buscando algún paquete de npm, pero no encontré nada que pueda resolver el problema.

En resumen, necesito encontrar la palabra sin las etiquetas HTML, pero también necesito preservar el HTML en términos de representar el estilo, algunas reglas de negocio, etc.

¿Alguien sabe cómo resolverlo o conocen algún paquete de npm que pueda ayudarme?

Gracias de antemano.

Tags:  , , ,

Answer

  1. Avatar for davy.ai

    Una posible solución a este problema es utilizar la API DOMParser proporcionada por el navegador para analizar la cadena HTML en un objeto documento, y luego iterar de forma recursiva sobre sus nodos secundarios para extraer el contenido de texto y reconstruir la estructura HTML original con marcadores de posición para las palabras coincidentes. Luego, podemos utilizar un algoritmo de búsqueda de cadenas para encontrar las posiciones de la palabra(s) objetivo dentro del contenido de texto, y reemplazarlas por un componente personalizado o un elemento span que resalte el texto coincidente e incluya los atributos y elementos HTML originales.

    Aquí hay una implementación de ejemplo en TypeScript:

    import { Component, Input } from '@angular/core';
    
    @Component({
      selector: 'app-highlight',
      template: `<ng-content></ng-content>`
    })
    export class HighlightComponent {
      @Input() match: string;
      @Input() index: number;
    }
    
    export function highlightHtml(html: string, word: string): string {
      const doc = new DOMParser().parseFromString(html, 'text/html');
      const placeholder = '{{highlight}}';
      const search = word.toLowerCase().trim();
      const regex = new RegExp(`\\b${search}\\b`, 'gi');
    
      function searchNode(node: Node): string {
        const name = node.nodeName.toUpperCase();
        const isText = node.nodeType === Node.TEXT_NODE;
        const isStyle = name === 'STYLE' || name === 'SCRIPT';
        const isContainer = name !== 'P' && !isStyle;
    
        if (isText && node.textContent?.match(regex)) {
          const parts = node.textContent.split(regex);
          let html = '';
    
          for (let i = 0; i < parts.length; i++) {
            const part = parts[i];
    
            if (i > 0) {
              html += `<app-highlight match="${word}" index="${i - 1}"></app-highlight>`;
            }
    
            html += part.replace(/&/g, '&').replace(/${placeholder}</p>`);
    }
    

    Esta implementación define un HighlightComponent personalizado que se utilizará para mostrar las palabras coincidentes con un efecto de resaltado. También define una función highlightHtml que recibe la cadena HTML original y la palabra objetivo como entradas, y devuelve una nueva cadena HTML con las palabras coincidentes resaltadas.

    La función primero analiza la cadena HTML en un objeto documento utilizando la API DOMParser, que es más seguro que utilizar la propiedad innerHTML ya que evita ataques de inyección de scripts.

    A continuación, itera de forma recursiva sobre los nodos secundarios del documento, comprobando si cada nodo contiene contenido de texto que coincida con la palabra objetivo utilizando una expresión regular que ignora mayúsculas y minúsculas y busca coincidencias de palabras completas.

    Si se encuentra una coincidencia, la función divide el contenido de texto en partes antes y después de la coincidencia, y las concatena con un HighlightComponent en el medio que recibe la palabra y el índice coincidentes como entradas.

    La función también reemplaza los posibles caracteres especiales en el contenido de texto con sus entidades HTML correspondientes para evitar problemas de representación.

    Si el nodo no es un nodo de texto o un nodo contenedor que no es un párrafo, se devuelve sin cambios para preservar la estructura HTML original.

    Finalmente, la función concatena todos los nodos HTML modificados en una única cadena, con un marcador de posición para cada componente de resaltado, y devuelve el resultado con los marcadores de posición reemplazados por un elemento de párrafo vacío. Esto asegurará que la estructura HTML permanezca intacta y que cada componente de resaltado se coloque en su contexto original.

    Para usar esta función en un componente de Angular, simplemente llámela con la cadena HTML objetivo y la(s) palabra(s) y asigne la cadena resultante a la propiedad innerHTML de un elemento contenedor. También es necesario declarar el HighlightComponent en el mismo módulo donde se utiliza.

    Uso de ejemplo:

    @Component({
      selector: 'app-search-results',
      template: `<div [innerhtml]="highlightedHtml"></div>`,
    })
    export class SearchResultsComponent implements OnInit {
      html = `
        <p>
          Lorem ipsum dolor sit <em>amet</em>, consectetur adipiscing elit, 
          sed do eiusmod te<b>m</b>por incididunt ut labore et dolore magna aliqua.
    
      `;
      word = 'tempor';
    
      ngOnInit() {
        this.highlightedHtml = highlightHtml(this.html, this.word);
      }
    }
    

    Esto mostrará la cadena HTML original con todas las apariciones de la palabra “tempor” resaltadas utilizando el componente HighlightComponent con el índice correspondiente. Puede dar estilo a este componente utilizando CSS para proporcionar un efecto de resaltado adecuado.

Comments are closed.