Плашка «Вы уже читали эту страницу» для uCoz

Юрий Герук 2025-12-25 55
Плашка «Вы уже читали эту страницу» для uCoz

Введение

На сайтах с большим количеством статей, инструкций и гайдов пользователи часто возвращаются к уже прочитанному контенту. Через день, неделю или месяц. И в этот момент у человека возникает простой вопрос. Я это уже читал или нет.

uCoz из коробки такую вещь не показывает. Ни визуально, ни логически. Мы это исправим аккуратным клиентским решением без серверной логики, без базы и без сторонних библиотек.

В итоге получаем небольшую плашку в начале статьи, которая честно сообщает, что пользователь уже был на этой странице, и показывает дату последнего визита.

Что это за решение и зачем оно нужно

Это JavaScript скрипт, который запоминает факт посещения конкретной статьи в браузере пользователя через localStorage.

При повторном заходе на ту же страницу он:

  • определяет, что это именно страница материала;
  • проверяет, был ли пользователь здесь раньше;
  • если был, показывает плашку с датой последнего визита.

Решение работает:

  • на стандартных URL uCoz вида /news/...-123;
  • на красивых URL без ID вида /lipkoe-oglavlenie-stati-ucoz;
  • на кастомных шаблонах;
  • без привязки к модулю;
  • без нагрузки на сервер.

Почему это важно:

  • повышает UX, особенно в блогах и базах знаний;
  • помогает пользователю ориентироваться в контенте;
  • не вмешивается в SEO и индексацию;
  • не ломается при смене URL.

Как это работает технически

Логика простая и надежная.

  1. Скрипт определяет, является ли страница страницей статьи, а не списком или главной.
  2. Для идентификации страницы используется canonical URL. Это ключевой момент.
  3. Canonical приводится к стабильному виду без параметров и якорей.
  4. В localStorage сохраняется timestamp последнего визита.
  5. При следующем заходе дата извлекается и выводится пользователю.

Если canonical отсутствует, используется текущий путь страницы.

Применение на практике и чем это хорошо

Такую плашку особенно полезно использовать:

  • в блогах с длинными техническими статьями;
  • в базах знаний;
  • в инструкциях и мануалах;
  • на сайтах с обучающим контентом.

Плюсы решения:

  • не требует правок базы данных;
  • не зависит от модуля uCoz;
  • одинаково работает на старых и новых URL;
  • легко кастомизируется под любой дизайн;
  • не влияет на скорость загрузки страницы.

По факту это маленькая деталь, которая делает сайт ощущаемо умнее.

Установка и подключение

Куда вставлять CSS

  • Панель управления сайта
  • Управление дизайном
  • Таблица стилей CSS

Вставь код полностью.

.uc-read-flag {
 position: relative;
 margin: 14px 0;
 padding: 12px 14px;
 border-radius: 12px;
 background: rgba(0, 0, 0, 0.06);
 border: 1px solid rgba(0, 0, 0, 0.10);
 line-height: 1.35;
 font-size: 14px;
}

@media (prefers-color-scheme: dark) {
 .uc-read-flag {
 background: rgba(255, 255, 255, 0.06);
 border: 1px solid rgba(255, 255, 255, 0.12);
 }
}

.uc-read-flag__title {
 font-weight: 700;
 margin: 0 0 4px 0;
 font-size: 15px;
}

.uc-read-flag__meta {
 opacity: 0.85;
 margin: 0;
}

.uc-read-flag__btn {
 position: absolute;
 top: 10px;
 right: 10px;
 border: 0;
 border-radius: 10px;
 padding: 6px 10px;
 cursor: pointer;
 background: rgba(0, 0, 0, 0.10);
 font-size: 13px;
}

@media (prefers-color-scheme: dark) {
 .uc-read-flag__btn {
 background: rgba(255, 255, 255, 0.12);
 }
}

Куда вставлять JavaScript

  • Панель управления сайта
  • Управление дизайном
  • Модуль куда ставите
  • Страница материала и комментариев
  • Перед закрывающим </body>
<script>
(function () {
 'use strict';

 var KEEP_DAYS = 180;
 var SAVE_AFTER_MS = 2500;

 function nowMs() { return Date.now(); }

 function pad2(n) { return (n < 10 ? '0' : '') + n; }

 function formatDate(ts) {
 var d = new Date(ts);
 return pad2(d.getDate()) + '.' + pad2(d.getMonth() + 1) + '.' + d.getFullYear() +
 ' ' + pad2(d.getHours()) + ':' + pad2(d.getMinutes());
 }

 function safeJsonParse(s) {
 try { return JSON.parse(s); } catch (e) { return null; }
 }

 function normUrl(u) {
 try {
 var x = new URL(u, location.href);
 return x.origin + x.pathname.replace(/\/+$/, '');
 } catch (e) {
 return (u || '').split('#')[0].split('?')[0].replace(/\/+$/, '');
 }
 }

 function getPageKey() {
 var canonical = document.querySelector('link[rel="canonical"]');
 var base = canonical && canonical.href ? canonical.href : location.href;
 return normUrl(base);
 }

 function isEntryPage() {
 if (document.querySelector('.eMessage')) return true;
 if (document.querySelector('article, .entry, .post, .article-post')) return true;
 var og = document.querySelector('meta[property="og:type"][content="article"]');
 return !!og;
 }

 function findEntryNode() {
 return document.querySelector('article.article-post') ||
 document.querySelector('.article-post') ||
 document.querySelector('.post') ||
 document.querySelector('.entry') ||
 document.querySelector('.eMessage') ||
 document.querySelector('article');
 }

 if (!isEntryPage()) return;

 var entryNode = findEntryNode();
 if (!entryNode) return;

 var STORAGE_KEY = 'uc_read_flag:' + getPageKey();

 var prevRaw = localStorage.getItem(STORAGE_KEY);
 var prev = prevRaw ? safeJsonParse(prevRaw) : null;

 if (prev && prev.last && (nowMs() - prev.last) > KEEP_DAYS * 86400000) {
 localStorage.removeItem(STORAGE_KEY);
 prev = null;
 }

 if (prev && prev.last) {
 var el = document.createElement('div');
 el.className = 'uc-read-flag';
 el.innerHTML =
 '<div class="uc-read-flag__title">Вы уже читали эту страницу.</div>' +
 '<p class="uc-read-flag__meta">Последний визит: <b>' + formatDate(prev.last) + '</b>.</p>' +
 '<button class="uc-read-flag__btn" type="button">Скрыть</button>';

 el.querySelector('.uc-read-flag__btn').onclick = function () {
 el.remove();
 };

 entryNode.parentNode.insertBefore(el, entryNode);
 }

 setTimeout(function () {
 try {
 localStorage.setItem(STORAGE_KEY, JSON.stringify({ last: nowMs() }));
 } catch (e) {}
 }, SAVE_AFTER_MS);

})();
</script>

Какие классы используются и как адаптировать под свой шаблон

Основной контейнер плашки:

  • .uc-read-flag - Это весь блок уведомления.

Внутренние элементы:

  • .uc-read-flag__title - Заголовок плашки.
  • .uc-read-flag__meta - Текст с датой последнего визита.
  • .uc-read-flag__btn - Кнопка скрытия плашки.

Если у тебя другой контейнер статьи, просто добавь его в функцию findEntryNode().

Например, если статья лежит в .article-post-content, добавь первой строкой:

return document.querySelector('.article-post-content') || ...

Логика скрипта от этого не ломается.

Заключение

Это решение не про вау-эффект. Оно про удобство, честность и уважение к пользователю.

Плашка ненавязчивая, информативная и работает стабильно независимо от того, использует ли сайт стандартные URL uCoz или давно перешёл на красивые.

Такие мелочи делают сайт ощущаемо продуманным. А именно это отличает нормальный проект от просто набора страниц.

Оцените полезность материала!

Лицензия: CC BY-SA 4.0

Автор: Юрий Герук

Похожие материалы:

Комментарии