CSS свойство "white-space" и все, что с ним связано.

Опубликовано: 21 февраля 2014      Автор:

Большинство компонентов CSS спецификации тесно взаимосвязаны, и чтобы полностью понять назначение и способ применения некоторых из них, они должны рассматриваться в комплексе с другими родственными им компонентами. В данной публикации речь пойдет о таких компонентах CSS 2.1 спецификации, как свойство white-space и одноименная модель обработки пробельного контента. Само свойство представляет собой интерфейс, с помощью которого мы с вами можем управлять поведением пользовательских агентов, реализующих модель обработки white-space. Поскольку первопричиной появления этих двух компонентов является пробельный контент, то с него и начнем.

Пробельный контент.

Несмотря на то, что термин «пробельный контент» созвучен со словом «пробел», это вовсе не означает, что такой контент состоит исключительно из символов пробела. Согласно CSS синтаксиса этот контент может состоять из следующих символов: пробел (U+0020), табуляция (U+0009), перевод строки (U+000А), возврат каретки (U+000D) и перевод страницы (U+000C) или любой комбинации этих символов. Причем входящий в набор допустимых в данном случае символов пробел, должен быть представлен именно указанным символом, а не его вариациями типа широкого пробела (U+2003), идеографического пробела (U+3000) и им подобным.

Обработка пробельного контента HTML5 спецификацией аналогична той, которая рассматривается в данной статье. Алгоритм парсинга документа игнорирует все, находящиеся за рамками тегов элементов символы форматирования текста (перечислены выше). Внутри тегов такие символы применяются лишь для разделения используемых в них атрибутов и им подобных компонентов, во всех остальных случаях они игнорируются. Более того, наличие пробельного контента сразу после знака <, непосредственно перед именем тега (или слешем, если тег закрывающий), недопустимо. В этом случае происходит ошибка парсинга, в результате которой тег игнорируется и обрабатывается как обычный текст.

В обоих стандартах образование новой строки возможно несколькими способами:

  • с помощью символа возврата каретки (U+000D);
  • с помощью символа перевода строки (U+000A);
  • комбинацией этих двух символов (U+000D, U+000A), причем только в такой последовательности;
  • с помощью кодовой либо мнемонической ссылки на символ перевода строки (U+000A).

HTML5 спецификация для этих целей также предусматривает специальный элемент разрыва строки <br> ("break"). Как и в случае с символами, образующими горизонтальное свободное пространство (пробелы, табуляция), множественные символы разрывов строк также сворачиваются в единый символ новой строки.

Такая обработка исходного текста связана с тем, что разработчики, создавая разметку документа, как правило, используют пробельные символы с целью визуального форматирования кода, что позволяет представить его в более читабельном виде, и облегчает процесс модификации и сопровождения. Однако, после парсинга исходного кода, избыточные пробельные символы удаляются, при этом пользовательский агент руководствуется определяемой в CSS 2.1 спецификации моделью обработки пробельного контента white-space, которой мы коснемся позже.

Как вам, вероятно, уже хорошо известно, для предотвращения сворачивания пробельного контента можно воспользоваться таким HTML элементом как <pre>, позволяющим сохранить форматирование текста при его отображении. С этой же целью при формировании текста можно также использовать ссылки на соответствующие символы с помощью мнемоник или их кодовых аналогов (&nbsp; или &#160; для неразрывного пробела, &shy; или &#173; для определения места мягкого переноса и т.д.).

Свойство white-space.

CSS стандарт предоставляет нам более гибкий механизм управления пробельным контентом, реализуемый с помощью свойства white-space. Давайте рассмотрим какой эффект имеет каждое из допустимых значений. Для наглядности в демонстрационном примере символы пробелов между словами отмечены зеленым цветом (space), табуляции — голубым (tab), а переносы строк — красным (break).

normal

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

<div style="width:450px;height:150px;white-space:normal">
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.
</div>
Отформатированныйsсsпомощьюsпробельногоsконтентаb
фрагментsтекста.

Небольшой эксперимент, сужаем контейнер до 200 пикселей.

<div style="width:200px;height:150px;white-space:normal">
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.
</div>
Отформатированныйsсb
помощьюsпробельногоb
контентаsфрагментsтекста.

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

pre

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

<div style="width:450px;height:150px;white-space:pre">
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.
</div>
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.

Уменьшим ширину контейнера до 200 пикселей.

<div style="width:200px;height:150px;white-space:pre">
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.
</div>
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.

Пример наглядно показывает, что при использовании значения pre контент форматируемого элемента представляется в точности так, как это предусмотрено исходным кодом, независимо от размера элемента-контейнера. Естественные разрывы строк не применяются и пробельный контент сохраняется в его первоначальной форме. В принципе, это аналогично заключению контента в рамки HTML элемента <pre>.

nowrap

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

<div style="width:450px;height:150px;white-space:nowrap">
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.
</div>
Отформатированныйsсsпомощьюsпробельногоsконтентаsфрагментsтекста.

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

pre-wrap

Это значение предотвращает сворачивание последовательностей пробельных символов. Переводы строк применяются, причем как явные, так и неявные.

<div style="width:450px;height:150px;white-space:pre-wrap">
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.
</div>
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.

Теперь уменьшим ширину дива до 200 пикселей.

<div style="width:200px;height:150px;white-space:pre-wrap">
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.
</div>
Отформатированныйsсb
помощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.

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

pre-line

Как и при нормальной обработке избыточный пробельный контент сворачивается, табуляция игнорируется, при необходимости переноса текста применяются естественные разрывы строк, однако, сохраняются и явные разрывы строк, используемые в исходном коде документа.

<div style="width:450px;height:150px;white-space:pre-line">
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.
</div>
Отформатированныйsсsпомощьюsпробельногоb
b
контентаb
фрагментsтекста.

Теперь уменьшим ширину контейнера до 200 пикселей.

<div style="width:200px;height:150px;white-space:pre-line">
Отформатированныйsсsпомощьюsssпробельногоb
b
    t    контентаb
    t    фрагментsssтекста.
</div>
Отформатированныйsсb
помощьюsпробельногоb
b
контентаb
фрагментsтекста.

При сужении родительского элемента пользовательский агент, как и в предыдущем случае, применил дополнительный разрыв строки, необходимый для содержания контента в рамках контейнера, однако, присутствие всего остального («лишнего») пробельного контента минимизировано или проигнорировано.

Несколько советов.

В некоторых ситуациях использование CSS свойства white-space является незаменимым. В качестве удачного примера можно привести случаи, когда в строке текста используются отдельные фразы, термины либо участки кода, которые с целью корректного и однозначного восприятия пользователем должны быть неразрывны. При отображении пользовательским агентом такие фрагменты текста могут быть автоматически разорваны с последующим частичным переносом на другую строку. Для сохранения целостности требуемого участка текста, к нему нужно применять CSS форматирование, а точнее свойство white-space со значением nowrap. При этом форматируется лишь необходимый фрагмент текста, который представлен в виде отдельного элемента. В нынешнее время интенсивного развития отзывчивого дизайна данный вопрос особо актуален, поскольку размеры элементов, содержащих блоки текста, зачастую изменяются очень гибко и непредсказуемо.

Стоит обратить внимание на то, что использование последних двух значений (pre-wrap и pre-line) ограничено проблемами кроссбраузерности. Ранние версии определенных браузеров их не поддерживают. Эти значения игнорируется в IE ниже 7 версии и в Firefox ниже третьей. pre-line к тому же не поддерживается в Opera до 9.2 версии.

Для того чтобы избежать неприятных ситуаций в будущем, необходимо четко понимать, что свойство white-space воздействует на элементы только строчного уровня, однако оно не применяется непосредственно к этим элементам. То есть оно должно быть определено для их блочного элемента-контейнера. В нашем примере в качестве контейнера использован элемент <div>. На сам блок, к которому оно применяется, это свойство никаким образом не влияет, оно форматирует исключительно его содержимое.

Модель обработки пробельного контента "white-space".

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

Здесь давайте сделаем небольшое отступление, необходимое для прояснения ситуации с используемыми в спецификации и данной статье терминами. Когда мы, фронт-энд разработчики говорим о символе, мы в первую очередь имеем в виду отображаемый на экране символ буквы, цифры иероглифа и т.д., однако, если смотреть с колокольни бэк-энд разработчика, то символ, это не что иное как байт данных, называемый октетом, которому соответствует используемый при визуализации документа графический объект — глиф, посредством которого и отображается нужная буква, цифра, иероглиф или любой другой визуальный символ. Соответствие между октетами и глифами устанавливается, как вам известно, с помощью таблицы кодировки. Все символы делятся на различные категории в зависимости от их назначения и характеристик. Здесь я выделю лишь несколько категорий, которые упоминаются в статье:

  • Базовые символы используются для представления основных символов — букв, цифр, иероглифов и т.д. Такие символы являются протяжёнными, то есть они занимают место в строке при отображении.
  • Управляющие символы в ASCII кодировке относятся к символам с кодами от 00h до 31h, которые используются в служебных целях — для управления устройствами, форматирования текста и кода и т.п.
  • Модифицирующие символы в противоположность базовым являются непротяжёнными (бесширинными), не занимающими места в строке и используются для модификации базовых символов. Это так называемые диакритические знаки — ударение, квадрат или куб числа, градус, акцент и т.д.
Категоризация значений.

Теперь давайте вернемся к основной теме. White-space модель — это механизм, с помощью которого мы, посредством манипуляции значениями свойства white-space можем управлять поведением браузера при обработке пробельного контента. Прежде всего, необходимо знать, что эта модель поведения применяется к каждому элементу строчного уровня, включая анонимные внутристрочные элементы (то есть те, для которых не определены строчные элементы явным образом, например, включенный в блочный элемент фрагмент текста, не заключённый в отдельный элемент строчного уровня).

Проанализировав пять допустимых значений свойства white-space можно выделить шесть действий, которые они предписывают к выполнению:

  1. Сворачивать последовательности пробельных символов.
  2. Не сворачивать последовательности пробельных символов.
  3. Допускать явные (указанные в разметке) разрывы строк.
  4. Допускать неявные (естественные) разрывы строк.
  5. Подавлять явные (указанные в разметке) разрывы строк.
  6. Подавлять неявные (естественные) разрывы строк.

Теперь, используя латинские буквы, указанные при перечислении этих действий, значения свойства white-space можно классифицировать следующим образом:

A
Значения, обязывающие пользовательский агент сворачивать пробельный контент:
normal, nowrap и pre-line.
B
Значения, запрещающие пользовательскому агенту сворачивать пробельный контент:
pre и pre-wrap.
D
Значения, допускающие неявные, используемые пользовательским агентом естественные разрывы строк:
normal, pre-wrap и pre-line.
AE
Значения, обязывающие пользовательский агент сворачивать пробельный контент и подавлять явно указанные разрывы строк:
normal и nowrap.
Хотя при значении nowrap подавляются не только явные, но и неявные разрывы строк, оно также попадает под эту категорию, так как действие F в случае использования этого значения всё-таки выполняется. Однако его можно выделить в отдельную подкатегорию, учитывающую все условия данного значения — AEF
BC
Значение pre-wrap, запрещающее пользовательскому агенту сворачивать пробельный контент и допускающее явные разрывы строк (хотя и неявные тоже, и при этом уточнении можно определить категорию BCD).

Почему я сгруппировал значения таким образом? Ведь существуют и другие комбинации. Используя эту маркировку можно определить отдельную категорию для каждого значения. Дело в том, что именно по таким признакам значения свойства white-space используются в одноименной модели.

Стоит также отметить один существенный момент. В спецификации CSS 2.1 при описании данной модели говориться о местах возможных разрывов строк, которые определяются пользовательским агентом при обработке контента строчного уровня. При этом стандарт не берётся устанавливать какие-либо правила, по определению таких мест, возлагая эту ответственность на пользовательского агента. Такие места, как правило, находятся: в конце языковых единиц — слов; после дефиса в сложных словах, пишущихся через дефис; в конце любых непрерывных последовательностей буквенно-цифровых символов, разделяемых пробелом. В общем, это места, где, по мнению пользовательского агента можно сделать перенос текстового контента на другую строку без нарушения его целостности.

Сворачивание пробельного контента.

Итак, давайте приступим к рассмотрению непосредственно самой модели.

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

  1. В первую очередь, если используются значения категории A (то есть обязывающие пользовательского агента сворачивать избыточный пробельный контент), то удаляется любой символ табуляции, возврата каретки или пробела, который находится рядом с символом перевода строки (U+000A). Другими словами, символ перевода строки «очищается» от любых других, рядом расположенных пробельных символов, при этом сам символ перевода строки пока сохраняется. Идем дальше.
  2. Теперь противоположный случай, когда используются значения категории B (предписывающие не сворачивать избыточный пробельный контент), все непрерывные последовательности любого количества пробелов (U+0020) выделяются в отдельный текстовый элемент, именуемый целостная последовательность пробелов, который получает все свойственные языковым единицам права. То есть при использовании свойства pre-wrap, образующего свою подкатегорию BC (которое в отличие от свойства pre допускает естественные разрывы строк), после целостной последовательности пробелов сохраняется возможность разрыва строки.
  3. Далее, если имеет место одно из значений категории AE (игнорирующие явно указанные переводы строк), пользовательский агент преобразует все символы переводов строк (сохраненные в первом шаге) в пробелы, либо просто игнорирует их (в зависимости от того, что предусмотрено используемым для обработки текста алгоритмом).
  4. Последние действия, связанные со сворачиванием пробельных символов предусматривают преобразование каждого символа табуляции в пробел и удаление каждого пробела, следующего за таким же символом пробела, если применяются значения категории A (заставляющие сворачивать избыточный пробельный контент). Причем рядом находящийся второй пробел удаляется даже в тех случаях, если он расположен перед или внутри другого внутристрочного бокса. Для того чтобы это понять рассмотрим пример.

    Возьмем простейший фрагмент, состоящий из абзаца, который содержит только два элемента строчного уровня, образующие два соседних внутристрочных бокса. Первый с желтой границей, второй с красной. Для наглядности символ пробела выделим таким же образом, как и в предыдущих примерах, только теперь нам нужно их различать и поэтому мы их пронумеруем (s1, s2 и s3). Свойство white-space родительского элемента контейнера <p> может принимать одно из значений категории A (согласно нашей классификации это: normal, nowrap или pre-line).

    Стили одни для всех случаев:

    p {white-space: normal} /* nowrap или pre-line */
    #box1 {border:2px solid yellow}
    #box2 {border:2px solid red}

    Случай первый:
    В исходном коде пробелы присутствуют в трёх местах — в конце первого элемента(s1), в начале второго (s3) и между ними (s2):

    <p>
    <span id="box1">Box1s1</span>s2<span id="box2">s3Box2</span>
    </p>

    Box1s1Box2

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

    Случай второй:
    В исходном коде пробелы присутствуют в двух местах — между элементами и в начале второго:

    <p>
    <span id="box1">Box1</span>s2<span id="box2">s3Box2</span>
    </p>

    Box1s2Box2

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

    Случай третий:
    В исходном коде пробелы присутствуют в двух местах — в конце первого элемента и в начале второго:

    <p>
    <span id="box1">Box1s1</span><span id="box2">s3Box2</span>
    </p>

    Box1s1Box2

    Как и следовало ожидать, остался только первый пробел.

    Случай четвертый:
    Последний, третий пробел (s3) будет отображен лишь при условии отсутствия первых двух:

    <p>
    <span id="box1">Box1</span><span id="box2">s3Box2</span>
    </p>

    Box1s3Box2

    Случай пятый и последний:
    Теперь всё, как и в первом случае, только вместо первого пробела мы используем символ табуляции:

    <p>
    <span id="box1">Box1    t    </span>s2<span id="box2">s3Box2</span>
    </p>

    Box1s1Box2

    Браузер, следуя модели, преобразовал символ табуляции в пробел и убрал два других, «лишних» пробела.

Распределение строчного контента по строкам.

После выполнения действий, описанных в предыдущих четырёх шагах, пользовательский агент приступает ко второму этапу обработки строчного контента, предусмотренному моделью white-space — распределению контента по строкам, то есть теперь строчный контент можно сказать «заливается» в строки, согласно алгоритму обработки двунаправленного текста и в соответствии с применяемыми значениями свойства white-space. Данным процессом предусмотрено распределение генерируемых для элементов строчного уровня внутристрочных боксов и автоматически образуемых для неразмеченных фрагментов текста анонимных внутристрочных боксов по строкам.

Для естественной «заливки» строчного контента необходимо определить места возможных разрывов строк (то есть места в которых перенос контента на другую строку допускается, так как не при этом не нарушается его целостность). Это нужно для тех случаев, когда используются значения из категории D. Так вот эти места определяются пользовательским агентом ещё до выполнения действий по сворачиванию пробельного контента (перечисленные выше четыре шага) на основе необработанного текста. То есть сначала устанавливаются допустимые места разрывов строк и лишь потом текст при необходимости «очищается» от избыточного пробельного контента.

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

  1. При значениях категории A (удаляющие «лишний» пробельный контент) встречаемые в начале строки пробелы удаляются. То есть даже те пробелы, которые остались после выполнения предыдущих, «очищающих» действий лишь потому, что они необходимы для разделения единиц строкового контента, в результате естественной «заливки» могут оказаться первым символом следующей строки и в этом случае они тоже удаляются.
  2. Далее, если пользовательский агент встречает символ табуляции, то применяется горизонтальный сдвиг, размер которого равен восьми пробелам используемого типа шрифта. Этот сдвиг производится от начального края следующего глифа (точки, где располагался бы глиф следующего базового символа, если бы символ табуляции отсутствовал) и до следующей позиции табуляции, находящейся на расстоянии восьми пробелов от начальной точки табуляции.
  3. В конце сформированной строки может оказаться пробел, поэтому если используются значения категории A, то он удаляется.
  4. Более того, спецификация позволяет пользовательским агентам визуально скрывать находящийся в конце строки символ пробела или табуляции даже если используется свойство pre-wrap, образующее категорию BC, которая запрещает сворачивание пробельного контента. То есть при этом сами символы не удаляются, а лишь скрываются для того, чтобы избежать нежелательных пустот в конце строк.

В рамках определения модели white-space спецификация говорит о том, что абсолютно позиционированные и плавающие элементы не образуют места возможных разрывов строк. Это объясняется тем, что боксы участвующих в этих схемах позиционирования элементов изымаются из нормального потока документа и теряют связь с поточным контентом. Поточный и внепоточные контенты могут накладываться друг на друга, поскольку они отображаются в различных слоях документа.

Обработка управляющих и модифицирующих символов.

И в заключение хотелось бы вкратце отметить два момента. Согласно CSS 2.1 спецификации любые используемые в разметке управляющие символы, отличные от табуляции (U+0009), перевода строки (U+000A), пробела (U+0020) и входящих в набор специальных символов алгоритма обработки двунаправленного текста Unicom bidi (U+202x), обрабатываются как и все остальные символы с последующим их отображением. То есть любые другие, присутствующие в разметке управляющие символы, согласно модели обработки пробельного контента white-space не сворачиваются, а обрабатываются пользовательским агентом соответствующим образом.

Второе касается модифицирующих символов, которые должны рассматриваться пользовательским агентом как часть базового символа, к которому они применяются. Дело в том, что базовый символ и ассоциируемый с ним модифицирующий символ отображаются с помощью одного глифа и в таком случае назначение стилей для базового символа означает их применение к глифу в целом, а не только к базовому символу. То есть, если, к примеру, взять вот эту достаточно редкую конструкцию:

<span style="color:red">У</span><span>&#769;</span>

То согласно требованиям спецификации она должна отображаться таким образом:

У́же

Однако это соблюдается далеко не всеми браузерами. Вот как поступает ваш:

У́же