📖 «Аккордеон-спойлер» для скрытия полей мини-профиля в топиках
🎯 Назначение
Скрипт автоматически находит мини-профили пользователей в топиках форума и сворачивает менее важные поля (IP, возраст, город, кол-во сообщений и т.д.) в компактный аккордеон. Пользователь видит только основные поля (аватар, ник, статус), а всё остальное раскрывается по клику на кнопку со стрелкой.
Ключевое отличие от аналогов: код написан на чистом vanilla JavaScript без использования jQuery или других библиотек. Это означает:
✅ Минимальный размер — нет лишних килобайт зависимостей
✅ Высокая производительность — прямая работа с DOM через нативные API
✅ Совместимость — работает в любом современном браузере без дополнительных плагинов
✅ Современные подходы — используются querySelectorAll, MutationObserver, classList, CSS Grid и другие актуальные стандарты
👀 Как это выглядит
Спойлер закрыт
[html]<img class="postimg" loading="lazy" src="https://upforme.ru/uploads/001a/f0/7d/2/580448.webp" alt="https://upforme.ru/uploads/001a/f0/7d/2/580448.webp">[/html]
Спойлер открыт
[html]<img class="postimg" loading="lazy" src="https://upforme.ru/uploads/001a/f0/7d/2/108643.webp" alt="https://upforme.ru/uploads/001a/f0/7d/2/108643.webp">[/html]
🧱 Структура, которую создаёт скрипт
Для каждого мини-профиля (.post-author) скрипт генерирует такую DOM-структуру:
Показать структуру
.post-author
├── .pa-avatar ← остаётся на месте (SHOW)
├── .pa-author ← остаётся на месте (SHOW)
├── .pa-title ← остаётся на месте (SHOW)
│
├── .msp-wrap ← ← ← НОВЫЙ контейнер (вставляется сюда)
│ ├── .msp-toggle ← кнопка-стрелка с тултипом
│ │ └── <img> ← иконка стрелки (поворачивается на 180°)
│ │
│ └── .msp-spoiler ← grid-обёртка (анимирует высоту)
│ └── .msp-spoiler-inner ← реальный контейнер контента
│ ├── .pa-ip ← перенесено внутрь (HIDE)
│ ├── .pa-age ← перенесено внутрь (HIDE)
│ ├── .pa-from ← перенесено внутрь (HIDE)
│ ├── .pa-posts ← перенесено внутрь (HIDE)
│ ├── .pa-respect ← перенесено внутрь (HIDE)
│ ├── .pa-sex ← перенесено внутрь (HIDE)
│ └── .pa-ua ← перенесено внутрь (HIDE)
⚙️ Как работает JavaScript
Показать
1. Конфигурация — что показывать, что скрывать
var SHOW = ['pa-author','pa-title','pa-avatar'];
var HIDE = ['pa-ip','pa-age','pa-from','pa-posts','pa-respect','pa-sex','pa-ua'];
SHOW — поля, которые всегда видны. Скрипт ищет последнее из них, чтобы вставить спойлер сразу после.
HIDE — поля, которые будут «упакованы» внутрь спойлера.
2. Инициализация — поиск и перенос элементов
document.querySelectorAll('.post-author').forEach(function (box) {
Для каждого мини-профиля:
Проверка box.dataset.mspDone — чтобы не обработать один и тот же блок дважды.
Сбор скрытых полей — скрипт перебирает массив HIDE, находит соответствующие элементы внутри .post-author и складывает в массив hidden.
Создание DOM-структуры — создаются .msp-wrap, .msp-toggle, .msp-spoiler, .msp-spoiler-inner.
Перенос элементов — все найденные скрытые поля физически перемещаются внутрь .msp-spoiler-inner через appendChild() (браузер сам удаляет элемент со старого места).
Вставка спойлера — скрипт ищет последний видимый элемент из SHOW и вставляет .msp-wrap сразу после него через insertBefore(wrap, anchor.nextSibling).
3. Обработчик клика
btn.addEventListener('click', function () {
wrap.classList.toggle('msp-open');
});
При клике просто добавляется/убирается класс .msp-open на обёртке. Вся анимация — на CSS.
4. Защита от динамической подгрузки
new MutationObserver(init).observe(document.body, { childList: true, subtree: true });
MutationObserver следит за изменениями DOM. Если форум подгружает новые сообщения через AJAX (бесконечная прокрутка, переход по страницам без перезагрузки), скрипт автоматически обработает новые мини-профили.
🎨 Как работает CSS-анимация (Grid-трюк)
Это самая интересная часть — анимация высоты без JavaScript.
Показать
Проблема обычной анимации
Чтобы анимировать height: 0 → auto, нужно знать точную высоту контента в пикселях. Но высота зависит от контента, шрифтов, адаптивности... Раньше это решали так:
// ❌ Старый подход: JS считает высоту
element.style.height = element.scrollHeight + 'px';
Это требует пересчёта при каждом изменении контента, при ресайзе окна, и работает дёрганно.
Решение через CSS Grid
.msp-spoiler {
display: grid;
grid-template-rows: 0fr; /* ← свёрнуто */
transition: grid-template-rows 0.4s ease-out;
}
.msp-wrap.msp-open .msp-spoiler {
grid-template-rows: 1fr; /* ← развёрнуто */
}
.msp-spoiler > .msp-spoiler-inner {
overflow: hidden;
min-height: 0; /* ← обязательно! */
}
Пошаговая механика
1. msp-spoiler — это grid-контейнер с одной строкой
2. grid-template-rows: 0fr — высота строки = 0 долей от доступного пространства. Фактически 0px.
3. grid-template-rows: 1fr — высота строки = 1 доля = весь доступный контент. Браузер сам вычисляет нужную высоту.
4. transition плавно интерполирует между 0fr и 1fr за 0.4 секунды
5. msp-spoiler-inner с overflow: hidden обрезает контент, который вылезает за пределы grid-ячейки
6. min-height: 0 — критически важно! Без этого grid-элемент не может стать меньше своего минимального контента (по спецификации CSS grid min-height: auto по умолчанию), и анимация не сожмётся до нуля
Почему это работает
0fr → 0.1fr → 0.3fr → 0.6fr → 0.9fr → 1fr
0px 12px 38px 72px 108px 120px (пример)
Браузер линейно интерполирует значение fr от 0 до 1, а высота grid-ячейки плавно растёт. Внутренний overflow: hidden обрезает контент на каждом кадре — получается плавное «раскрытие».
Поддержка браузерами - Chrome, Firefox, Safari, Edge поддерживают CSS-анимацию с 2023 года.
На момент 2026 года это работает во всех актуальных браузерах.
🖱️ Интерактивные элементы
Показать
Кнопка-стрелка
.msp-toggle img {
transition: transform 0.3s;
}
.msp-wrap.msp-open .msp-toggle img {
transform: rotate(180deg); /* стрелка переворачивается */
}
Стрелка плавно поворачивается на 180° при раскрытии — визуальная подсказка состояния.
Тултип (всплывающая подсказка) при наведении
.msp-toggle::after {
content: attr(data-tip); /* берёт текст из data-tip="Развернуть/Свернуть" */
position: absolute;
bottom: 100%;
opacity: 0;
transition: opacity 0.2s;
}
.msp-toggle:hover::after { opacity: 1; }
Псевдоэлемент ::after создаёт всплывающую подсказку над кнопкой. Текст берётся из атрибута data-tip — удобно менять без правки CSS.
🔄 Полный цикл работы
Развернуть
Страница загружается (или подгружается через AJAX)
▼
init() находит все .post-author на странице
▼
Для каждого: проверяет dataset.mspDone
(уже обработан? → пропускаем)
▼
Собирает поля из массива HIDE
(pa-ip, pa-age, pa-from и т.д.)
▼
Создаёт DOM: .msp-wrap > .msp-toggle + .msp-spoiler
Переносит скрытые поля внутрь .msp-spoiler-inner
▼
Вставляет .msp-wrap после последнего поля из SHOW
▼
Пользователь кликает на .msp-toggle
→ toggle('msp-open')
→ CSS анимирует grid-template-rows: 0fr ↔ 1fr
→ Стрелка поворачивается на 180°
💡 Ключевые преимущества этого подхода
Развернуть
Без JS-расчётов - не нужно измерять scrollHeight, не нужно пересчитывать при ресайзе
Плавная анимация - CSS transition работает на GPU, 60fps без лагов
Адаптивность - высота подстраивается под любой контент автоматически
Производительность - один MutationObserver на всю страницу, а не слушатели на каждом элементе
Защита от повторов - dataset.mspDone гарантирует, что каждый блок обработан ровно один раз
Совместимость с AJAX - MutationObserver ловит динамически подгруженные сообщения
🔧 Как настроить под себя
Развернуть
// Какие поля оставить видимыми (CSS-классы без точки)
var SHOW = ['pa-author', 'pa-title', 'pa-avatar'];
// Какие поля спрятать в спойлер
var HIDE = ['pa-ip', 'pa-age', 'pa-from', 'pa-posts', 'pa-respect', 'pa-sex', 'pa-ua'];
// Текст всплывающей подсказки
var TIP = 'Развернуть/Свернуть';
// URL иконки-стрелки (32×16 px)
var IMG = 'https://forumstatic.ru/files/001a/f0/7d/19517.png';
Достаточно поменять массивы SHOW / HIDE — и скрипт автоматически перераспределит поля. Порядок в SHOW определяет, после какого поля появится кнопка спойлера (скрипт берёт последний найденный).
📝 Исходный код
P.S. Пример реального кода на моём форуме, с небольшими добавлениями (с дополнительными полями):
Отредактировано Merlin777 (Сегодня 01:38:04)