Обзор HasLayout

Маркус Мильке

Оригинал: http://msdn.microsoft.com/en-us/library/bb250481(VS.85).aspx

Перевод: Влад Мержевич

Что ещё за HasLayout и почему он так важен?

Некоторые ошибки в Internet Explorer можно обойти, установив для элемента «лейаут». Большинство пользователей не подозревают о значениях «лейаута» применяемого к элементу. Данный документ объясняет, что происходит, когда элемент содержит «лейаут» и последствия этого.

Прим. переводчика

Английское слово layout можно перевести как «макет», но в данном контексте оно обозначает внутреннюю структуру данных IE. Чтобы не было путаницы с терминологией, в статье layout указывается как «лейаут».

Начнем с того, что есть два набора элементов.

  • Элементы, которые полагаются на родителя для определения размеров и упорядочивания их содержания.
  • Элементы, которые отвечают за размеры и упорядочивание собственного содержания.

В основном, элементы в Internet Explorer не отвечают за организацию самих себя. Тег <div> или <p> может иметь положение, как в исходном коде и потоке документа, но их контент упорядочивает ближайший предок с лейаутом (обычно <body>). Такие элементы полагаются на предка, чтобы он сделал для них всю тяжелую часть по определению размера и единиц измерения.

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

Итак, что означает «имеет лейаут»?

  • Если коротко, включение лейаута означает, что элемент отвечает за размеры и позиционирование самого себя и возможно любых дочерних элементов (если дочерний элемент имеет собственный лейаут, то он определяет размеры).
  • Некоторые «фиксированные» элементы, иными словами, имеющие ограничения размера всегда имеют лейаут. Например, кнопки, изображения, поля форм, теги <select> и <marquee> всегда исходного размера, если ширина и высота явно не указана.
  • Иногда элементы, которым обычно не требуется информация о лейаут, вроде <div> или <span>, могут иметь специфичные свойства, устанавливающие лейаут для применения некоторых параметров — к примеру, элемент должен иметь лейаут, чтобы получить полосы прокрутки.
  • Как только лейаут применяется, устанавливается флаг hasLayout. Это свойство возвращает true при обращении к нему.

Почему лейаут так важен?

  • Он ограничивает элемент прямоугольной формой. Это означает, что содержимое элемента не может проходить вокруг других блоков. К примеру, плавающие элементы не могут посягать на границы элементов с лейаутом.
  • Для элементов с лейаутом можно установить новый контекст форматирования блока (см. п. 9.4.1 спецификации CSS 2.1).
  • Поскольку лейауты это дополнительные объекты с информацией о кэшировании, а также принимают участие в алгоритме вычисления размеров и позиционирования, содержание лейаута обходится недёшево — это приводит к увеличению использования памяти и может привести к снижению производительности.
  • Есть также побочный эффект авторазмера — элемент с лейаутом нельзя уменьшить до размеров дочернего элемента. К примеру, абсолютно позиционированный блок вокруг элемента с лейаутом не получится сделать меньше дочернего элемента.
  • Прямоугольник лейаута подгоняется под размер контента (баг с высотой в IE6).
  • Многие люди используют лейаут для обхода багов IE, в частности с относительным позиционированием элементов. Однако относительно позиционированным элементам не нужен лейаут, в этом случае побочные эффекты могут привести к появлению проблем.

Какие элементы всегда имеют лейаут?

  • Изображения.
  • Таблицы, их строки (<tr>) и ячейки (<td> и <th>).
  • <hr>.
  • Элементы форм: текстовые поля, кнопка, поле с файлом, <select>.
  • <marquee>.
  • <frameset>, <frame>.
  • <object>, <applet>, <embed>.
  • Абсолютно позиционированные элементы.
  • Плавающие элементы.
  • Строчно-блочные элементы.
  • Фильтры (rotation, dropshadow и др.).
  • <body> (а также <html> в строгом режиме).

Какие элементы могут получить лейаут?

  • Блочные элементы с width или height в строгом режиме.
  • Любой элемент с заданным width или height в режиме совместимости.
  • Элемент, у которого установлено свойство zoom.
  • Элементы в режиме редактирования.
  • Элементы, которые принимают поведение viewlink.
  • У которых порядок вывода отличается от родительского (справа налево вместо слева направо).

Значение HasLayout

По этой ссылке содержится список известных обходных путей, которые устанавливают лейаут без визуальных изменений. Если вы видели или использовали их, знайте и о последствиях.

Сценарий 1. Обтекание

Давайте рассмотрим следующий код

<div style="float:left; border: 2px solid red"> 123</div>
<span style="border: 2px solid blue">
Быстрая лиса перепрыгнула через ленивую собаку.
Быстрая лиса перепрыгнула через ленивую собаку.
Быстрая лиса перепрыгнула через ленивую собаку.
Быстрая лиса перепрыгнула через ленивую собаку.
Быстрая лиса перепрыгнула через ленивую собаку.
</span>

Чего мы ожидаем?

  • Текст красиво обтекает вокруг левого плавающего <div>, как и ожидалось.
  • Граница демонстрирует схему блоков вокруг <span>.

Что произойдёт, когда мы применим лейаут к <span> с текстом?

<style>
.gainlayout {zoom: 1;}
</style>
...
<span class="gainlayout" style="border: 2px solid blue">

  • Применение лейаута устанавливает <span> как прямоугольный блок.
  • Текст не обтекает больше <div>. Это очень важно, потому что разрушает ожидаемое поведение разметки текста (представьте, что вы применили лейаут к тегу <p>).

Сценарий 2. Авторазмер

<div style="position: absolute; background:red">
<div>
123
</div>
</div>

Чего мы ожидаем?

  • Первый <div> позиционируется абсолютно, связан с блоком контента и выведен из потока.
  • Красный фон располагается вокруг «123», так как он определён родителем абсолютно позиционированного <div>.

Что случится, если применить лейаут к <div> содержащему  «123»?

<div style="position: absolute; background:red">
<div class="gainlayout">
123
</div>
</div>

  • <div> теперь имеет собственный лейаут и полностью игнорирует все «требования» родителя.
  • Мы полностью потеряли поведение «обёртки» инициированного родителем. Это побочный эффект, о котором вы должны знать, применяя лейаут к элементу.

Сценарий 3. position: relative

Давайте теперь посмотрим пример, в котором hasLayout используется, чтобы обойти ошибку.

<div style="position: relative; border: 2px solid blue">
<div style="float:left; border: 2px solid red">
<img style="position: relative; border: 2px solid green; width:100px; height:100px"
src="slider.png">
</div>
</div>

Мы ожидаем, что IE предоставит следующий результат.

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

Должно это выглядеть следующим образом.

 

В IE6 нас ждёт сюрприз.

Что произошло?

  • Изображение, как относительно позиционированный элемент, зависит от родителя, который должен определить, где его расположить.
  • У <div> с float установлено hasLayout.
  • В IE6 код для измерения относительного позиционирования очень хрупок (это баг и он пофиксен в IE7) и родитель совершает ошибку при измерении сдвига изображения.

Традиционно, попытаемся решить эту проблему установкой лейаута.

<style>
.gainlayout {zoom: 1;}
</style>
...
<div class="gainlayout" style="position: relative; border: 2px solid blue">

Результат выглядит теперь так.

Хорошо

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

Плохо

Лейаут <div> имеет теперь нежелательные побочные эффекты:

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

В этом сценарии не так много, что вы могли бы сделать (кроме, возможно, удаления ненужных position:relative). Мы не пытаемся препятствовать использованию hasLayout для обхода багов IE6, но просто пытаемся объяснить, как решить проблемы. В идеале, все эти проблемы должны быть решены, если не в IE7, то в будущих версиях.

Резюме

Я попытался объяснить некоторую часть внутренней работы и побочные эффекты hasLayout. Я перечислил, какие элементы имеют встроенный лейаут и когда нормальные элементы могут получить его. Наконец, я обсудил последствия включения лейаута и привел примеры того, что происходит с элементами.

В идеале, пользователь не должен знать об этой функции, поскольку она в основном используется внутри IE для реализации CSS-позиционирования. Однако, она показала себя как «лекарство» для некоторых багов IE. Эта фича не сталкивается со спецификацией CSS, поскольку она предназначена для внутреннего использования для реализации спецификации (правда с ошибками). Имея это в виду, я хотел бы пролить свет на последствия лейаута.

Благодарности

Я хотел бы поблагодарить Web Standards Group (WaSP) за их поддержку и обратную связь по этой статье. В частности, я хотел бы поблагодарить Дина Эдвардса и Криса Уилсона за их рецензию. Благодарю Холли Бергевина, Инго Чао, Бруно Фассино, Джона Галланта, Георга Сортуна и Филиппа Уиттенберга за подробное обсуждение статьи. Она предназначена для расширения усилия сообщества по дополнительной информации и объяснению загадки «hasLayout». Спасибо всем участникам.

Не выкладывайте свой код напрямую в комментариях, он отображается некорректно. Воспользуйтесь сервисом cssdeck.com или jsfiddle.net, сохраните код и в комментариях дайте на него ссылку. Так и результат сразу увидят.