Planteamiento y contexto
Un lector de documentos muestra coincidencias de búsqueda, anotaciones y el resultado actual en texto enriquecido. Envolver cada coincidencia en un span alteraría la selección, la edición y el diseño. Diseña una solución con la CSS Custom Highlight API que cubra el ciclo de vida de Range, actualizaciones del registro, cascada, estados accesibles, respaldo y rendimiento. Esta es una pregunta frontend sobre renderizado de texto y mejora progresiva.
Qué evalúa el entrevistador
- Distinguir entre objetos JavaScript Range, Highlight y
::highlight(). - Reconstruir rangos desactualizados tras el reemplazo o edición de texto.
- Entender que los resaltados no alteran el DOM y pueden combinarse en cascada con
::selection. - Diseñar contraste, resultado actual, foco y semántica de teclado.
- Proporcionar un respaldo seguro y cuantificar el costo.
Preguntas de clarificación a realizar
- ¿Los navegadores y WebViews de destino soportan la API?
- ¿El documento es editable, está virtualizado o se vuelve a renderizar con frecuencia?
- ¿Cuántas coincidencias existen y puede una coincidencia cruzar nodos de texto?
- ¿Se requieren modo oscuro, colores forzados y lectores de pantalla?
- ¿Es aceptable un respaldo basado en DOM marcado cuando no hay soporte?
Una respuesta de 30 segundos
“Representaría cada coincidencia como un Range, agruparía los rangos en un Highlight y lo registraría en CSS.highlights para estilizarlo con ::highlight(name). Cuando el texto cambie, eliminaría los registros antiguos y reconstruiría a partir de una versión de texto y desplazamientos (offsets) en lugar de retener nodos desconectados. Separaría las capas de búsqueda, anotaciones y resultado actual; expondría el recuento y el estado de teclado más allá del color. Detectaría el soporte, aplicaría un respaldo seguro de texto marcado y mediría la cantidad de coincidencias, el p95 de actualización, los cuadros por segundo y la memoria.”
Respuesta detallada
Paso 1: Crear rangos y resaltados
La API permite que JavaScript cree objetos Range de texto arbitrarios, los combine en un Highlight y registre ese Highlight en el HighlightRegistry. CSS estiliza los rangos con ::highlight(name) sin agregar elementos envoltorios.
const range = new Range();
range.setStart(textNode, start);
range.setEnd(textNode, end);
const hit = new Highlight(range);
CSS.highlights.set("search", hit);El código de producción aún debe manejar rangos entre múltiples nodos y el ciclo de vida de los nodos.
Paso 2: Reconciliar actualizaciones de texto
Persiste una versión del documento, la ruta del nodo de texto y los desplazamientos de caracteres con cada coincidencia. Volver a renderizar o editar puede desconectar un nodo, haciendo que su Range quede obsoleto. Elimina las entradas antiguas del registro y reconstruye a partir del nuevo índice de texto; no conserves únicamente referencias al DOM.
Paso 3: Gestionar capas
Usa nombres separados para la búsqueda, las anotaciones, el resultado actual y las sugerencias de corrección ortográfica. Define la cascada prevista con ::selection y otros resaltados del agente de usuario. Actualizar un único objeto de resultado actual no debería forzar la reconstrucción de todas las coincidencias.
Paso 4: Proporcionar retroalimentación accesible
El color no puede ser la única señal. Expón el recuento del resultado actual, navegación enfocable, un indicador de foco visible y alternativas para colores forzados. No asumas que un lector de pantalla anunciará un resaltado por sí mismo; la lista de resultados debe exponer el estado semántico.
Paso 5: Controlar el rendimiento
Aplica debounce a la entrada, agrupa las actualizaciones del registro por lotes y limita el trabajo a una ventana visible cuando sea posible. Mide el escaneo de texto, el p95 de actualización, el cálculo de estilos, los cuadros de desplazamiento y la memoria. Evita una mutación del registro por cada pulsación de tecla.
Paso 6: Respaldo progresivo
Detecta características y elige Custom Highlight, DOM marcado o una lista de resultados simple. Un respaldo basado en DOM debe escapar el texto, preservar enlaces y evitar envoltorios duplicados anidados. La salida del servidor debe permanecer legible antes de la mejora en el cliente.
Paso 7: Probar y aceptar
Prueba coincidencias entre múltiples nodos, re-renderizado, edición, modo oscuro, colores forzados, copiado, virtualización, RTL y navegadores sin soporte. Compara el texto resultante, el orden del teclado, el primer pintado, los FPS de desplazamiento, el p95 de actualización y la memoria; el respaldo no debe perder coincidencias.
Respuesta modelo
“Mantendría el DOM del documento sin cambios: representaría las coincidencias como Ranges, las agruparía en Highlights, registraría los nombres en CSS.highlights y las estilizaría con ::highlight(). Cada resultado lleva una versión de texto y un desplazamiento; re-renderizar elimina los registros obsoletos y los reconstruye. La búsqueda, las anotaciones y el resultado actual tienen capas separadas, mientras que el recuento, el foco y el estado semántico hacen que el resultado actual sea accesible.
La detección de características selecciona Custom Highlight o un respaldo seguro de DOM marcado/lista de resultados. El debounce, las actualizaciones por lotes y los límites de ventana visible controlan el costo. La aceptación cubre rangos entre nodos, copiado, edición, modo oscuro, colores forzados, virtualización y navegadores no compatibles, comparando p95, FPS, memoria e igualdad de resultados.”
Errores comunes
- Envolver cada coincidencia en spans → altera la estructura del texto y la selección → preferir Range/Highlight.
- Retener Ranges antiguos → los re-renderizados desconectan nodos → reconstruir por versión de texto.
- Usar solo color → usuarios con colores forzados y daltonismo pierden el estado → agregar estado semántico y foco.
- Mutar en cada tecla → provoca churn de estilos → aplicar debounce y procesar por lotes.
- Concatenar HTML de respaldo → genera riesgo de inyección y anidamiento → escapar y delimitar el rango marcado.
Preguntas de seguimiento y respuestas
Pregunta de seguimiento 1: ¿Por qué no usar spans?
Custom Highlight deja el DOM intacto, reduciendo la interferencia con la selección, la edición y el diseño. Los spans siguen siendo un respaldo de compatibilidad con controles de escape y anidamiento.
Pregunta de seguimiento 2: ¿Qué le sucede a un Range después de editar?
Su nodo puede ser reemplazado o desconectado. Reconstruye a partir de la nueva versión de texto y los desplazamientos en lugar de usar la referencia antigua.
Pregunta de seguimiento 3: ¿Los lectores de pantalla anunciarán el resaltado?
No asumas que lo harán. Expón el recuento de resultados, el estado actual y la navegación por teclado como texto semántico.
Pregunta de seguimiento 4: ¿Cómo demuestras que no hay regresiones?
Usa documentos y recuentos de coincidencias fijos para comparar el p95 de actualización, el trabajo de estilo, los FPS de desplazamiento, la memoria, el comportamiento de copiado y la virtualización.