"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Creación de un editor inteligente: detecte URL automáticamente y conviértalas en hipervínculos

Creación de un editor inteligente: detecte URL automáticamente y conviértalas en hipervínculos

Publicado el 2024-11-04
Navegar:283

Esta es una idea que se me ocurrió en el trabajo para mejorar la experiencia del usuario. Implica implementar un cuadro de texto que detecta automáticamente las URL y las convierte en hipervínculos a medida que el usuario escribe (código fuente Github/AutolinkEditor). Esta interesante característica es algo complicada de implementar y se deben abordar los siguientes problemas.

  • Detectar URL con precisión dentro del texto
  • Mantener la posición del cursor después de convertir la cadena URL en un hipervínculo
  • Actualice la URL de destino en consecuencia cuando los usuarios editen el texto del hipervínculo
  • Conservar saltos de línea en el texto
  • Admite pegar texto enriquecido conservando tanto el texto como los saltos de línea, con el estilo del texto coincidiendo con el formato del cuadro de texto.

Building a Smart Editor: Automatically Detect URLs and Convert Them to Hyperlinks

...
 if(target && target.contentEditable){
  ...
  target.contentEditable = true;
  target.focus();
 }
...

La conversión se realiza mediante eventos "onkeyup" y "onpaste". Para reducir la frecuencia de las conversiones, se implementa un mecanismo de retraso con "setTimeout", donde la lógica de conversión se activa solo después de que el usuario deja de escribir durante 1 segundo de forma predeterminada.

idle(func, delay = 1000) {
      ...
      const idleHandler = function(...args) {
        if(this[timer]){
          clearTimeout(this[timer]);
          this[timer] = null;
        }
        this[timer] = setTimeout(() => {
          func(...args);
          this[timer] = null;
        }, delay);

      };
      return idleHandler.bind(this);
    }

Identificar y extraer URL con expresión regular.

No tenía intención de perder tiempo creando la expresión regular perfecta para las URL coincidentes, así que encontré una utilizable a través de un motor de búsqueda. Si alguien tiene uno mejor, ¡no dude en hacérmelo saber!

...
const URLRegex = /^(https?:\/\/(([a-zA-Z0-9] -?) [a-zA-Z0-9] \.) (([a-zA-Z0-9] -?) [a-zA-Z0-9] ))(:\d )?(\/.*)?(\?.*)?(#.*)?$/;
const URLInTextRegex = /(https?:\/\/(([a-zA-Z0-9] -?) [a-zA-Z0-9] \.) (([a-zA-Z0-9] -?) [a-zA-Z0-9] ))(:\d )?(\/.*)?(\?.*)?(#.*)?/;
...

if(URLRegex.test(text)){
  result  = `${escapeHtml(text)}`;
}else {
  // text contains url
  let textContent = text;
  let match;
  while ((match = URLInTextRegex.exec(textContent)) !== null) {
    const url = match[0];
    const beforeUrl = textContent.slice(0, match.index);
    const afterUrl = textContent.slice(match.index   url.length);

    result  = escapeHtml(beforeUrl);
    result  = `${escapeHtml(url)}`;
    textContent = afterUrl;
  }
  result  = escapeHtml(textContent); // Append any remaining text
}

Restaurar la posición del cursor después de la conversión

Con las funciones document.createRange y window.getSelection, calcula la posición del cursor dentro del texto del nodo. Dado que la conversión de URL en hipervínculos solo agrega etiquetas sin modificar el contenido del texto, el cursor se puede restaurar según la posición registrada previamente. Para obtener más detalles, lea No se puede restaurar la selección después de modificar el HTML, incluso si es el mismo HTML.

Actualizar o eliminar al editar el hipervínculo
A veces creamos hipervínculos donde el texto y la URL de destino son los mismos (aquí se denominan "hipervínculos simples"). Por ejemplo, el siguiente HTML muestra este tipo de hipervínculo.

http://www.ejemplo.com
Para dichos enlaces, cuando se modifica el texto del hipervínculo, la URL de destino también debe actualizarse automáticamente para mantenerlos sincronizados. Para que la lógica sea más sólida, el enlace se volverá a convertir a texto sin formato cuando el texto del hipervínculo ya no sea una URL válida.

handleAnchor: anchor => {
  ...
    const text = anchor.textContent;
    if(URLRegex.test(text)){
      return nodeHandler.makePlainAnchor(anchor);
    }else {
      return anchor.textContent;
    }
  ...
}
...
makePlainAnchor: target => {
  ...
  const result = document.createElement("a");
  result.href = target.href;
  result.textContent = target.textContent;
  return result;
  ...
}

Para implementar esta función, almaceno los 'hipervínculos simples' en un objeto y los actualizo en tiempo real durante los eventos onpaste, onkeyup y onfocus para garantizar que la lógica anterior solo maneje hipervínculos simples.

target.onpaste = initializer.idle(e => {
  ...
  inclusion = contentConvertor.indexAnchors(target);
}, 0);

const handleKeyup = initializer.idle(e => {
  ...
  inclusion = contentConvertor.indexAnchors(target);
  ...
}, 1000);

target.onkeyup = handleKeyup;
target.onfocus = e => {
  inclusion = contentConvertor.indexAnchors(target);
}

...

indexAnchors(target) {
  const inclusion = {};
  ...
  const anchorTags = target.querySelectorAll('a');
  if(anchorTags) {
    const idPrefix = target.id === "" ? target.dataset.id : target.id;

    anchorTags.forEach((anchor, index) => {
      const anchorId = anchor.dataset.id ?? `${idPrefix}-anchor-${index}`;
      if(anchor.href.replace(/\/ $/, '').toLowerCase() === anchor.textContent.toLowerCase()) {
        if(!anchor.dataset.id){
          anchor.setAttribute('data-id', anchorId);
        }
        inclusion[[anchorId]] = anchor.href;
      }
    });
  }
  return Object.keys(inclusion).length === 0 ? null : inclusion;
  ...
}

Manejar saltos de línea y estilos

Al manejar texto enriquecido pegado, el editor automáticamente diseñará el texto con los estilos de texto del editor. Para mantener el formato, se conservarán las etiquetas
en el texto enriquecido y todos los hipervínculos. Manejar el texto de entrada es más complejo. Cuando el usuario presiona Enter para agregar una nueva línea, se agrega un elemento div al editor, que el editor reemplaza con
para mantener el formato.

node.childNodes.forEach(child => {
  if (child.nodeType === 1) { 
    if(child.tagName === 'A') { // anchar element
      const key = child.id === "" ? child.dataset.id : child.id;

      if(inclusion && inclusion[key]){
        const disposedAnchor = handleAnchor(child);
        if(disposedAnchor){
          if(disposedAnchor instanceof HTMLAnchorElement) {
            disposedAnchor.href = disposedAnchor.textContent;
          }
          result  = disposedAnchor.outerHTML ?? disposedAnchor;
        }
      }else {
        result  = makePlainAnchor(child)?.outerHTML ?? "";
      }
    }else { 
      result  = compensateBR(child)   this.extractTextAndAnchor(child, inclusion, nodeHandler);
    }
  } 
});

...
const ElementsOfBR = new Set([
  'block',
  'block flex',
  'block flow',
  'block flow-root',
  'block grid',
  'list-item',
]);
compensateBR: target => {
  if(target && 
    (target instanceof HTMLBRElement || ElementsOfBR.has(window.getComputedStyle(target).display))){
      return "
"; } return ""; }

Conclusiones

Este artículo describe algunas técnicas prácticas utilizadas para implementar un editor simple, como eventos comunes como onkeyup y onpaste, cómo usar Selección y Rango para restaurar la posición del cursor y cómo manejar los nodos de un elemento para lograr las funciones del editor. funcionalidad. Si bien las expresiones regulares no son el tema central de este artículo, una expresión regular completa puede mejorar la solidez del editor a la hora de identificar cadenas específicas (la expresión regular utilizada en este artículo permanecerá abierta a modificaciones). Puede acceder al código fuente a través de Github/AutolilnkEditor para obtener más detalles si es útil para su proyecto.

Declaración de liberación Este artículo se reproduce en: https://dev.to/oninebx/building-a-smart-editor-automatically-detect-urls-and-convert-them-to-hyperlinks-ilg?1 Si hay alguna infracción, por favor contacto Study_golang@163 .comeliminar
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3