Введение
Fieldnote — небольшой справочник событий выходного дня. Полезная ссылка поиска должна описывать представление: ключевое слово, категорию, сортировку и страницу результатов. Друг, открывший ссылку, должен увидеть те же события, включая правильное время и место двух событий с одинаковым названием. Вы построите эту связь состояний на основе подготовленного адаптивного сайта и небольшого локального каталога.
Нужны основы путей терминала, HTML-форм, компоновки и фокуса CSS, а также массивов, объектов, функций, выбора DOM и событий JavaScript. Лабораторная знакомит с фильтрацией массивов, компараторами, границами страниц, URL и URLSearchParams. Поиск и разбор URL применяются по-разному; здесь результат — полное воспроизводимое представление из четырёх значений. Подготовленный механизм отображения строит карточки из записей, выбранных вашим кодом. Знания backend или фреймворков не нужны. Все события синтетические. 55 минут — оценка автора с учётом чтения и обычных ошибок.
Используйте Workspace для команд и Fieldnote для входной страницы. Эта лабораторная запускается независимо. Меняйте только /home/labex/project/fieldnote/work/selection.js, state.js и url.js; app.js здесь подготовлен, а вашей задачей станет в следующей лабораторной. Сохраните catalog.js, view.js, index.html, style.css, отдельное представление Study и подготовленный сервис. Контроллер уже отправляет форму и перерисовывает страницу, обновляя текущую запись через replaceState. Работа истории здесь подготовлена, а не является пока учебным требованием. Подписи сайта остаются английскими.
Выбирать реальные записи в стабильном порядке
На этом шаге вы откроете рабочее общее представление, затем обеспечите предсказуемый поиск по слову, категории и порядку на своей странице.
В Fieldnote выберите Open Study view in a new page. Эта настоящая ссылка открывает отдельную страницу браузера вне встроенной вкладки продукта. Выполняйте действия сайта на новой странице. Адрес содержит q, category, sort и page; ключевое слово — print & fold, категория — Make, а Page 2 of 2 показывает Print & Fold: map edition и Print & Fold. Последнее — событие в 14:00 в North room, не более раннее одноимённое событие. Выберите Open this view in a new page под результатами. Новая страница должна показать те же элементы, страницу, названия, даты и места. Ссылка работает только пока запущены лабораторная и её маршрут прокси. Это независимый пример, а не ваша работа или результат проверки.
Независимое представление Study ниже показывает две записи второй страницы. Сравнивайте дату, время и место, а не только названия.

Закройте дополнительные страницы примера. На входной странице Fieldnote выберите Open Work view in a new page. Оставьте эту отдельную рабочую страницу открытой. Для редактирования вернитесь во вкладку Workspace продукта. Переключение вкладок отличается от перехода по ссылке сайта или кнопки Back браузера. Ваша работа изначально не завершена, даже если Study работает.
cd меняет каталог этого терминала; абсолютный путь начинается с /. pwd печатает текущий каталог. cat показывает небольшой текстовый файл, не изменяя его:
cd /home/labex/project/fieldnote/work
pwd
cat catalog.js
Прочитайте три записи: FN-101 и FN-102 начинаются 2026-11-14 в 10:00; у FN-108 то же название, что у FN-102, но более поздняя дата. Входной порядок каталога специально отличается от отображения. Предскажите первые три совпадения для print & fold, категории make, по ближайшему времени: FN-101, FN-102, FN-104. Сравнивайте идентичность записей, не только число карточек.
Будем фильтровать, не меняя каталог. filter создаёт новый массив подходящих объектов. includes проверяет буквальную подстроку, а toLowerCase делает поиск нечувствительным к регистру. Ищите в одной объединённой строке title + ' ' + description: совпадение может пересекать соединяющий пробел. && требует обоих условий, || — хотя бы одного. all допускает любую категорию. Стрелочная функция получает каждое событие.
sort меняет сортируемый массив; сортировка нового отфильтрованного массива сохраняет подготовленный каталог. Компаратор возвращает отрицательное число, ноль или положительное число. Помощник ниже использует строковые сравнения JavaScript < и >. Порядок по времени сравнивает date + 'T' + time, затем ASCII-ID при равенстве. Порядок по названию сравнивает названия в нижнем регистре, затем ID. Этот фиксированный текстовый порядок не зависит от локали браузера и исходных позиций массива. Подготовленные даты относятся к одному часовому поясу; вы не реализуете планирование между поясами.
Условное выражение condition ? first : second выбирает значение. Здесь primary || ... использует сравнение ID только при нулевом основном результате. CATALOG — подготовленный объект, загружаемый до ваших файлов. Объект state содержит q, category, sort и page; следующий шаг сделает значения безопасными и согласованными.
Сохраните полную функцию в selection.js. cat > filename заменяет файл. <<'JS' с кавычками начинает here-document: вставляйте строки до завершающего JS на отдельной строке. Кавычки сохраняют пунктуацию JavaScript буквально. Это редактирование и сохранение через терминал, а не автоматизация редактора 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
Вернитесь на отдельную рабочую страницу и обновите её. Дождитесь элементов и карточек. Введите print & fold в Keyword, выберите Make в Category, оставьте Soonest first и активируйте Search. Появятся три предсказанные записи; два события в 10:00 различаются названиями и местами. Измените Sort by на Title A–Z, отправьте снова и ожидайте Print & Fold, Print & Fold, Print & Fold: colour. Прочитайте даты: одноимённые события идут как FN-102, затем FN-108. Изменение только списка — черновик; Search подтверждает все три фильтра вместе. URL для общего доступа пока не завершён.
При пустом результате сначала проверьте слово и категорию. Опечатка prnit законно не даёт совпадений; замените её на print & fold и отправьте снова. Если после редактирования страница не отвечает, вернитесь в Workspace, выполните cat selection.js, сравните скобки и кавычки с полным блоком и замените файл снова. После сохранения обновите страницу; сброс VM не нужен.
Используйте Check шага после наблюдения обоих порядков. Он выполняет ваши исходные frontend-файлы в одноразовой браузерной среде и проверяет связи полных записей; он не дополняет активную страницу и не считает Study вашим результатом.
Нормализовать фильтры и границы страниц
На этом шаге вы превратите черновые или входящие значения в предсказуемое состояние, включая пустые результаты и последнюю доступную страницу.
Оставайтесь на отдельной рабочей странице. Отправьте print & fold, Make, Soonest first и выберите Next. Есть пять совпадений и три места на странице, поэтому вторая содержит последние два. Три записи на странице — подготовленное значение CATALOG.pageSize. Механизм отображения использует slice(start, end), извлекая страницу без изменения полного списка. Индексы начинаются с нуля, конечный не включается: первая страница использует slice(0, 3), вторая — slice(3, 6). Каждая карточка использует выбранный объект с его собственными ID, названием, описанием, датой, временем и местом.
Теперь рассмотрите входящую страницу 999, неизвестную категорию или слово с внешними пробелами. Нормализация задаёт публичный смысл до отображения. Отсутствующее слово становится пустым; удаляются только пробелы по краям, внутренние пробелы и регистр пользователя сохраняются. Сопоставление по-прежнему не учитывает регистр. Неизвестная/отсутствующая категория становится all, сортировка — soon. Преобразуйте страницу через Number: разрешены положительные безопасные целые, включая эквивалентное 2.0. Недопустимые, нулевые, отрицательные, нецелые или небезопасные значения становятся 1. Ограничьте допустимую слишком большую страницу последней страницей совпадений. Ноль результатов сохраняет страницу 1 из 1 и обе кнопки отключёнными.
Number.isSafeInteger исключает неточно представленные целые. Math.ceil округляет вверх: пять совпадений, делённые на три, дают две страницы. Math.max(1, ...) оставляет хотя бы одну страницу пустого результата; Math.min ограничивает запрошенную страницу. Array.includes также проверяет принадлежность спискам категорий и сортировок. Значения по умолчанию с || обрабатывают отсутствующий или пустой ввод. Нормализованное состояние содержит значения, а не уже закодированный текст URL.
Перейдите в Workspace, оставаясь в /home/labex/project/fieldnote/work. Замените state.js полной функцией:
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
Она вызывает вашу matches, вычисляя границы после фильтрации, и не сортирует исходный каталог. Предскажите состояние для q: ' print & fold ', category: 'make', sort: 'soon', page: '999': слово без внешних пробелов, категория и порядок прежние, страница 2. При page: '2.5' получится страница 1. Входящие URL проверите на следующем шаге, когда появится чтение URL.
Вернитесь и обновите рабочую страницу. В Keyword введите два пробела, print & fold, затем два пробела. Выберите Make, активируйте Search. Поле должно содержать print & fold без внешних пробелов. Выберите Next: остаются версия с картой и событие в 14:00 в North room, а Next отключена. Со страницы 2 найдите sound + light, Listen, Title A–Z. Это подтверждает новый фильтр и возвращает к Page 1 of 1. Буквальный плюс — часть названия, не пробел. Найдите no such constellation: карточек нет, читаемое сообщение, страница 1 из 1, обе кнопки отключены. Это правильный результат. Верните подходящие слово и категорию, затем отправьте.
Перед Check убедитесь, что исходный каталог и всё содержимое записей сохранены. Проверки неизвестных значений и числовых записей URL добавятся на следующем шаге; этот проверяет нормализованную форму и достижимые границы страниц.
Читать и записывать фактический URL представления
На этом шаге непосредственно открытая ссылка будет восстанавливать все четыре подтверждённых значения и видимые записи.
URL содержит origin, путь, запрос и необязательный фрагмент после #. Прокси LabEx предоставляет фактические origin и путь; не угадывайте новую базу, добавляя / к origin. new URL(location.href) начинает с реального адреса страницы. Его searchParams читает декодированные параметры через get и записывает обычные незакодированные значения через set. Он кодирует разделители, например & в слове, а буквальный плюс — как %2B. Пробелы могут сериализоваться как + или %20. Вызов encodeURIComponent перед set кодировал бы дважды; передавайте исходное значение.
Курс управляет только q, category, sort и page. Сохраняйте все дополнительные параметры и существующий фрагмент: они вне фильтра и могут принадлежать платформе. Пропущенные/стандартные значения можно опускать или писать явно. Порядок параметров, эквивалентные числовые страницы и два вида пробела не меняют смысла. Инициализация может нормализовать URL без дополнительной записи истории. Здесь замену делает подготовленный контроллер; вызовы истории изучаются в следующей лабораторной.
Цикл for...of ниже проходит четыре известные строки ключей. delete удаляет старое управляемое значение перед записью нестандартных значений.
В Workspace сохраните чтение и запись в 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
Предварительное удаление убирает старые фильтры при выборе стандартного значения, сохраняя неизвестные ключи. Условия опускают только наши стандартные значения. Функция записи возвращает объект URL и сама не выполняет переход. Подготовленный контроллер меняет адрес, затем механизм отображения обновляет настоящее поле и ссылку общего доступа.
Вернитесь на рабочую страницу и обновите. Найдите print & fold, Make, Title A–Z, выберите Next. Поле общего доступа представляет все четыре значения, включая sort=title и page=2. Выберите Open this view in a new page и обновите новую страницу. Ожидайте Print & Fold: map edition, затем Print & Fold: tiny type, последние две записи в порядке названий. Проверьте даты и места, не только количество. Это настоящая ссылка на ваши файлы. Независимый Check также открывает её в чистом контексте без старых локальных предпочтений в кэше.
Используйте адресную строку отдельной страницы для входящих тестов. Сохраняйте реальный origin и путь /work/; заменяйте только запрос после ?. Добавьте #directory-title, чтобы проверить фрагмент. Сначала используйте:
?edition=november&platform=keep-me&q=print%20%26%20fold&category=make&sort=soon&page=999#directory-title
Должны появиться версия с картой и событие в North room со страницы 2, с выбранным Soonest first. Нормализованный адрес может опустить sort=soon или представить пробелы через +; edition, platform и фрагмент сохраняются. Теперь замените запрос:
?edition=november&category=unknown&sort=unknown&page=2.5#directory-title
Ожидайте пустое слово, All categories, Soonest first, Page 1 of 4 и первые записи FN-101, FN-102, FN-105. Неизвестное значение заменяется стандартным; это не ошибка, требующая сброса. Наконец, запрос с page=2.0 и словом/категорией первого теста также означает страницу 2. Порядок и запись стандартных значений гибкие, но точное видимое состояние и полные записи — нет.
Оставьте отдельную рабочую страницу для чтения. При ширине около 768, затем 390 пикселей фильтры, кнопки, ссылка и полный текст записей должны оставаться удобными без горизонтальной прокрутки. Нажмите Tab для проверки видимого фокуса. Каркас и стили подготовлены; сохраняйте их. Запустите Check. Эта лабораторная проверяет прямое восстановление URL, согласованность URL/элементов/результатов и безопасные границы. Back/Forward пока не требуются: подготовленный контроллер заменяет текущую запись и станет началом следующей лабораторной.
Резюме
Вы выбрали полные записи событий со стабильным разрешением равенств, нормализовали фильтры и страницы и прочитали/записали четыре значения фактического URL. Эквивалентные записи URL воспроизводят ту же картину независимо от исходного порядка каталога. Следующая лабораторная заменяет подготовленный контроллер, чтобы настоящий Back и Forward тоже восстанавливали эти представления.
