null

CSS: обрезка текста с многоточием и без

В одной из своих предыдущих статей я затрагивала тему работы с длинными словами и принудительных переносов. Теперь я хочу поговорить об обрезке текста средствами CSS.

Достаточно типовая ситуация, когда у вас есть, скажем, карточка товара с изображением, ценой и описанием, но описание некоторых товаров слишком большое. Похожий пример карточки с описанием приведён ниже, это карточки курсов с порталов Openprofession и Coursera. Обратите внимание на то, как решена проблема длинных названий курсов.

Карточки курсов на портале Openprofession (слева) и Coursera(справа).

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

Обрезка до нужного числа строк (без многоточия)

Принцип обрезки будет простой: оставить в видимой области необходимое количество строк, а остальные вынести за пределы контейнера и скрыть.
Возьмем для примера абзац текста, который не должен превышать по высоте 80px, а всё, что сверх, должно быть скрыто.

<div class="truncate-text">
  Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat.
</div>

Способ первый - грубо обрезаем текст до видимой области. Почему грубо, расскажу ниже.

.truncate-text {
  background: #c8ad90;
  padding: 1rem;
  width: 200px;
  height: 80px;
  overflow: hidden;
}

Задаем ограничение по высоте (height: 80px;) и свойство overflow, отвечающее за отображение содержимого за границами родительского контейнера (overflow: hidden;). Для наглядности контейнеру задан фон. Ниже можно видеть, как работает свойство overflow: пример слева без этого свойства (значение по умолчанию visible), справа - с ним (значение hidden).

Осталось добавить градиент того же цвета, что и фон. Используем для этого псевдоэлемент after с аюсолютным позиционированием.

.truncate-text {
  background: #c8ad90;
  padding: 1rem;
  width: 200px;
  height: 80px;
  overflow: hidden;
  position: relative;
}

.truncate-text:after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 40px;
  background: linear-gradient(180deg, transparent, #c8ad90 50%);
}

Следует отметить, что в идеале можно было бы задавать высоту блока кратной высоте строки, чтобы он всегда вмещал целое число строк в видимой области. Или наоборот, заложить под последнюю строку меньше высоты целой строки, чтобы получить эффект наполовину срезанных букв не за счёт градиента.

Как вы могли заметить, у нашего блока были заданы внутренние отступы (padding), и свойство overflow обрезало текст не по границе этих отступов, а по границе блока. В результате мы потеряли отступ снизу, и текст прилегает к нижнему краю блока. Конечно, мы сгладили этот недочет с помощью градиента, но это повод упомянуть о втором способе.

Итак, способ второй - используем колонки.

.truncate-text {
  background: #c8ad90;
  padding: 1rem;
  width: 200px;
  height: 80px;
  overflow: hidden;
  column-width: 200px;
}

Чтобы была понятна разница, покажу как это выглядит без свойства overflow. Текст переносится во вторую и третью колонки, при этом учитываются внутренние отступы, и вы не получите наполовину обрезанную по высоте строку.

Здесь становится видно, что блок плохо подогнан по высоте под 3 строки, слишком много места остается снизу. Поэтому предлагаю явно задать высоту строки и рассчитать для неё высоту блока.

.truncate-text {
  background: #c8ad90;
  padding: 1rem;
  width: 200px;
  overflow: hidden;
  column-width: 200px;
  line-height: 1.3em;
  height: 3.9em;
}

Далее можно добавить градиент, как это делалось выше, откорректировав его высоту.

Обрезка одной строки текста (с многоточием)

Более аккуратный вариант обрезки текста добавляет многоточие в конце строки. Однако следует сразу предупредить, что без прибегания к нестандартным свойствам и с помощью CSS это можно сделать только для однострочного текста. Поэтому разработчики пишут к JS функции, которые удаляют по одному слову (или букве), пока высота текста не начнёт совпадать с требуемой. Мы не будем рассматривать решения на базе JS, но рассмотрим ненадёжный, но красивый способ с применением свойств с вендорными префиксами.

Вариант для одной строки поддерживается широко и достаточно давно, начнём с него.

<h3 class="truncate-text">Длинный заголовок в одну строку, который надо обрезать</h3>
.truncate-text {
  width: 350px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

Обрезка до нужного числа строк (с многоточием)

К сожалению, чтобы провернуть нечто подобное с несколькими строками, придётся прибегнуть к ненадёжному методу. Ненадёжный он потому, что используемые в нём свойства не являются частью стандарта, могут поддерживаться не всеми браузерами и их поддержка может быть прекращена в любой момент. Поэтому они не рекомендованы для использования в продакшене. На данный момент приведённое решение будет работать во всех браузерах на основе Webkit (Chrome, Opera), Firefox и Edge.

Сначала рассмотрим, это работает.

.truncate-text {
  width: 200px;
  height: 80px;
  background: #c8ad90;
  padding: 1rem;
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
}

Сочетание трёх последних строк даёт нам добавление многоточия в конце 3-й строки (при этом строки ниже не скрываются, это надо сделать вручную). Также мы снова имеем проблему текста поверх нижнего внутреннего отступа, которую на этот раз мы решим за счёт изменения разметки.

<div class="container">
  <div class="truncate-text">
    Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat.
  </div>
</div>
.container {
  width: 200px;
  background: #c8ad90;
  padding: 1rem;
}
.truncate-text {
  overflow: hidden;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  line-height: 1.3em;
  height: 3.9em;
}

Рассмотрим эти свойства подробнее.

display: -webkit-box;

Вообще display: box это старый синтаксис display: flex, который используется, например, для поддержки flexbox в старых Safari. При этом обязательно использовать префикс -webkit. В документации Firefox можно найти упоминание значения display: -moz-box, но в поздних версиях оно было заменено на -webkit-box, также Firefox поддерживает значение без префикса (box). Как говорилось выше, это аналог display: flex, и соответственно влияет на расположение дочерних элементов и распределение свободного пространства.

-webkit-box-orient: vertical;

Аналог свойства flex-direction: column. Также является частью старой спецификации flexbox.

-webkit-line-clamp: 2;

Первоначально реализованное в Webkit и поддерживаемое в Firefox и Edge свойство, ограничивающее количество строк контента в блоке. Спецификация CSS Overflow Module Level 3 определяет свойство line-clamp, призванное прийти на замену варианту с префиксом, но для обратной совместимости требуется, чтобы -webkit-line-clamp также поддерживалось. В IE, естественно, это не сработает даже с префиксом.

Живой пример можно найти тут.