Загадочное свойство hasLayout

Влад Мержевич

Для отображения элементов и учёта их взаимодействия между собой, разработчики IE внедрили в этот браузер уникальное свойство hasLayout, значением которого выступает true или false. «Установить hasLayout» означает задать ему значение true, а «убрать hasLayout» говорит о том, что это свойство не задано или у него значение false.

Большинство имеющихся ошибок в IE6, IE7 связано, так или иначе, именно со свойством hasLayout. В IE8 оно было снято, что с одной стороны хорошо, но с другой повлияло на то, что IE разных версий отображают одну и ту же страницу совершенно по-своему. Поэтому важно понимать, как работает это свойство и какую роль выполняет.

Свойство hasLayout оказывает следующее воздействие на элементы веб-страницы.

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

Это не весь список особенностей и неприятностей hasLayout, подробнее об этом ниже.

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

Элементы, у которых всегда установлено свойство hasLayout:

  • изображения (тег <img>);
  • таблицы (<table>), их строки (<tr>) и ячейки (<td>, <th>);
  • линии (<hr>);
  • структурные элементы (<html>, <body>);
  • фреймы (<frameset>, <frame>, <iframe>);
  • некоторые элементы форм (<button>, <fieldset>, <input>, <legend>, <select>, <textarea>);
  • объекты (<embed>, <object>) и апплеты (<applet>);
  • тег <marquee>.

hasLayout устанавливается автоматически, если для элемента задано одно из следующих свойств и значений:

  • display: inline-block;
  • position: absolute;
  • float: left | right;
  • width: любое значение кроме auto;
  • height: любое значение кроме auto;
  • writing-mode: tb-rl
  • zoom: 1.

С первыми стилевыми свойствами в этом списке мы уже знакомились, поэтому подробнее остановимся на двух последних: writing-mode и zoom.

writing-mode устанавливает направление текста на странице, его значение tb-rl располагает текст вертикально. Это свойство добавлено в CSS3 и пока поддерживается только браузером IE. Из-за того, что оно влияет на отображение текста его использование нельзя назвать универсальным.

Свойство zoom изменяет масштаб объекта согласно заданному значению. За нормальный масштаб принимается 1, значения больше 1.0 увеличивают масштаб объекта, значения меньше 1.0 уменьшают его масштаб. Использование zoom: 1 не приведёт к каким-либо изменениям, потому как говорит, что оставить элемент исходным. Однако это свойство не является стандартным и его добавление приведёт к невалидному коду CSS. Поэтому, если его и включать в свой стиль, то использовать для этого условные комментарии.

В IE7 кроме перечисленных свойств hasLayout устанавливают следующие:

  • position: fixed;
  • overflow: hidden | scroll | auto;
  • overflow-x: hidden | scroll | auto;
  • overflow-y: hidden | scroll | auto;
  • min-width: любое значение кроме auto;
  • max-width: любое значение кроме auto;
  • min-height: любое значение кроме auto;
  • max-height: любое значение кроме auto.

Убрать hasLayout можно добавлением к элементу одного из следующих свойств и значений:

  • position: static;
  • float: none
  • width: auto;
  • height: auto;
  • overflow: visible;
  • writing-mode: lr-tb | rl-tb | bt-rl;
  • zoom: normal.

Проблемы с hasLayout

Большинство проблем связанных с этим свойством можно исправить его установкой, добавляя в стилях элементу zoom: 1 или height: 1%.

Хаотичные границы

Границы вокруг некоторых элементов отображаются некорректно, в тех местах, где их не должно быть или отсутствуют в нужных. В примере 4.3 приведены три <div> с границами разных цветов.

Пример 4.3. Хаотичные границы

XHTML 1.0CSS 2.1IECrOpSaFx

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>Хаотичные границы</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <style type="text/css">
   #wrapper { border: 2px solid gray; }
   #one { border: 2px solid red; }
   #two { border: 2px solid blue; margin-top: -2px; }
  </style>
 </head>
 <body>
  <div id="wrapper">
   <div id="one">Первый</div>
   <div>Второй</div>
   <div id="two">Третий</div>
  </div>
 </body>
</html>

Результат примера в IE6 показан на рис. 4.11. Серая и синяя граница отображаются неправильно.

Некорректное отображение границ в IE6

Рис. 4.11. Некорректное отображение границ в IE6

Для исправления ошибки достаточно установить hasLayout для родительского элемента wrapper.

<!--[if IE 6]>
 <style type="text/css">
  #wrapper {
   zoom: 1; /* Устанавливаем hasLayout */
  }
 </style>
<![endif]-->

Результат показан на рис. 4.12.

Правильное отображение границ

Рис. 4.12. Правильное отображение границ

Отмена обтекания

Элементы с hasLayout отменяют действие обтекания вокруг плавающих элементов. В примере 4.4 (рис. 4.13) добавляется картинка с обтеканием её текстом. Однако в IE6–7 никакого обтекания не наблюдается.

Нет обтекания вокруг картинки

Рис. 4.13. Нет обтекания вокруг картинки

Пример 4.4. Отмена обтекания

XHTML 1.0CSS 2.1IE 7IE 8+CrOpSaFx

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>Обтекание</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <style type="text/css">
   p { border: 3px solid #000; padding: 5px; }
   #floated { float: left; width: 100px; margin: 0 10px 10px 0; }
   #text { width: 300px; }
  </style>
 </head>
 <body>
  <p id="floated"><img src="images/figure.jpg" alt="" /></p>
  <p id="text">Винни-Пух был всегда не прочь немного подкрепиться, в
   особенности часов в одиннадцать утра, потому что в это время
   завтрак уже давно окончился, а обед еще и не думал начинаться.
   И, конечно, он страшно обрадовался, увидев, что Кролик достает
   чашки и тарелки.</p>
  </body>
</html>

Для сравнения этот же пример в других браузерах, в частности, в Firefox, выглядит иначе (рис. 4.14).

Рис. 4.14. Результат обтекания в Firefox

Здесь уже не поможет никакой zoom, потому что hasLayout и так установлено и от него наоборот, необходимо избавиться. Для этого убираем свойство width у слоя text, а саму ширину ограничиваем контейнером wrapper.

<div id="wrapper">
  <p id="floated"><img src="images/figure.jpg" alt="" /></p>
  <p id="text">...</p>
</div>

Стиль поменяется незначительно.

p { border: 3px solid #000; padding: 5px; }
#floated { float: left; width: 100px; margin: 0 10px 10px 0; }
#wrapper { width: 300px; }

Отступы в списке

Если в списке содержатся строчные элементы, для которых установлено displayblock, то в IE6 между пунктами списка появляется небольшой промежуток (рис. 4.15).

Промежуток между пунктами списка в IE6

Рис. 4.15. Промежуток между пунктами списка в IE6

Код вызывающий ошибку отображения продемонстрирован в примере 4.5.

Пример 4.5. Отступы в списке

XHTML 1.0CSS 2.1IECrOpSaFx

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>Отступы в списке</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <style type="text/css">
   SPAN { display: block; background: #333;color: #fff; }
  </style>
 </head>
 <body>
  <ul>
   <li><span>Первый</span></li>
   <li><span>Второй</span></li>
   <li><span>Третий</span></li>
  </ul>
 </body>
</html>

Проблема решается установкой hasLayout тегу <span>, например, через zoom.

<!--[if IE 6]>
 <style type="text/css">
  SPAN { zoom: 1; }
 </style>
<![endif]-->

Подобная же неприятность возникает и в IE7, если для тега <li> задана фиксированная ширина. Стоит включить в стили width и результат будет похож на рис. 4.15, разве что отступы несколько меньше.

LI { width: 200px; }

В подобном случае опять же помогает установка zoom для <span>.

Высота слоя

Браузер IE6 некорректно обрабатывает высоту слоя, когда контент превышает заданное значение. Высота увеличивается автоматически, подстраиваясь под содержание, в то время как по спецификации она должна оставаться неизменной. В примере 4.6 показан код приводящий к неверному отображению.

Пример 4.6. Некорректная высота слоя

XHTML 1.0CSS 2.1IECrOpSaFx

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>Высота слоя</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <style type="text/css">
   DIV { height: 30px; background: #fc0; }
  </style>
 </head>
 <body>
  <div>Лев ревет только в том случае, когда сообщает, что территория<br /> 
  принадлежит ему или провозглашает себя царем природы.</div>
</body>
</html>

Исправить ошибку можно убрав свойство height, но если оно требуется, тогда следует ограничить контент через overflow со значением hidden.

Блочные ссылки

Блочные ссылки это приём, который активно используется в различных меню, таким способом повышается полезная площадь ссылки. В IE6 если ссылка установлена блочной с помощью display: block она не занимает доступное пространство родителя и ссылкой является только текст. Эта ошибка проявляется в том случае, когда к родителю добавляется свойство float (пример 4.7).

Пример 4.7. Блочные ссылки

XHTML 1.0CSS 2.1IECrOpSaFx

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <title>Блочные ссылки</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <style type="text/css">
    DIV { 
     background: #ffc; padding: 1%; text-align: center; 
     float: left; width: 98%;
    }
    A { display: block; } 
   </style>
  </head>
 <body>
  <div><a href="#">Ссылка на всю ширину</a></div>
 </body>
</html>

Проблема решается установкой hasLayout блочным тегам <a>, например, через zoom.

<!--[if IE 6]>
 <style type="text/css">
  DIV A { zoom: 1; }
 </style>
<![endif]-->

Пустой тег

Пустым считается тег, внутри которого нет текста, включая спецсимволы. IE6–7 устанавливает высоту пустых тегов, для которых задано hasLayout. Варианты пустых тегов.

<p></p>
<div> </div>
<div><span></span></div>

Варианты непустых тегов.

<p>Текст</p>
<div>&nbsp;</div>
<div><span>1</span></div>

В примере 4.8 для пустого тега <div> указывается цвет фона и ширина, которая устанавливает hasLayout.

Пример 4.8. Пустой тег

XHTML 1.0CSS 2.1IE 7IE 8+CrOpSaFx

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Пустой тег</title>
  <style type="text/css">
   DIV { background: #fc0; width: 100%; }
  </style>
 </head>
 <body>
  <div></div>
 </body>
</html>

В данном примере будет виден фон в IE6–7, в то время как другие браузеры устанавливают высоту пустого тега нулевой и, соответственно, не отображают фон.

Для исправления ошибки в IE6 следует установить для пустого тега overflowhidden, а для IE7 нулевую высоту.

div {
  overflow: hidden; /* Для  IE6 */
  height: 0; /* Для IE7 */
}

Ошибка с маркерами

Если для списка <ul> или <ol> установлен hasLayout, то пропадают маркеры (пример 4.9).

Пример 4.9. Не отображаются маркеры

XHTML 1.0CSS 2.1IE 7IE 8+CrOpSaFx

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Список</title>
  <style type="text/css">
   UL {
    width: 300px; /* Устанавливаем hasLayout */
   }
  </style>
 </head>
 <body>
  <ul>
   <li>Земля</li><li>Огонь</li><li>Вода</li><li>Воздух</li>
  </ul>
 </body>
</html>

Чтобы исправить эту ошибку необходимо сместить пункты списка вправо.

LI {
  margin-left: 1em;}

Так как это приведёт к сдвигу текста, то вернуть его на исходное место можно через свойство margin-left, задав отрицательное значение для UL.

UL {
  margin-left: -1em;
}

Ошибка с отрицательным отступом

Часть элемента пропадает за пределами родителя, когда используется отрицательный отступ. В примере 4.10 внутри слоя t1 находится слой t2, у которого задан отрицательный верхний отступ. Из-за ошибки в IE6 всё, что оказывается за границей слоя t1, исчезает.

Пример 4.10. Отрицательный отступ

XHTML 1.0CSS 2.1IECrOpSaFx

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" 
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Отрицательный отступ</title>
  <style type="text/css">
   .t1 {
    background: #fc0; padding: 10px;
   }
   .t2 { 
    border: 1px solid #000; background: #fff; padding: 5px; 
    margin-top: -20px; /* Отступ сверху */
   }
  </style>
 </head>
 <body>
  <div class="t1">
    <div class="t2">Текст</div>
  </div>
 </body>
</html>

Результат примера в IE6 показан на рис. 4.16.

Часть слоя пропадает

Рис. 4.16. Часть слоя пропадает

Для устранения ошибки для внутреннего элемента достаточно установить hasLayout, например, задав height: 1%.

.t2 {
  height: 1%;
}

Полезные ссылки

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

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