Introducción
La palabra clave, categoría, orden y página de Fieldnote ya pueden viajar en una URL. Su controlador preparado reemplaza la entrada actual del historial tras cada búsqueda. Eso mantiene útiles los enlaces directos, pero pierde la vista anterior: quien realiza varias búsquedas no puede recorrerlas con Back de forma fiable. Distinguirás las nuevas acciones del usuario de la restauración y harás funcionar el historial propio del navegador.
Este laboratorio independiente prepara los archivos completos de selección, normalización y URL del anterior; no requiere ninguna VM previa. Necesitas el contrato de cuatro estados de ese laboratorio y conocimientos básicos de funciones, objetos, selección del DOM y eventos de JavaScript. Aprenderás pushState, replaceState, popstate y por qué compartir necesita la URL en lugar de una antigua preferencia local o un objeto de historial en memoria. No hay solicitudes asíncronas ni routers de frameworks. Los 40 minutos son una estimación del autor.
Usa Workspace y Fieldnote. Edita únicamente /home/labex/project/fieldnote/work/app.js. Conserva selection.js, state.js, url.js, catalog.js, view.js, HTML/CSS, Study independiente y el servicio. El renderizador está preparado: muestra el título, descripción, fecha, hora y lugar propios de cada evento seleccionado. El sitio permanece en inglés. Un enlace solo abre mientras esté disponible la ruta pública de esta VM; esto no es una publicación en Internet.
Crear visitas para los cambios confirmados del usuario
En este paso, mantendrás la inicialización en una entrada del historial y crearás una visita nueva cuando se confirme una búsqueda o página diferente.
En Fieldnote, elige Open Study view in a new page. Es una página independiente del navegador, no la pestaña integrada del producto. Lee su resultado de print & fold, Make, Soonest first, página 2. Elige Previous, cambia Keyword a sound + light, Category a Listen, Sort by a Title A–Z y activa Search. La página vuelve a 1. Usa el botón Back del navegador, no el botón Previous del sitio: restaura palabra clave, categoría, orden, página y registros completos coincidentes anteriores. Usa Forward del navegador para recuperar la vista nueva. Este es el ejemplo normal; no completa tu trabajo.
Esta es la vista Study independiente antes de editar. Su href compartido describe la misma página visible; no completa Work.

Cierra Study y elige Open Work view in a new page en la entrada de Fieldnote. Los archivos de trabajo contienen todas las funciones de G1, pero el controlador preparado aún reemplaza entradas. Cambia a Workspace. cd establece el directorio de esta terminal persistente; cat lee un archivo sin cambiarlo:
cd /home/labex/project/fieldnote/work
cat app.js
La función show actual llama a normalize, crea una URL con stateURL, reemplaza la dirección y pide a draw que muestre los registros seleccionados. Esas funciones auxiliares proceden de los archivos preparados state.js, url.js, view.js y selection.js. Conservarás ese orden distinguiendo dos motivos para mostrar una vista.
history.pushState(null, '', url) añade una entrada del mismo origen y cambia la dirección sin solicitar una página nueva. replaceState solo actualiza la entrada actual. El primer argumento es un estado asociado opcional; usamos null porque todo el estado necesario ya está en la URL. La cadena vacía es el argumento de título no utilizado de la API. Ninguna llamada dispara popstate por sí misma. El siguiente paso trata el recorrido real del historial. La normalización inicial usa replace, de modo que abrir un enlace no añade una visita extra. La búsqueda y los botones usan push cuando la URL confirmada cambia; enviar una vista sin cambios no necesita duplicar la entrada.
document.querySelector('form') encuentra el formulario preparado. form.elements.q accede al control llamado q. preventDefault impide la navegación predeterminada del formulario para que este controlador actualice la vista. Un listener se ejecuta cuando sucede el evento indicado. La sintaxis spread {...current, page: ...} copia los campos del estado actual y luego reemplaza su página, conservando los filtros confirmados. Editar un control es solo un borrador hasta Search; los botones de página utilizan el estado confirmado.
Guarda este reemplazo completo. cat > app.js reemplaza solo ese archivo. El here-document <<'JS' entre comillas mantiene literal su contenido; termina con JS solo en una línea:
cat > app.js <<'JS'
const form = document.querySelector('form');
let current;
function show(raw, mode) {
current = normalize(raw);
const url = stateURL(current);
if (mode === 'push' && url.href !== location.href) {
history.pushState(null, '', url);
} else if (mode === 'replace') {
history.replaceState(null, '', url);
}
draw(current, matches(current));
}
form.addEventListener('submit', event => {
event.preventDefault();
show({q: form.elements.q.value, category: form.elements.category.value,
sort: form.elements.sort.value, page: 1}, 'push');
});
document.querySelector('#previous').addEventListener('click', () =>
show({...current, page: current.page - 1}, 'push'));
document.querySelector('#next').addEventListener('click', () =>
show({...current, page: current.page + 1}, 'push'));
show(readURL(), 'replace');
document.documentElement.dataset.ready = 'true';
JS
&& exige ambas condiciones, de modo que solo una petición de push con dirección diferente crea una visita. else if elige reemplazo para inicializar. Los archivos preparados siguen siendo responsables de normalizar y de las cuatro claves del curso; se conservan la ruta real del proxy, los parámetros extra y el fragmento. El marcador final data-ready indica que el controlador terminó su dibujo inicial; la aparición de un control HTML no demuestra que JavaScript haya terminado.
Vuelve a la página de trabajo independiente y actualízala. Busca print & fold, Make, Soonest first, y elige Next. Observa la edición del mapa y el evento de North room de la segunda página. Cambia a sound + light, Listen, Title A–Z y envía: la página 1 muestra los dos eventos de sonido distintos. Activa Open this view in a new page y actualiza esa nueva página. Debe conservar Title A–Z y las fechas y lugares correctos. No hace falta una preferencia en caché; los cuatro valores confirmados pertenecen a la URL compartida, incluido un orden no predeterminado. Cierra la página nueva y conserva la original.
Todavía no esperes que Back redibuje la página de trabajo: las entradas nuevas existen, pero falta el listener de restauración. Ejecuta Check de este paso: verifica el comportamiento de URL directa, nuevas visitas útiles y reemplazo inicial, sin exigir el listener del siguiente paso. Si una errata en JavaScript detiene las actualizaciones, usa cat app.js, compara el bloque completo, reemplázalo y actualiza. Guardar el archivo corregido no borra el catálogo preparado ni requiere reiniciar la VM.
Restaurar vistas con Back y Forward reales
En este paso, leerás la URL tras una navegación real por el historial y demostrarás que otro contexto de navegador puede reconstruir la misma vista.
Back y Forward cambian la entrada actual. El navegador emite popstate cuando recorre entradas aplicables del mismo documento; llamar a pushState no lo emite. Escucha el evento real, lee la URL actual, normaliza y dibuja la vista. No hagas push desde ese listener: crearías una visita nueva hacia delante en lugar de restaurar la existente. La URL también funciona sin un event.state en memoria, por ejemplo en un enlace abierto directamente. No uses localStorage como autoridad ni guardes un orden solamente en un objeto de historial.
Cambia a Workspace. Añade un listener al archivo guardado en el paso anterior. A diferencia de >, >> añade sin borrar el contenido existente. Ejecuta este bloque una vez; si lo añades dos veces por error, reemplaza app.js por el bloque completo anterior y añádelo una sola vez.
cat >> app.js <<'JS'
window.addEventListener('popstate', () => show(readURL(), 'replace'));
JS
El callback flecha se ejecuta en un recorrido nativo del historial. Reutiliza la misma ruta show, por lo que controles, orden de registros, etiqueta de página, límites desactivados y href compartido real coinciden con la nueva URL actual. Reemplazar aquí puede normalizar una entrada equivalente, pero no debe añadir una entrada. El flujo del estudiante no simula eventos ni utiliza atajos de prueba invisibles.
Vuelve a la página de trabajo original. Para obtener una entrada inicial clara, abre desde su barra de direcciones la URL real /work/ con esta consulta, conservando el origen y la ruta reales:
?edition=november&q=print%20%26%20fold&category=make&sort=title&page=2#directory-title
Ese es el estado A: Title A–Z, página 2, Print & Fold: map edition y después Print & Fold: tiny type. Lee sus horas y lugares. Crea B eligiendo Previous: la página 1 tiene los dos registros simples Print & Fold, seguidos de Print & Fold: colour. Crea C enviando sound + light, Listen, Soonest first: página 1 de 1, dos fechas distintas. Estas acciones deben conservar edition y el fragmento. Cambiar los filtros reinicia la página.
Usa Back real dos veces: C → B → A. En cada entrada compara todos los controles, la página, los títulos ordenados y la fecha, hora y lugar de cada evento. Ahora usa Forward dos veces: A → B → C. Repite una vez más ese ciclo Back/Back/Forward/Forward; restaurar no debe añadir visitas silenciosamente ni perder la cadena de avance. Cambiar de pestaña del producto no recorre el historial; Previous del sitio es una acción de paginación, no Back del navegador.
En C, activa el enlace real Open this view in a new page. La página nueva debe mostrar el mismo estado confirmado. Actualízala y vuelve a comparar. El verificador también abre el enlace exacto generado en un contexto limpio sin antiguo localStorage ni estado de historial. En tu navegador normal, esa página es otra pestaña, no una afirmación de que todo el almacenamiento esté aislado. No se necesita una API de portapapeles ni un mensaje Copied sin probar.
Vuelve a la página original, retrocede a B y actualiza. Debe seguir mostrando B porque actualizar lee la URL. Una palabra clave cambiada pero no enviada es un borrador que se descarta al actualizar. Pruébalo expresamente: escribe unsubmitted draft sin Search, actualiza y observa la palabra clave confirmada de B restaurada. Como error habitual, envía prnit, lee el mensaje válido sin resultados y corrige a print & fold, Make, y envía. Un resultado vacío no debe producir una tarjeta de respaldo falsa.
Con ancho de escritorio, unos 768 píxeles y unos 390, compara las tarjetas completas, controles y enlace. Usa Tab y Enter para realizar una búsqueda e inspecciona la indicación de foco visible; conserva la estructura adaptable preparada. Ejecuta Check. La comprobación ejecuta tu frontend sin cambios en un entorno aislado, recorre Back/Forward reales y abre un contexto limpio, comprueba cada registro visible contra el catálogo independiente y conserva tu área de trabajo activa. No crea cuentas ni publica un sitio permanente.
Resumen
Separaste las nuevas acciones confirmadas de la restauración inicial o histórica. Back/Forward reales, los enlaces compartidos directos y la actualización usan ahora el mismo estado basado en URL y registros completos. El desafío traslada ese comportamiento a otro catálogo y tamaño de página sin añadir un router, paginación de backend ni dependencia del almacenamiento privado.
