Многострочный усеченный текст с кнопкой «Показать больше» на чистом CSS
Я написал скрипт на CSS для размещения многострочного усеченного текста с кнопкой «Читать далее».
Я задался вопросом, позволяет ли CSS правильно оформить усеченный текст так, чтобы разместить в нем несколько строк? А также по нажатию кнопки «Показать больше» отобразить текст полностью.
Свойство text-overflow: ellipsis не поддерживает многострочность. Но я вспомнил о свойстве line-clamp, которое позволяет обрезать многострочный текст.
Подробнее о кнопке «Показать больше»
Для создания кнопки «Показать больше» я не мог использовать тег <button> или <a>. Для этого требуется label и чекбокс. Вот что получилось:
<div class="box">
<input type="checkbox" id="expanded">
<p>Hey, don't cut me off like that. I want to speak my mind and don't appreciate being put into a box.</p>
<!-- Примечание: я не нашел способа поместить дополнительную кнопку/метку в строку внутри абзаца после или перед усечением. -->
<label for="expanded" role="button">read more</label>
</div>
Я разместил чекбокс прямо перед абзацем, поэтому можно использовать псевдокласс :checked для активации усечения текста:
input:checked + p {
-webkit-line-clamp: unset;
}
Нажмите кнопку, и в абзаце отобразится обрезанная версия текста.
Но как насчет доступности?
Хак с чекбоксом не только семантически неправильный, но и ограничивает возможности доступа к функционалу для людей с плохим зрением. При этом присваивается для чекбокса свойство display: none. Из-за этого нарушается навигация с помощью клавиатуры. Нельзя использовать скрытый чек-бокс, а использование метки не помогает. Событие нажатия клавиши пробела/ввода не перенаправляют событие в чекбокс.
Чтобы решить эту проблему, я сделал чекбокс фокусируемым, хоть и все еще невидимым.
input {
opacity: 0;
position: absolute;
pointer-events: none;
}
Теперь чекбокс можно выделить, но без скринридера нельзя узнать, установлен он или нет, так как невидим. Чтобы решить эту проблему, я заставил label наследовать стиль фокуса браузера по умолчанию, когда чекбокс выделен:
input:focus ~ label {
outline: -webkit-focus-ring-color auto 5px;
}
Ниже приведена комбинированная демо-версия. Попробуйте нажать на кнопку с помощью мышки, а затем используя клавишу пробела.
Как отображать кнопку динамически?
Но как динамически отображать кнопку, когда текст в абзаце слишком длинный, чтобы поместиться в контейнер. Это единственная функция, которую я не смог реализовать с помощью CSS. Потому что она требует применения селектора :truncated:
.read-more { display: none; }
p:truncated + .read-more { display: block; }
Поэтому реализовать это можно только с помощью JavaScript. Например:
const ps = document.querySelectorAll('p');
const observer = new ResizeObserver(entries => {
for (let entry of entries) {
entry.target.classList[entry.target.scrollHeight > entry.contentRect.height ? 'add' : 'remove']('truncated');
}
});
ps.forEach(p => {
observer.observe(p);
});
А вот расширенная демо-версия: