Главная » Статьи » Усеченный многострочный текст с кнопкой «показать больше» (только с помощью CSS)

Усеченный многострочный текст с кнопкой «показать больше» (только с помощью CSS)

Усеченный многострочный текст с кнопкой «показать больше» (только с помощью CSS)

От автора: на днях у нас в офисе зашла дискуссия об усечении текста, и я с тех пор задался вопросом, зашел ли CSS достаточно далеко, чтобы можно было правильно сделать усеченный текст, например, с помощью checkbox.

То есть поддерживает ли он следующее:

Несколько строк

Кнопку «Показать больше», которая отображает полный текст при нажатии

text-overflow: ellipsis не поддерживает несколько строк, но я вспомнил о свойстве line-clamp, которое можно использовать для получения многострочного усеченного текста. К счастью, на CSS Tricks есть хорошая рабочая демо-версия, плюс поддержка браузерами теперь довольно приличная. Здорово!

А как насчет кнопки «показать больше»?

Создание кнопки «показать больше» требует некоторой нестандартной хакерской работы с CSS. Я не мог использовать button или тег <a>, так как это работа для печально известного хака с чек-боксом, и для этого потребовался элемент метки и… чек-бокс! Итак, я получил следующую разметку:

<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;
}

И действительно, это сделало именно то, что я хотел: нажмите кнопку, и мы переключаемся с усеченного текста к полному. Красиво.

Но… но… но это хак, а что насчет a11y??

В самом деле, хак с чек-боксом не такой уж проблемный в плане семантики, но и не очень доступный, так как большинство просто наивно устанавливают display: none для чек-бокса. Это приводит к нарушению навигации с клавиатуры, поскольку вы не можете переключиться через табуляцию на скрытый чек-бокс, и переключение на метку не помогает, поскольку клавиши пробела / ввода не перенаправляют событие на чек-бокс, как это происходит в случае нажатия кнопки (это имеет смысл, так как обычно вы бы выбрали бы чек-бокс вместо метки).

Я хочу, чтобы мои примеры и демонстрации были как можно более доступными, поэтому, чтобы решить эту проблему, я сначала сделал чек-бокс focusable / tabbable, хотя все еще невидимым, например, так:

input { opacity: 0; position: absolute; pointer-events: none;
}

Единственная оставшаяся проблема заключалась в том, что теперь я мог переключаться табуляцией на чсек-бокс, но у меня не было возможности узнать, без программы чтения с экрана, выделен ли он фокусом или нет … ну … он ведь невидим. Чтобы решить эту проблему, я заставил метку получать (примерно) стиль фокуса браузера по умолчанию, когда чек-бокс выделен фокусом:

input:focus ~ label { outline: -webkit-focus-ring-color auto 5px;
}

Проверьте комбинированную демонстрацию, попробуйте нажать на кнопку мышью, а затем с помощью клавиатуры и клавиши пробела!

Что если я не знаю, будет ли текст усекаться или нет? Как мне отображать кнопку динамически?

Я попытался выяснить, что я могу сделать, чтобы динамически отображать кнопку, когда произвольный текст в абзаце слишком длинный, и скрывать ее в противном случае. К сожалению, это единственная функция, которую я просто не смог сделать с помощью CSS, потому что для этого требуется селектор :truncated, чтобы мы могли сделать что-то вроде этого:

.read-more { display: none; }
p:truncated + .read-more { display: block; }

Оказывается, у других была та же идея, но обсуждение 2014 года никуда не привело. Бу! Если вам действительно нужна эта функция, рассмотрите возможность использования JS до тех пор, пока не появится поддержка браузерами, например:

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);
});

А вот расширенная демонстрация с JS хелпером и изменением размера блока (откройте в Codepen и измените размер окна браузера, чтобы увидеть это поведение):

Автор: Paul Bakaus

Источник: https://paulbakaus.com

Редакция: Команда webformyself.