Проект: блог
Второй проектный урок: лента постов с категориями и «избранным», которое переживает перезагрузку. Двигаемся от функции к функции — в конце получится работающий блог.
Шаг 1: массив постов
Держим всё в одном месте: карточку, категорию и дату. Рендер и фильтры читают только этот массив.
const posts = [
{ id: 1, title: "Первый пост", cat: "новости", date: "2026-01-14" },
{ id: 2, title: "Как учить JS", cat: "обучение", date: "2026-02-03" },
{ id: 3, title: "CSS в 2026", cat: "css", date: "2026-03-01" }
];
Шаг 2: карточка и лента
Снова map, createElement, и всё подряд знакомым языком. Добавим звёздочку-избранное с data-id.
function postCard(p) {
const a = document.createElement("article");
a.className = "post";
a.innerHTML = `<h3>${p.title}</h3>
<span class="tag">${p.cat}</span>
<button data-id="${p.id}" class="fav">☆</button>`;
return a;
}
function renderFeed(list) {
const feed = document.getElementById("feed");
feed.innerHTML = "";
list.forEach(p => feed.appendChild(postCard(p)));
}
Шаг 3: категории как фильтр
Тот же приём, что в магазине: клик по тегу — фильтр по массиву.
document.getElementById("tags").addEventListener("click", e => {
const tag = e.target.closest("[data-tag]");
if (!tag) return;
const value = tag.dataset.tag;
renderFeed(value === "все" ? posts : posts.filter(p => p.cat === value));
});
💡
closest() ищет ближайшего подходящего родителя — так клик по внутренности тега (тексту, иконке) всё равно отловится. Гораздо надёжнее, чем цепляться за конкретный элемент.Шаг 4: избранное в localStorage
Идентично корзине: массив id → строкой в storage. На перерисовке сразу проставляем активные звёзды.
const saved = JSON.parse(localStorage.getItem("favorites")) || [];
function toggleFav(id) {
const i = saved.indexOf(id);
i === -1 ? saved.push(id) : saved.splice(i, 1);
localStorage.setItem("favorites", JSON.stringify(saved));
renderFeed(currentList);
}
⚠️
Не храните в localStorage секреты и не ставьте его в один ряд с базой данных — это максимум несколько мегабайт на устройстве пользователя, и он может его очистить.
Шаг 5: «показать ещё»
Не выводим все посты сразу — сперва часть, по клику добавляем ещё несколько.
let shown = 4;
function renderMore() {
renderFeed(currentList.slice(0, shown));
shown += 4;
const more = document.getElementById("more");
if (more) more.style.display = shown >= currentList.length ? "none" : "block";
}
Итоговый чек-лист блога
- Лента рендерится из массива постов;
- Фильтры по категориям;
- Избранное сохраняется между сессиями;
- Кнопка «показать ещё» добавляет порции постов.
ℹ️
Готовый блог можно выложить на GitHub Pages уже сегодня — кстати, как раз урок о том, куда публиковать, у нас следующий и последний.