Introdução
Fieldnote é um pequeno guia de eventos de fim de semana. Um link de busca útil deve descrever a visualização: palavra-chave, categoria, ordenação e página dos resultados. Uma pessoa que abrir o link deve ver os mesmos eventos, incluindo horário e local corretos para dois eventos de mesmo título. Você construirá essa relação de estado sobre um site responsivo preparado e um pequeno catálogo local.
Você precisa conhecer caminhos básicos no terminal, formulários HTML, layout e foco com CSS, além de arrays, objetos, funções, seleção do DOM e eventos JavaScript. Este laboratório apresenta filtragem de arrays, comparadores, limites de páginas, URL e URLSearchParams. Busca e análise de URL têm várias aplicações; aqui o resultado é uma visualização completa e reproduzível com quatro valores. O renderizador preparado cria cartões a partir dos registros selecionados pelo seu código. Não é necessário conhecer backend ou frameworks. Todos os eventos são sintéticos. Os 55 minutos são uma estimativa do autor, incluindo leitura e erros comuns.
Use Workspace para comandos e Fieldnote para a página de entrada. Este laboratório começa de forma independente. Edite apenas /home/labex/project/fieldnote/work/selection.js, state.js e url.js; app.js é o controlador preparado neste laboratório e será seu trabalho no próximo. Preserve catalog.js, view.js, index.html, style.css, a visualização Study separada e o serviço preparado. O controlador já envia formulários e redesenha a página, usando replaceState para atualizar a entrada atual. O histórico é comportamento preparado aqui, ainda não um requisito de aprendizagem. Os rótulos do site permanecem em inglês.
Selecionar registros reais em uma ordem estável
Neste passo, você abrirá uma visualização compartilhada funcional e dará à sua página um comportamento previsível de palavra-chave, categoria e ordenação.
Em Fieldnote, escolha Open Study view in a new page. Esse link real abre uma página independente do navegador, fora da aba integrada do produto. Faça as ações do site nessa nova página. O endereço contém q, category, sort e page; a palavra-chave é print & fold, a categoria Make, e Page 2 of 2 mostra Print & Fold: map edition e Print & Fold. O último é o evento das 14:00 em North room, não o evento anterior com o mesmo título. Escolha Open this view in a new page abaixo dos resultados. A nova página deve mostrar os mesmos controles, página, títulos, datas e locais. O link funciona apenas enquanto o laboratório e sua rota de proxy estiverem ativos. É um exemplo independente, não seu trabalho ou um resultado de avaliação.
A visualização Study independente abaixo mostra os dois registros da segunda página; compare também data, horário e local, não apenas títulos.

Feche as páginas extras do exemplo. Na entrada de Fieldnote, escolha Open Work view in a new page. Mantenha essa página de trabalho aberta. Volte à aba Workspace do produto para editar arquivos; trocar de aba é diferente de seguir um link do site ou pressionar Back no navegador. Seu trabalho começa incompleto, mesmo com Study funcionando.
cd muda o diretório deste terminal; um caminho absoluto começa com /. pwd imprime o diretório atual. cat exibe um pequeno arquivo de texto sem modificá-lo:
cd /home/labex/project/fieldnote/work
pwd
cat catalog.js
Leia três registros: FN-101 e FN-102 começam em 2026-11-14 às 10:00; FN-108 tem o mesmo título de FN-102, mas uma data posterior. A ordem de entrada do catálogo é deliberadamente diferente da exibição. Preveja as três primeiras correspondências para print & fold, categoria make, em ordem de horário: FN-101, FN-102, FN-104. Compare a identidade real dos registros, não apenas a quantidade de cartões.
Vamos filtrar sem mudar o catálogo. filter cria um novo array de objetos correspondentes. includes testa uma substring literal; toLowerCase torna a busca indiferente a maiúsculas e minúsculas. Pesquise na única string concatenada title + ' ' + description, de modo que uma correspondência possa atravessar o espaço de união. && exige ambas as condições; || exige pelo menos uma. all aceita qualquer categoria. A função de seta recebe cada evento.
sort altera o array que ordena; ordenar o novo array filtrado preserva o catálogo. Um comparador retorna número negativo, zero ou positivo. A função auxiliar abaixo usa as comparações de strings < e > do JavaScript. A ordem de horário compara date + 'T' + time e depois o ID ASCII em caso de empate. A ordem por título compara títulos em minúsculas e depois ID. Esse critério textual fixo não depende da localidade do navegador nem da posição original no array. As datas preparadas compartilham um fuso; você não implementa agendamento entre fusos horários.
A expressão condicional condition ? first : second escolhe um valor. Aqui primary || ... usa o ID apenas quando o resultado principal é zero. CATALOG é o objeto preparado carregado antes dos seus arquivos. state contém q, category, sort e page; o próximo passo tornará esses valores seguros e coerentes.
Salve a função completa em selection.js. cat > filename substitui esse arquivo. <<'JS', com o delimitador entre aspas, inicia um here-document: cole as linhas até o JS final, sozinho em uma linha. As aspas mantêm literal a pontuação JavaScript. É edição e gravação no terminal, não automação do editor interno do WebIDE.
cat > selection.js <<'JS'
function compare(a, b) {
return a < b ? -1 : a > b ? 1 : 0;
}
function matches(state) {
return CATALOG.events.filter(event =>
(state.category === 'all' || event.category === state.category) &&
(event.title + ' ' + event.description).toLowerCase()
.includes(state.q.toLowerCase())
).sort((a, b) => {
const primary = state.sort === 'title'
? compare(a.title.toLowerCase(), b.title.toLowerCase())
: compare(a.date + 'T' + a.time, b.date + 'T' + b.time);
return primary || compare(a.id, b.id);
});
}
JS
Volte à página independente e atualize. Espere os controles e cartões. Digite print & fold em Keyword, selecione Make em Category, mantenha Soonest first e acione Search. Os três registros previstos devem aparecer; os dois eventos das 10:00 se distinguem por título e local. Mude Sort by para Title A–Z, envie novamente e espere Print & Fold, Print & Fold, Print & Fold: colour. Leia as datas: os eventos de mesmo título são FN-102 e depois FN-108. Mudar só uma seleção edita um rascunho; Search confirma os três filtros juntos. A URL compartilhada ainda não está completa.
Se o resultado estiver vazio, confira primeiro palavra-chave e categoria. Um erro como prnit legitimamente não encontra nada; corrija para print & fold e envie. Se a página parar após editar, volte a Workspace, execute cat selection.js, compare chaves e aspas com o bloco completo e substitua novamente o arquivo. Atualize depois de salvar; não é preciso reiniciar a VM.
Use Check deste passo depois de observar ambas as ordens. Ele executa seus arquivos originais em um ambiente descartável de navegador e verifica as relações de registros completos; não preenche a página ativa nem conta Study como seu resultado.
Normalizar filtros e limites de páginas
Neste passo, você transformará valores de rascunho ou recebidos em um estado previsível, incluindo resultados vazios e a última página disponível.
Permaneça na página independente. Envie print & fold, Make, Soonest first e escolha Next. Há cinco correspondências e três posições por página, então a segunda contém as duas últimas. Três por página é o dado preparado CATALOG.pageSize; o renderizador usa slice(start, end) para extrair uma página sem alterar a lista. Índices começam em zero e o final é excluído: página 1 usa slice(0, 3), página 2 usa slice(3, 6). Cada cartão usa o objeto selecionado com seus próprios ID, título, descrição, data, horário e local.
Considere uma página recebida 999, categoria desconhecida ou palavra com espaços nas pontas. Uma função de normalização define o significado público antes de renderizar. Palavra ausente vira vazia; remova apenas espaços externos, mantendo espaços internos e as maiúsculas e minúsculas digitadas. A correspondência continua ignorando maiúsculas. Categoria ausente/desconhecida vira all; ordenação ausente/desconhecida vira soon. Converta página com Number: permita inteiros positivos seguros, inclusive o equivalente 2.0. Valores inválidos, zero, negativos, não inteiros ou inseguros viram 1. Limite uma página válida excessiva à última página correspondente. Zero resultados ainda significa página 1 de 1 e ambos os botões desativados.
Number.isSafeInteger evita inteiros imprecisos. Math.ceil arredonda para cima: cinco correspondências divididas por três resultam em duas páginas. Math.max(1, ...) mantém uma página para o resultado vazio; Math.min limita a página solicitada. Array.includes também verifica pertinência às listas de categorias e ordens permitidas. Os padrões com || tratam entrada ausente ou vazia. Um estado normalizado guarda valores, não texto de URL já codificado.
Mude para Workspace, ainda em /home/labex/project/fieldnote/work. Substitua state.js pela função completa:
cat > state.js <<'JS'
function normalize(raw) {
const state = {
q: (raw.q || '').trim(),
category: CATALOG.categories.includes(raw.category) ? raw.category : 'all',
sort: ['soon', 'title'].includes(raw.sort) ? raw.sort : 'soon',
page: Number(raw.page || 1)
};
if (!Number.isSafeInteger(state.page) || state.page < 1) state.page = 1;
const pages = Math.max(1, Math.ceil(matches(state).length / CATALOG.pageSize));
state.page = Math.min(state.page, pages);
return state;
}
JS
Ela chama sua função matches para calcular limites depois de filtrar. Não ordena o catálogo original. Preveja o estado para q: ' print & fold ', category: 'make', sort: 'soon', page: '999': palavra sem espaços externos, categoria e ordem iguais, página 2. Para page: '2.5', será página 1. Você testará valores de URL recebidos no próximo passo, quando o leitor existir.
Volte à página e atualize. Em Keyword, digite dois espaços, print & fold e dois espaços. Escolha Make e acione Search. Após enviar, o campo deve conter print & fold sem espaços externos. Escolha Next: ficam a edição do mapa e o evento das 14:00 em North room, e Next está desativado. Da página 2, busque sound + light, Listen, Title A–Z. Isso confirma novos filtros e reinicia em Page 1 of 1. O sinal de mais literal faz parte do título, não é espaço. Busque no such constellation: nenhum cartão, mensagem legível, página 1 de 1 e ambos os botões desativados. É um resultado correto. Restaure uma palavra e categoria com correspondências e envie.
Antes de Check, confirme que o catálogo original e todo o conteúdo permanecem intactos. As verificações de desconhecidos e grafias numéricas de URL entram no próximo passo; este verifica o formulário normalizado e limites alcançáveis.
Ler e escrever a URL real da visualização
Neste passo, um link aberto diretamente restaurará os quatro valores confirmados e seus registros visíveis.
Uma URL possui origem, caminho, consulta e um fragmento opcional após #. O proxy do LabEx fornece a origem e o caminho reais; não invente uma base juntando origem e /. new URL(location.href) parte do endereço verdadeiro da página. Seu searchParams lê valores decodificados com get e escreve valores comuns, sem codificar, com set. Ele codifica separadores como o & da palavra-chave e o mais literal como %2B. Espaços podem ser serializados em + ou %20. Chamar encodeURIComponent antes de set codificaria duas vezes; passe o valor original.
O curso controla apenas q, category, sort e page. Preserve todo parâmetro extra e o fragmento existente; estão fora dos filtros e podem pertencer à plataforma. Valores ausentes/padrão podem ser omitidos ou explícitos. Ordem dos parâmetros, páginas numéricas equivalentes e as duas grafias de espaços não mudam o significado. A inicialização pode normalizar a URL sem criar uma visita extra. O controlador preparado faz a substituição aqui; o próximo laboratório ensina as chamadas de histórico.
O laço for...of percorre as quatro strings de chaves conhecidas. delete remove cada valor antigo controlado antes que o escritor defina valores diferentes do padrão.
Em Workspace, salve leitura e escrita em url.js:
cat > url.js <<'JS'
function readURL() {
const params = new URL(location.href).searchParams;
return normalize({q: params.get('q'), category: params.get('category'),
sort: params.get('sort'), page: params.get('page')});
}
function stateURL(state) {
const url = new URL(location.href);
for (const key of ['q', 'category', 'sort', 'page']) url.searchParams.delete(key);
if (state.q) url.searchParams.set('q', state.q);
if (state.category !== 'all') url.searchParams.set('category', state.category);
if (state.sort !== 'soon') url.searchParams.set('sort', state.sort);
if (state.page !== 1) url.searchParams.set('page', state.page);
return url;
}
JS
Excluir antes remove filtros antigos ao selecionar um padrão, conservando chaves desconhecidas. As condições omitem só os nossos padrões. O escritor retorna um objeto URL, não navega sozinho. O controlador preparado altera o endereço antes que o renderizador atualize o campo e o link reais de compartilhamento.
Volte à página independente e atualize. Busque print & fold, Make, Title A–Z e escolha Next. O campo de compartilhamento representa os quatro valores, inclusive sort=title e page=2. Escolha Open this view in a new page e atualize a nova página. Espere Print & Fold: map edition, depois Print & Fold: tiny type, os dois últimos registros na ordem de títulos. Confira datas e lugares, não só a quantidade. É um link real para seus arquivos. O Check independente também abre em contexto limpo, sem preferências locais antigas em cache.
Use a barra de endereços da página independente para testar os valores recebidos. Mantenha a origem e o caminho /work/; substitua somente a consulta depois de ?. Acrescente #directory-title para testar o fragmento. Use primeiro:
?edition=november&platform=keep-me&q=print%20%26%20fold&category=make&sort=soon&page=999#directory-title
Devem aparecer a edição do mapa e o evento de North room da página 2, com Soonest first selecionado. O endereço normalizado pode omitir sort=soon ou usar + para espaços; edition, platform e fragmento permanecem. Substitua agora por:
?edition=november&category=unknown&sort=unknown&page=2.5#directory-title
Espere palavra vazia, All categories, Soonest first, Page 1 of 4 e os registros FN-101, FN-102, FN-105. Um valor desconhecido recebe o padrão, não é um erro que exija reiniciar. Por fim, uma consulta com page=2.0 e palavra/categoria do primeiro teste também significa página 2. Ordem e escrita dos padrões são flexíveis; estado visível exato e registros completos não são.
Mantenha a página independente para leitura. Perto de 768 pixels e depois 390, filtros, paginação, link e textos completos devem ser utilizáveis sem rolagem horizontal. Pressione Tab para observar o foco visível. Estrutura e estilos estão preparados; preserve-os. Execute Check. Este laboratório verifica URL direta, concordância URL/controles/resultados e limites seguros. Ainda não exige Back/Forward: o controlador preparado substitui a entrada atual e será o ponto de partida do próximo laboratório.
Resumo
Você selecionou eventos completos com desempate estável, normalizou filtros e páginas e leu/escreveu quatro valores na URL real. Grafias equivalentes de URL reproduzem a mesma visualização sem depender da ordem original do catálogo. O próximo laboratório substitui o controlador para que Back e Forward reais também restaurem essas visualizações.
