Исследование переноса текста и слов
Защищая макет
Обычно текст переносится на следующую строку в "местах возможного переноса" — это причудливое название мест, где текст должен разбиваться естественным образом, например между словами или после дефиса. Но иногда можно столкнуться с длинными фрагментами текста, не имеющими возможности мягкого переноса, например, с очень длинными словами или URL-адресами. Это может вызвать проблемы с вёрсткой. Например, текст может переполнить свой контейнер, или он может стать слишком широким и сдвинуть всё с места.
Хорошим защитным кодированием считается предвидение проблем, связанных с тем, что текст не разбивается. К счастью, CSS предоставляет для этого определённые инструменты.
Перенос слишком длинного текста
Применение к элементу параметра overflow-wrap: break-word
позволяет при необходимости разбивать текст на середине слова. Сначала он попытается сохранить слово неразрывным, переместив его на следующую строку, но затем разорвёт его, если места всё ещё недостаточно.
Существует также overflow-wrap: anywhere
, разбивающий слова таким же образом. Разница заключается в том, как он влияет на расчёт размера min-content
элемента, для которого он установлен. Это заметно, когда ширина установлена на min-content
.
.top {
width: min-content;
overflow-wrap: break-word;
}
.bottom {
width: min-content;
overflow-wrap: anywhere;
}
Элемент top
с overflow-wrap: break-word
вычисляет min-content
так, как будто ни одно слово не разбито, поэтому его ширина становится шириной самого длинного слова. Элемент bottom
с overflow-wrap: anywhere
вычисляет min-content
со всеми разрывами, которые он может создать. Поскольку разрыв может произойти в любом месте, min-content
становится равным ширине одного символа.
Помните, что такое поведение проявляется только при использовании min-content
. Если бы мы установили для width
какое-то жёсткое значение, мы бы увидели одинаковый результат разбиения слов в обоих случаях.
Разбиение слов без пощады
Ещё один вариант разбиения слов — word-break: break-all
. Этот вариант даже не пытается сохранить слова целыми — он разбивает их немедленно. Посмотрите.
Обратите внимание, что длинное слово не переносится на следующую строку, как это было бы при использовании overflow
. Также обратите внимание, что слово "words" разбито, хотя оно прекрасно поместилось бы на следующей строке.
word-break: break-all
без проблем разбивает слова, но всё же осторожно относится к пунктуации. Например, он не будет начинать строку с точки в конце предложения. Если вам нужен действительно беспощадный разрыв, даже с пунктуацией, используйте line-break: anywhere
.
Видите, как word-break: break-all
переносит "k" вниз, чтобы не начинать вторую строку с "."? В то же время line-break: anywhere
это не волнует.
Экспрессивная пунктуация
Давайте посмотрим, как рассмотренные ранее CSS свойства справляются с чрезмерно длинными участками пунктуации.
overflow-wrap: break-word
и line-break: anywhere
способны сдержать ситуацию, но word-break: break-all
снова странно ведёт себя с пунктуацией — на этот раз это приводит к переполнению текста.
Это нужно иметь в виду. Если вы категорически не хотите, чтобы текст переполнялся, учтите, что word-break: break-all
не остановит убегающую пунктуацию.
Указание мест, где слова могут разбиваться
Для большего контроля можно вручную вставить в текст возможности разрыва слов с помощью <wbr>
. Также можно использовать "пробел нулевой ширины", HTML-сущность ​
(да, как вы видите, он должен быть написан с заглавными буквами!).
Давайте посмотрим на них в действии, перенеся длинный URL по сегментам, который обычно не переносится.
<!-- normal -->
<p>https://subdomain.somewhere.co.uk</p>
<!-- <wbr> -->
<p>https://subdomain<wbr>.somewhere<wbr>.co<wbr>.uk</p>
<!-- ​ -->
<p>https://subdomain​.somewhere​.co​.uk</p>
Автоматический перенос слов
Вы можете указать браузеру разбивать и переносить слова там, где это необходимо, используя 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;
}
Ручной перенос слов
Также можно взять дело в свои руки и вставить "мягкий перенос" вручную с помощью HTML-сущности ­
. Он не будет виден, пока браузер не решит перенести текст, и в этом случае перенос появится. Обратите внимание, что в следующем примере мы используем ­
дважды, но видим его только один раз, когда текст переносится.
<p lang="en">Magic? Abraca­dabra? Abraca­dabra!</p>
Для правильного отображения дефисов необходимо установить hyphens
в auto
или manual
. Удобно, что по умолчанию используется значение hyphens: manual
, так что можно обойтись без дополнительного CSS (если только по какой-то причине не объявить hyphens: none
).
Предотвращение переноса текста
Давайте сменим обстановку. Бывают случаи, когда текст не должен свободно переносится, чтобы можно было лучше контролировать представление контента. Есть несколько инструментов, которые помогут в этом.
Во-первых, это white-space: nowrap
. Поместите его на элемент, чтобы предотвратить обратный перенос его текста.
Предварительное форматирование текста
Также есть 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;
}
Перенос, где слова не могут переноситься
Для переноса строки можно использовать <br>
внутри элемента с white-space: nowrap
или white-space: pre
. Текст будет перенесён.
Но что произойдёт, если использовать <wbr>
в таком элементе? Вопрос с подвохом… потому что браузеры не согласны. Chrome/Edge распознает <wbr>
и потенциально может перенести, а Firefox/Safari — нет.
Когда дело доходит до пробела нулевой ширины (​
), браузеры сходятся во мнении. Ни один из них не перенесёт текст с white-space: nowrap
или white-space: pre
.
<p>Darth Vader: Nooooooooooooo<br>oooo!</p>
<p>Darth Vader: Nooooooooooooo<wbr>oooo!</p>
<p>Darth Vader: Nooooooooooooo​oooo!</p>
Неразрывные пробелы
Иногда требуется, чтобы текст переносился свободно, за исключением очень специфических мест. Хорошие новости! Существует несколько специализированных HTML-сущностей, позволяющих сделать именно это.
Неразрывный пробел (
) часто используется для того, чтобы оставить пробел между словами, но запретить перенос строки между ними.
<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 orphans.</p>
Соединители слов и неразрывные дефисы
Текст может естественным образом переноситься даже без пробелов, например после дефиса. Чтобы предотвратить перенос без добавления пробела, можно использовать ⁠
(с учётом регистра!) для получения "соединителя слов". Для дефисов можно использовать ‑
(у него нет красивого имени HTML-сущности), чтобы получить "неразрывный дефис".
<p>Turn right here to get on I-85.</p>
<p>Turn right here to get on I-⁠85.</p>
<p>Turn right here to get on I‑85.</p>
CJK текст и перенос слов
CJK текст (Chinese/Japanese/Korean) в некоторых случаях ведёт себя иначе, чем текст не-CJK. Определённые свойства и значения CSS могут использоваться для дополнительного контроля над переносом CJK-текста.
Стандартное поведение браузера позволяет переносить слова в тексте CJK. Это означает, что word-break: normal
(по умолчанию) и word-break: break-all
дадут одинаковые результаты. Однако вы можете использовать word-break: keep-all
, чтобы предотвратить перенос слов в CJK тексте (не CJK текст не будет затронут).
Вот пример на корейском языке. Обратите внимание, как слово "자랑스럽게" переносится или не переносится.
Однако будьте осторожны: в китайском и японском языках не используются пробелы между словами, как в корейском, поэтому word-break: keep-all
может привести к переполнению длинного текста, если не принять другие меры.
CJK текст и правила переноса строк
Мы говорили о line-break: anywhere
ранее с не-CJK текстом и, что он без проблем переносит знаки препинания. То же самое справедливо и для CJK текста.
Вот пример на японском языке. Обратите внимание, как "。" разрешается или не разрешается начинать строку.
Существуют и другие значения для line-break
, влияющие на перенос текста CJK: loose
, normal
и strict
. Эти значения указывают браузеру, какие правила использовать при определении места разрыва строки. W3C описывает несколько правил, и браузеры также могут добавлять свои собственные правила.
Стоит упомянуть: Переполнение элементов
CSS свойство overflow
не относится к тексту, но часто используется для того, чтобы текст не выходил за пределы элемента, ширина или высота которого ограничена.
.top {
white-space: nowrap;
overflow: auto;
}
.bottom {
white-space: nowrap;
overflow: hidden;
}
Как видите, значение auto
позволяет прокручивать содержимое (auto
показывает полосы прокрутки только при необходимости, scroll
показывает их всегда). Значение hidden
просто отсекает содержимое и оставляет его в таком виде.
overflow
на самом деле является сокращением для установки overflow-x
и overflow-y
, для горизонтального и вертикального переполнения соответственно. Используйте то, что вам больше подходит.
Мы можем развить принцип overflow: hidden
, добавив text-overflow: ellipsis
. Текст всё равно будет обрезан, но мы получим красивое многоточие в качестве индикатора.
p {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
Бонусный трюк: Псевдоэлемент разрыв строки
Можно сделать принудительный перенос строки перед и/или после встроенного элемента, сохранив его как встроенный элемент, с помощью небольшого трюка с псевдоэлементами.
Сначала установите 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;
}
Можно было бы просто поставить display: block
на <span>
, чтобы получить те же самые разрывы, но тогда он уже не будет встроенным. Цвет фона позволяет увидеть, что при использовании этого метода у нас всё ещё есть встроенный элемент.
Бонусные заметки
Существует старое CSS свойство под названием
word-wrap
. Оно нестандартное, и браузеры рассматривают его как псевдоним дляoverflow-wrap
.У CSS свойства
white-space
есть ещё несколько значений, которые мы не рассматривали:pre-wrap
,pre-line
иbreak-spaces
. В отличие от тех, которые мы рассмотрели, они не предотвращают перенос текста.В спецификации CSS Text Module Level 4 описано CSS свойство
text-wrap
, которое выглядит интересно, но на момент написания статьи (в мае 2022 года) ни в одном браузере оно не реализовано.Обновление: Браузеры его реализовали! Подробнее о том, как использовать его для более красивого переноса текста.
Время подводить итоги
Есть много вещей, связанных с отображением текста на веб-странице. В большинстве случаев не нужно задумываться об этом, поскольку браузеры всё делают сами. Но в тех случаях, когда необходимо больше контроля, приятно знать, что существует множество вариантов.
Спасибо за прочтение!