Многострочный усеченный текст с кнопкой «Показать больше»

CSS ограничивает текст тремя строками, а чекбокс с label раскрывает абзац. Переключение работает без JavaScript: псевдокласс :checked снимает ограничение строк. Невидимый чекбокс сохраняет клавиатурный фокус, который CSS показывает на метке. Чтобы скрывать переключатель у коротких абзацев, добавьте проверку переполнения через ResizeObserver.

Подробнее о кнопке «Показать больше»

В этом варианте состояние раскрытия хранит обычный HTML-чекбокс. Связанный с ним label выглядит как кнопка и переключает флажок. Элемент <button> тоже подходит для раскрытия, но потребует обработчика JavaScript. Сам по себе он не переключает состояние :checked.

Добавьте разметку в тело HTML-страницы. Разместите чекбокс прямо перед абзацем: селектор + выбирает следующего соседа.

<div class="box">
  <input type="checkbox" id="expanded">
  <p>Длинное описание помогает читателю разобраться в деталях предложения. Здесь можно рассказать об условиях работы, особенностях продукта и вариантах использования. В свернутом блоке читатель увидит только начало описания. Остальная часть текста откроется после нажатия на переключатель. Добавьте сюда собственный текст, который занимает больше трех строк при выбранной ширине блока.</p>
  <label class="read-more" for="expanded">
    <span class="more">Показать больше</span>
    <span class="less">Свернуть</span>
  </label>
</div>

Для нескольких блоков задайте каждому чекбоксу уникальный id, а значение for у соответствующей метки должно совпадать с ним.

Поместите CSS в файл стилей страницы. -webkit-line-clamp: 3 ограничивает текст 3 строками. Связка display: -webkit-box, вертикальной ориентации и overflow: hidden обеспечивает усечение.

.box {
  position: relative;
  max-width: 24rem;
}

.box > p {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
  line-height: 1.5;
}

.box > input:checked + p {
  -webkit-line-clamp: unset;
}

.box > .read-more {
  display: inline-block;
  padding: 0.4em 0.8em;
  border: 1px solid currentColor;
  border-radius: 0.25em;
  cursor: pointer;
}

.box .less {
  display: none;
}

.box > input:checked ~ .read-more .more {
  display: none;
}

.box > input:checked ~ .read-more .less {
  display: inline;
}

Откройте страницу в браузере и нажмите «Показать больше». В абзаце отобразится полная версия текста, а надпись сменится на «Свернуть». Повторное нажатие вернет ограничение в 3 строки. Для другого предела замените значение 3 в CSS.

Но как насчет доступности?

Не скрывайте действующий чекбокс через display: none: браузер исключит его из клавиатурной навигации и дерева доступности. Метка label сама по себе не получает фокус клавишей Tab. Поэтому одна лишь видимая метка не заменяет доступный элемент управления.

Сделайте чекбокс невидимым через opacity: 0, сохранив возможность фокусировки. Добавьте эти правила в тот же файл стилей:

.box > input {
  opacity: 0;
  position: absolute;
  width: 1px;
  height: 1px;
  pointer-events: none;
}

.box > input:focus ~ .read-more {
  outline: 2px solid currentColor;
  outline-offset: 3px;
}

Клавиша Tab переводит фокус на невидимый чекбокс. Селектор :focus показывает рамку вокруг связанной с ним метки. Пробел переключает флажок и раскрывает текст; Enter для этого не требуется.

Не добавляйте метке role="button": фактическим элементом управления остается чекбокс. Скринридер получает его имя из label и сообщает состояние флажка. Такое решение сохраняет семантику чекбокса, но не превращает его в кнопку раскрытия.

CSS-усечение ограничивает видимую часть абзаца, но не удаляет его содержимое. Не рассчитывайте, что скринридер прочитает только первые 3 строки. Используйте этот пример для обычного текста без ссылок и других фокусируемых элементов внутри обрезаемой части.

  1. Нажмите Tab до появления рамки вокруг метки переключателя.
  2. Нажмите пробел. Абзац раскроется, а метка покажет «Свернуть».
  3. Нажмите пробел повторно. Абзац снова сократится, фокус останется на переключателе.

Как отображать кнопку динамически?

У короткого абзаца переключатель не нужен. Псевдокласса :truncated в CSS нет, поэтому проверку переполнения выполнит JavaScript. Скрипт добавит абзацу класс truncated, а CSS использует селектор .truncated с точкой.

Добавьте правила после остальных стилей. Класс measured включает скрытие метки только после первого измерения:

.box.measured > .read-more {
  display: none;
}

.box.measured > p.truncated + .read-more,
.box.measured > input:checked ~ .read-more {
  display: inline-block;
}

Разместите следующий код внутри <script> перед закрывающим тегом </body>. Он обрабатывает блоки .box, не затрагивая остальные абзацы страницы.

document.querySelectorAll('.box').forEach(box => {
  const input = box.querySelector('input[type="checkbox"]');
  const p = box.querySelector('p');

  const update = () => {
    // В раскрытом состоянии сохраняем переключатель для сворачивания.
    if (input.checked) return;

    const truncated = p.scrollHeight > p.clientHeight;
    p.classList.toggle('truncated', truncated);
    input.disabled = !truncated;
    box.classList.add('measured');
  };

  const observer = new ResizeObserver(update);
  observer.observe(p);
  input.addEventListener('change', update);
  update();
});

Скрипт сравнивает полную высоту содержимого scrollHeight с видимой clientHeight. Если текст не помещается, класс truncated показывает метку через CSS. Если абзац помещается целиком, скрипт скрывает метку и отключает ненужный чекбокс. Отключенный элемент не создает лишнюю остановку при нажатии Tab.

ResizeObserver запускает проверку при изменении размеров абзаца. Пока блок раскрыт, скрипт сохраняет переключатель независимо от переполнения. После сворачивания обработчик change снова измеряет усеченный абзац.

При сужении окна у длинного текста появится «Показать больше». Раскройте абзац и убедитесь, что «Свернуть» остается доступным. Затем сверните блок и расширьте окно. Если весь текст поместится в 3 строки, метка исчезнет.

Этот пример отслеживает размеры абзаца, но не каждое изменение его содержимого. Если ваш код заменяет текст программно, вызывайте update() внутри того же блока после замены. Само изменение текста может не менять размеры уже усеченного абзаца.

Чтобы вернуть постоянный переключатель, удалите JavaScript и правила с .measured. Базовое раскрытие через чекбокс продолжит работать без проверки длины текста.

Совет от нашей редакции: перед публикацией пройдите блок клавишами Tab и пробелом. Видимая рамка должна сохраняться и после раскрытия текста.

Комментарии

Оставьте свой комментарий
Пока никто не оставил комментариев