Исследование переноса текста и слов

Источник: «Deep Dive into Text Wrapping and Word Breaking»
Давайте поговорим о различных способах управления переноса текста на веб-странице. CSS даёт множество инструментов, позволяющих сделать текст таким, каким мы хотим его видеть. Также рассмотрим трюки с использованием HTML и специальных символов.

Защищая макет

Обычно текст переносится на следующую строку в "местах возможного переноса" — это причудливое название мест, где текст должен разбиваться естественным образом, например между словами или после дефиса. Но иногда можно столкнуться с длинными фрагментами текста, не имеющими возможности мягкого переноса, например, с очень длинными словами или URL-адресами. Это может вызвать проблемы с вёрсткой. Например, текст может переполнить свой контейнер, или он может стать слишком широким и сдвинуть всё с места.

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

Перенос слишком длинного текста

Применение к элементу параметра overflow-wrap: break-word позволяет при необходимости разбивать текст на середине слова. Сначала он попытается сохранить слово неразрывным, переместив его на следующую строку, но затем разорвёт его, если места всё ещё недостаточно.

See the Pen

Существует также overflow-wrap: anywhere, разбивающий слова таким же образом. Разница заключается в том, как он влияет на расчёт размера min-content элемента, для которого он установлен. Это заметно, когда ширина установлена на min-content.

.top {
width: min-content;
overflow-wrap: break-word;
}

.bottom {
width: min-content;
overflow-wrap: anywhere;
}

See the Pen

Элемент top с overflow-wrap: break-word вычисляет min-content так, как будто ни одно слово не разбито, поэтому его ширина становится шириной самого длинного слова. Элемент bottom с overflow-wrap: anywhere вычисляет min-content со всеми разрывами, которые он может создать. Поскольку разрыв может произойти в любом месте, min-content становится равным ширине одного символа.

Помните, что такое поведение проявляется только при использовании min-content. Если бы мы установили для width какое-то жёсткое значение, мы бы увидели одинаковый результат разбиения слов в обоих случаях.

Разбиение слов без пощады

Ещё один вариант разбиения слов — word-break: break-all. Этот вариант даже не пытается сохранить слова целыми — он разбивает их немедленно. Посмотрите.

See the Pen

Обратите внимание, что длинное слово не переносится на следующую строку, как это было бы при использовании overflow. Также обратите внимание, что слово "words" разбито, хотя оно прекрасно поместилось бы на следующей строке.

word-break: break-all без проблем разбивает слова, но всё же осторожно относится к пунктуации. Например, он не будет начинать строку с точки в конце предложения. Если вам нужен действительно беспощадный разрыв, даже с пунктуацией, используйте line-break: anywhere.

See the Pen

Видите, как word-break: break-all переносит "k" вниз, чтобы не начинать вторую строку с "."? В то же время line-break: anywhere это не волнует.

Экспрессивная пунктуация

Давайте посмотрим, как рассмотренные ранее CSS свойства справляются с чрезмерно длинными участками пунктуации.

See the Pen

overflow-wrap: break-word и line-break: anywhere способны сдержать ситуацию, но word-break: break-all снова странно ведёт себя с пунктуацией — на этот раз это приводит к переполнению текста.

Это нужно иметь в виду. Если вы категорически не хотите, чтобы текст переполнялся, учтите, что word-break: break-all не остановит убегающую пунктуацию.

Указание мест, где слова могут разбиваться

Для большего контроля можно вручную вставить в текст возможности разрыва слов с помощью <wbr>. Также можно использовать "пробел нулевой ширины", HTML-сущность &ZeroWidthSpace; (да, как вы видите, он должен быть написан с заглавными буквами!).

Давайте посмотрим на них в действии, перенеся длинный URL по сегментам, который обычно не переносится.

<!-- normal -->
<p>https://subdomain.somewhere.co.uk</p>

<!-- <wbr> -->
<p>https://subdomain<wbr>.somewhere<wbr>.co<wbr>.uk</p>

<!-- &ZeroWidthSpace; -->
<p>https://subdomain&ZeroWidthSpace;.somewhere&ZeroWidthSpace;.co&ZeroWidthSpace;.uk</p>

See the Pen

Автоматический перенос слов

Вы можете указать браузеру разбивать и переносить слова там, где это необходимо, используя hyphens: auto. Правила расстановки переносов определяются языком, поэтому вам нужно указать браузеру, какой язык использовать. Это делается при помощи атрибута lang в HTML непосредственно на соответствующем элементе или в <html>.

<p lang="en">This is just a bit of arbitrary text to show hyphenation in action.</p>
  p {
-webkit-hyphens: auto; /* для Safari */
hyphens: auto;
}

See the Pen

Ручной перенос слов

Также можно взять дело в свои руки и вставить "мягкий перенос" вручную с помощью HTML-сущности &shy;. Он не будет виден, пока браузер не решит перенести текст, и в этом случае перенос появится. Обратите внимание, что в следующем примере мы используем &shy; дважды, но видим его только один раз, когда текст переносится.

<p lang="en">Magic? Abraca&shy;dabra? Abraca&shy;dabra!</p>

See the Pen

Для правильного отображения дефисов необходимо установить hyphens в auto или manual. Удобно, что по умолчанию используется значение hyphens: manual, так что можно обойтись без дополнительного CSS (если только по какой-то причине не объявить hyphens: none).

Предотвращение переноса текста

Давайте сменим обстановку. Бывают случаи, когда текст не должен свободно переносится, чтобы можно было лучше контролировать представление контента. Есть несколько инструментов, которые помогут в этом.

Во-первых, это white-space: nowrap. Поместите его на элемент, чтобы предотвратить обратный перенос его текста.

See the Pen

Предварительное форматирование текста

Также есть white-space: pre, который перенесёт текст так, как он напечатан в HTML. Однако будьте осторожны, так как при этом сохраняются пробелы в HTML, поэтому следите за форматированием. Кроме того, можно использовать тег <pre>, чтобы получить те же результаты (по умолчанию он содержит white-space: pre).

<!-- форматирование этого HTML приводит к появлению лишних пробелов! -->
<p>
What's worse, ignorance or apathy?
I don't know and I don't care.
</p>

<!-- более плотное форматирование, которое "обнимает" текст -->
<p>What's worse, ignorance or apathy?
I don't know and I don't care.</p>

<!-- то же самое, что и выше, но с использованием <pre> -->
<pre>What's worse, ignorance or apathy?
I don't know and I don't care.</pre>
p {
white-space: pre;
}

pre {
/* <pre> устанавливает font-family: monospace, это можно отменить */
font-family: inherit;
}

See the Pen

Перенос, где слова не могут переноситься

Для переноса строки можно использовать <br> внутри элемента с white-space: nowrap или white-space: pre. Текст будет перенесён.

Но что произойдёт, если использовать <wbr> в таком элементе? Вопрос с подвохом… потому что браузеры не согласны. Chrome/Edge распознает <wbr> и потенциально может перенести, а Firefox/Safari — нет.

Когда дело доходит до пробела нулевой ширины (&ZeroWidthSpace;), браузеры сходятся во мнении. Ни один из них не перенесёт текст с white-space: nowrap или white-space: pre.

<p>Darth Vader: Nooooooooooooo<br>oooo!</p>

<p>Darth Vader: Nooooooooooooo<wbr>oooo!</p>

<p>Darth Vader: Nooooooooooooo&ZeroWidthSpace;oooo!</p>

See the Pen

Неразрывные пробелы

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

Неразрывный пробел (&nbsp;) часто используется для того, чтобы оставить пробел между словами, но запретить перенос строки между ними.

<p>Something I've noticed is designers don't seem to like orphans.</p>

<p>Something I've noticed is designers don't seem to like&nbsp;orphans.</p>

See the Pen

Соединители слов и неразрывные дефисы

Текст может естественным образом переноситься даже без пробелов, например после дефиса. Чтобы предотвратить перенос без добавления пробела, можно использовать &NoBreak; (с учётом регистра!) для получения "соединителя слов". Для дефисов можно использовать &#8209; (у него нет красивого имени HTML-сущности), чтобы получить "неразрывный дефис".

<p>Turn right here to get on I-85.</p>

<p>Turn right here to get on I-&NoBreak;85.</p>

<p>Turn right here to get on I&#8209;85.</p>

See the Pen

CJK текст и перенос слов

CJK текст (Chinese/Japanese/Korean) в некоторых случаях ведёт себя иначе, чем текст не-CJK. Определённые свойства и значения CSS могут использоваться для дополнительного контроля над переносом CJK-текста.

Стандартное поведение браузера позволяет переносить слова в тексте CJK. Это означает, что word-break: normal (по умолчанию) и word-break: break-all дадут одинаковые результаты. Однако вы можете использовать word-break: keep-all, чтобы предотвратить перенос слов в CJK тексте (не CJK текст не будет затронут).

Вот пример на корейском языке. Обратите внимание, как слово "자랑스럽게" переносится или не переносится.

See the Pen

Однако будьте осторожны: в китайском и японском языках не используются пробелы между словами, как в корейском, поэтому word-break: keep-all может привести к переполнению длинного текста, если не принять другие меры.

CJK текст и правила переноса строк

Мы говорили о line-break: anywhere ранее с не-CJK текстом и, что он без проблем переносит знаки препинания. То же самое справедливо и для CJK текста.

Вот пример на японском языке. Обратите внимание, как "。" разрешается или не разрешается начинать строку.

See the Pen

Существуют и другие значения для line-break, влияющие на перенос текста CJK: loose, normal и strict. Эти значения указывают браузеру, какие правила использовать при определении места разрыва строки. W3C описывает несколько правил, и браузеры также могут добавлять свои собственные правила.

Стоит упомянуть: Переполнение элементов

CSS свойство overflow не относится к тексту, но часто используется для того, чтобы текст не выходил за пределы элемента, ширина или высота которого ограничена.

.top {
white-space: nowrap;
overflow: auto;
}

.bottom {
white-space: nowrap;
overflow: hidden;
}

See the Pen

Как видите, значение auto позволяет прокручивать содержимое (auto показывает полосы прокрутки только при необходимости, scroll показывает их всегда). Значение hidden просто отсекает содержимое и оставляет его в таком виде.

overflow на самом деле является сокращением для установки overflow-x и overflow-y, для горизонтального и вертикального переполнения соответственно. Используйте то, что вам больше подходит.

Мы можем развить принцип overflow: hidden, добавив text-overflow: ellipsis. Текст всё равно будет обрезан, но мы получим красивое многоточие в качестве индикатора.

p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

See the Pen

Бонусный трюк: Псевдоэлемент разрыв строки

Можно сделать принудительный перенос строки перед и/или после встроенного элемента, сохранив его как встроенный элемент, с помощью небольшого трюка с псевдоэлементами.

Сначала установите content псевдоэлемента ::before или ::after в \A, что даст вам символ новой строки. Затем установите white-space: pre, чтобы обеспечить поддержку символа новой строки.

<p>Things that go <span>bump</span> in the night.</p>
span {
background-color: #000;
}

span::before, span::after {
content: '\A';
white-space: pre;
}

See the Pen

Можно было бы просто поставить display: block на <span>, чтобы получить те же самые разрывы, но тогда он уже не будет встроенным. Цвет фона позволяет увидеть, что при использовании этого метода у нас всё ещё есть встроенный элемент.

Бонусные заметки

Время подводить итоги

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

Спасибо за прочтение!

Дополнительные материалы

Предыдущая Статья

Руководство по MySQL JOIN с примерами

Следующая Статья

Husky: Форматирование, линтинг и тестирование при коммите или пуше