Кнопка «Читать далее» для скрытия длинного текста

Руководство: Добавление кнопки «Читать далее» для длинного описания товара или статьи в Cotonti CMF. Два варианта готового решения для реализации задачи.

Рейтинг на основе отзывов:
Звезд получено: 0
Oтзывов всего: 0
Средняя оценка: 0
Опубликовано в: Блог пользователей

Руководство: Добавление кнопки «Читать далее» для длинного описания товара или статьи в Cotonti CMF. Два варианта готового решения для реализации задачи.


📘 Введение

Если в вашем магазине на Cotonti (модуль Market) описание товара ({MARKET_TEXT}) превышает 750 символов, полезно скрыть его часть и показать кнопку «Читать далее». Это улучшает читаемость страницы и не вредит SEO, так как весь текст остаётся в HTML.

Ниже представлены два варианта реализации с поддержкой мультиязычности через языковые файлы Cotonti. Выберите тот, который лучше вписывается в вашу архитектуру.

🌐 Подготовка локализации

Сначала добавьте строки в языковые файлы модуля Market. Если вы используете другой модуль, путь будет аналогичным.

Русский (/modules/market/lang/market.ru.lang.php)


$L['market_read_more'] = 'Читать далее';
$L['market_collapse']  = 'Свернуть';

Английский (/modules/market/lang/market.en.lang.php)


$L['market_read_more'] = 'Read more';
$L['market_collapse']  = 'Collapse';

При желании добавьте переводы для других языков (украинский, белорусский и т.д.).

Вариант 1: всё в одном шаблоне

Этот способ хорош, когда вы не хотите трогать глобальные CSS/JS‑файлы, а вся логика живёт внутри шаблона карточки товара.

📄 Файл: /modules/market/tpl/market.tpl

В нужном месте карточки товара (обычно там, где выводится {MARKET_TEXT}) вставьте следующий блок:

<div class="mb-3" id="protected-block">
    <div class="market-text-body">
        {MARKET_TEXT}
        <!-- IF {MARKET_BUY_DESCRIPTION} -->
        {MARKET_BUY_DESCRIPTION}
        <!-- ENDIF -->
    </div>
    <div class="d-flex align-items-center my-4">
        <hr class="flex-grow-1">
        <button type="button" class="btn btn-primary btn-lg read-more-btn d-none" id="readMoreBtn">
            {PHP.L.market_read_more}
        </button>
        <hr class="flex-grow-1">
    </div>
</div>

Здесь мы добавили:

  • Контейнер с id="protected-block" для обёртки.
  • Блок market-text-body, куда попадает описание.
  • Кнопку с id="readMoreBtn", которая скрыта по умолчанию (класс d-none). Текст кнопки подставляется из языковой переменной {PHP.L.market_read_more}.

🎨 CSS‑стили

Добавьте стили прямо в этот же файл (можно внутри тега <style>):

<style>
    .market-text-body {
        position: relative;
        overflow: hidden;
        transition: max-height 0.4s ease;
    }
    .market-text-body.collapsed {
        max-height: 200px; /* высота в свёрнутом состоянии */
    }
    .text-fade {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        height: 60px;
        background: linear-gradient(to bottom, transparent, var(--bs-body-bg, #fff));
        pointer-events: none;
        transition: opacity 0.3s;
    }
    .market-text-body.expanded .text-fade {
        opacity: 0;
    }
</style>

Пояснение: класс .collapsed ограничивает высоту текста, а градиент .text-fade создаёт эффект затухания внизу. При разворачивании градиент исчезает.

⚙️ JavaScript

Скрипт также размещается непосредственно в этом шаблоне:

<script>
    document.addEventListener('DOMContentLoaded', function() {
        const block = document.getElementById('protected-block');
        if (!block) return;
        const body = block.querySelector('.market-text-body');
        const btn = block.querySelector('#readMoreBtn');
        if (!body || !btn) return;

        const plainText = body.textContent || body.innerText;
        const limit = 750;

        if (plainText.length > limit) {
            body.classList.add('collapsed');
            const fade = document.createElement('div');
            fade.className = 'text-fade';
            body.appendChild(fade);
            btn.classList.remove('d-none');

            btn.addEventListener('click', function() {
                const isExpanded = body.classList.contains('expanded');
                if (isExpanded) {
                    body.classList.remove('expanded');
                    body.classList.add('collapsed');
                    btn.textContent = '{PHP.L.market_read_more}';
                    fade.style.opacity = '1';
                } else {
                    body.classList.remove('collapsed');
                    body.classList.add('expanded');
                    btn.textContent = '{PHP.L.market_collapse}';
                    fade.style.opacity = '0';
                }
            });
        }
    });
</script>

Почему здесь можно использовать {PHP.L.*} прямо в скрипте?
Потому что скрипт находится внутри .tpl‑файла, и Cotonti обрабатывает его как часть шаблона, заменяя переменные на нужные строки перед выводом в браузер.

✅ Плюсы варианта 1

  • Всё в одном месте, легко найти и изменить.
  • Не нужно редактировать глобальные файлы темы.

❌ Минусы

  • Код шаблона становится громоздким.
  • При использовании агрессивного кэширования шаблонов (например, {SOME_TPL}) могут быть нюансы.

Вариант 2: разделение по файлам

Это более чистый подход: HTML остаётся в шаблоне, CSS – в общей теме, JavaScript – в общем скрипте. Языковые строки передаются через data-атрибуты кнопки, так как JS‑файл не обрабатывается шаблонизатором.

📄 Файл: /modules/market/tpl/market.tpl

Замените вывод описания на следующий блок:

<div class="mb-3" id="protected-block">
    <div class="market-text-body">
        {MARKET_TEXT}
        <!-- IF {MARKET_BUY_DESCRIPTION} -->
        {MARKET_BUY_DESCRIPTION}
        <!-- ENDIF -->
    </div>
    <div class="d-flex align-items-center my-4">
        <hr class="flex-grow-1">
        <button type="button" class="btn btn-primary btn-lg read-more-btn d-none" id="readMoreBtn"
                data-read-more="{PHP.L.market_read_more}"
                data-collapse="{PHP.L.market_collapse}">
            {PHP.L.market_read_more}
        </button>
        <hr class="flex-grow-1">
    </div>
</div>

Ключевое отличие – в кнопке появились атрибуты data-read-more и data-collapse, которые хранят переводы. Именно они будут использоваться в JavaScript.

🎨 CSS‑стили

Добавьте следующий код в файл стилей вашей темы, например, themes/index36/css/default.css (путь может отличаться):

/* скрипт "Читать далее" для карточки товара */
.market-text-body {
    position: relative;
    overflow: hidden;
    transition: max-height 0.4s ease;
}
.market-text-body.collapsed {
    max-height: 380px; /* можно задать другую высоту */
}
.text-fade {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    height: 60px;
    background: linear-gradient(to bottom, transparent, var(--bs-body-bg, #fff));
    pointer-events: none;
    transition: opacity 0.3s;
}
.market-text-body.expanded .text-fade {
    opacity: 0;
}

Обратите внимание: здесь высота в свёрнутом состоянии может отличаться от первого варианта (380px против 200px). Вы можете выбрать любую удобную величину.

⚙️ JavaScript

Добавьте в общий JS‑файл вашей темы (например, themes/index36/js/js.js) следующий код. Лучше всего разместить его как отдельный обработчик, не вмешиваясь в существующие скрипты:

// скрипт "Читать далее" для карточки товара
document.addEventListener('DOMContentLoaded', function() {
    const block = document.getElementById('protected-block');
    if (!block) return;
    const body = block.querySelector('.market-text-body');
    const btn = block.querySelector('#readMoreBtn');
    if (!body || !btn) return;

    const plainText = body.textContent || body.innerText;
    const limit = 750;

    if (plainText.length > limit) {
        body.classList.add('collapsed');
        const fade = document.createElement('div');
        fade.className = 'text-fade';
        body.appendChild(fade);
        btn.classList.remove('d-none');

        btn.addEventListener('click', function() {
            const isExpanded = body.classList.contains('expanded');
            if (isExpanded) {
                body.classList.remove('expanded');
                body.classList.add('collapsed');
                btn.textContent = btn.dataset.readMore;  // берём из data-атрибута
                fade.style.opacity = '1';
            } else {
                body.classList.remove('collapsed');
                body.classList.add('expanded');
                btn.textContent = btn.dataset.collapse;  // берём из data-атрибута
                fade.style.opacity = '0';
            }
        });
    }
});

Важно!
Так как JavaScript находится в отдельном файле, шаблонизатор Cotonti не обрабатывает его, поэтому мы не можем писать {PHP.L.market_read_more} прямо в коде. Вместо этого строки читаются из атрибутов кнопки через btn.dataset.readMore и btn.dataset.collapse. Именно поэтому мы заранее добавили эти атрибуты в HTML.

✅ Плюсы варианта 2

  • Чистое разделение ответственности: HTML, CSS и JS не смешиваются.
  • CSS и JS могут кэшироваться браузером отдельно.
  • Подходит для крупных проектов с общей кодовой базой.

❌ Минусы

  • Требуется редактирование нескольких файлов.
  • Нужно помнить о передаче языковых строк через data-атрибуты.

🔄 Сравнение и рекомендации

КритерийВариант 1 (всё в шаблоне)Вариант 2 (раздельные файлы)
ЛокализацияПрямые {PHP.L.*} в скриптеЧерез data-атрибуты кнопки
Где хранятся стилиВ том же .tpl файлеВ общем CSS (например, default.css)
Где хранится скриптВ том же .tpl файлеВ общем JS (например, js.js)
Удобство поддержкиПроще, всё рядомЛучше масштабируется
ПроизводительностьЧуть быстрее (меньше HTTP‑запросов)Кэширование статики эффективнее

Рекомендация:
Если вы редко меняете скрипты и стили, а шаблон не слишком раздувается, используйте Вариант 1 – он самый простой в реализации.
Если же у вас крупный проект с активной разработкой, предпочтительнее Вариант 2 – он обеспечивает лучшую организацию кода и повторное использование.

🧪 Проверка работы

После внесения изменений откройте любой товар с длинным описанием (более 750 символов). Вы должны увидеть обрезанный текст с затемнением внизу и кнопку «Читать далее». При клике текст развернётся, кнопка сменится на «Свернуть». Повторный клик вернёт всё обратно. В товарах с коротким описанием кнопка не появится.

Убедитесь, что при смене языка сайта текст кнопки также меняется.

Теперь вы можете выбрать подходящий способ и внедрить его в свой проект. Оба варианта полностью рабочие и не нарушают SEO, так как полный текст изначально присутствует в HTML‑коде.

 



Отзывы

Отзывов пока нет


Комментарии (0)

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

Рекомендуемые товары и услуги

Index36 - Современная Тема сайта для Cotonti CMF

Index36 - Современная Тема сайта для Cotonti CMF

Index36 не просто шаблон – это полноценная экосистема для управления сайтом. Основной акцент сделан
DB Structure Viewer для Cotonti

DB Structure Viewer для Cotonti

Гибкий комбинированный экспорт в CSV для Excel, просмотр таблиц БД прямо в админке — всё это
Статистика посещений сайта с проверкой ботов и пауков

Статистика посещений сайта с проверкой ботов и пауков

Плагин для Cotonti CMF, обеспечивающий детальный учёт посещений сайта с расширенной аналитикой по

Обсуждение страницы в Telegram

Автор контента

webitproff

Оффлайн

Carbonate Sodium

Последняя авторизация: 11.07.2026 19:44

  • Чем могу помочь?

    Оказываю весь спектр услуг по CMF Cotonti. Разработка открытых и закрытых корпоративных интернет порталов, небольших социальных сетей, торговые площадки, маркетплейсы, биржи фриланса, каталоги товаров оптовых поставщиков, интернет-магазин под заказ, чтобы делать совместные покупки и групповые совместные продажи от имени нескольких продавцов.

  • Разработки на GitHub бесплатно
    • Страница размещена: 11.07.2026 15:38
    • Последнее обновление: 11.07.2026 16:05
    • Язык:

    Связанные статьи

    Рекомендуемые темы форума для этой статьи

    "Index36" - основной шаблон сайта для Cotonti. Руководство по установке

    "Index36" - основной шаблон сайта для Cotonti. Руководство по установке

    Инструкция по установке темы "Index36" на сайт Cotonti Siena CMF. Удачной установки и красивого
    #188 | Постов: 7 | Просмотров: 1587
    Заметки, инструкции и предупреждения по шаблону "Index36"

    Заметки, инструкции и предупреждения по шаблону "Index36"

    "Index36" - это шаблон основной темы сайта на CMF/CMS Cotonti Siena v0.9.26 самой новой версии по
    #185 | Постов: 1 | Просмотров: 566
    Дифференциация пользовательских шаблонов header.tpl

    Дифференциация пользовательских шаблонов header.tpl

    в файл modules/market/inc/market.functions.php  добавить:  /** * Функция проверяет существование
    #211 | Постов: 1 | Просмотров: 1282