Posted by: fxix on: Сентябрь 9, 2008
Множество несоответствий стандартам IE при визуализации веб-страниц может быть устранено путем присвоения конкретным элементам свойства layout. Layout является исключительно концепцией IE/Win, которая определяет, как будет ограничено и визуализировано внутреннее содержание элементов, как элементы будут взаимодействовать и влиять друг на друга, реагировать на и передавать события, вызванные пользователем или приложением. Это качество в одностороннем порядке может быть вызвано некоторыми свойствами CSS. Некоторые HTML элементы обладают им по умолчанию. Единственным способом выставить hasLayout = false для любого элемента является удаление или сброс первоначального CSS-свойства, которое вызвало hasLayout = true. В отличие от стандартных свойств или специфических CSS-свойств, доступных в различных браузерах, layout не может быть присвоен напрямую через CSS-объявления. Другими словами, не существует никакого «layout-свойства». Определенные элементы автоматически «имеют layout», и оно может быть незаметно добавлено при различных CSS-объявлениях.
По-видимому, layout есть по умолчанию у следующих элементов:
* <html>, <body>
* <table>, <tr>, <th>, <td>
* <img>
* <hr>
* <input>, <button>, <select>, <textarea>, <fieldset>, <legend>
* <iframe>, <embed>, <object>, <applet>
* <marquee>
Следующие заданные пары CSS-свойств/значений позволяют элементу получить layout:
* position: absolute. Назначается блоку, в него вложенному, что является причиной некоторых проблем.
* float: left|right. У модели «плавающих» элементов присутствует множество хаков (quirks), связанных с некоторыми аспектами layout-элемента.
* display: inline-block. Иногда помогает, когда элемент находится на уровне строки, и ему требуется layout. Добавление layout, по-видимому, единственный реальный эффект этого свойства. Само по себе «поведение в качестве строчного блока» (inline-block) может быть получено в IE, но несколько независимо: IE/Win: строчный блок и hasLayout.
* width: любое значение, отличное от auto. Очень часто используется в качестве неявного исправления, даже чаще, чем hasLayout успевает испортить ситуацию.
* height: любое значение, отличное от auto. height: 1% используется как Holly Hack.
* zoom: любое значение, отличное от normal. Исключительно MS-свойство, не стандартно. zoom: 1 может быть использовано для отладки.
* writing-mode: tb-rl. Исключительно MS-свойство, не стандартно.
В IE7 переполнение (overflow) вызывает также присвоения layout.
* overflow: hidden|scroll|auto Это свойство не применяется в предыдущих версиях, пока layout не будет добавлен к блоку другими методами.
* overflow-x|-y: hidden|scroll|auto Являясь частью модуля блочной модели CSS3, overflow-x и -y пока еще не достаточно широко реализованы. Они не вызывали свойство hasLayout в предыдущих версиях IE.
В IE7 появились новые действующие лица, регулирующие hasLayout. В контексте hasLayout минимальные/максимальные свойства действуют аналогично ширине и высоте, и, по-видимому, так же работает фиксированное и абсолютное позиционирование.
* position: fixed
./.
* min-width: любое значение. Даже выставление значения в 0 присваивает элементу layout.
* max-width: любое значение отличное от ‘none’.
./.
* min-height: любое значение. Даже значение, равное 0, выставляет hasLayout = true.
* max-height: любое значение отличное от ‘none’.
./.
Для линейных (inline) элементов (либо линейных по умолчанию, как span, или при назначении им display: inline)
* width и height вызывают hasLayout в IE5.x и IE6 или более новых только в режиме «quirks». Начиная с IE6, когда браузер находится в режиме «стандартной совместимости», линейные элементы игнорируют свойства width и height, и выставление свойств width или height не заставляет элемент приобрести layout.
* zoom всегда вызывает hasLayout, но оно не поддерживается в IE5.0.
Элементы, у который есть layout и display: inline ведут себя именно так, как предписано это стандартами для линейных блоков (inline-block): они располагаются горизонтально, как слова в строке или абзаце, чувствительны к вертикальному выравниванию и применяют некоторые методы «ужатия» (shrink-wrapping) к собственному содержимому. Как только линейные элементы получают layout, они начинают вести себя как линейные блоки. В этом и заключается объяснение того факта, что в IE/Win линейные элементы могут содержать блочные, и при этом возникает меньше проблем, чем в других браузерах, в которых элементы с display: inline остаются линейными.
Сбрасываем hasLayout
Если выставить следующие свойства в их значения по умолчанию с помощью отдельного правила, это сбросит (или отменит) hasLayout, если не останется других свойств, его выставляющих:
* width, height (в auto);
* max-width, max-height (в none) (в IE7);
* position (в static);
* float (в none);
* overflow (в visible) (в IE7);
* zoom (в normal);
* writing-mode (из tb-rl в lr-tb).
Разработчикам стоит с осмотрительностью использовать сброс этих свойств. Рассмотрим какое-нибудь меню: изменение статуса hasLayout у a:hover, умышленно или нет, приведет к неожиданному отображению (или нестабильности IE6 при совместном динамическом использовании с position: relative).
Отличается и свойство display: если inline-block выставляет haslayout = true, этот флаг позже не сбрасывается в false при переписывании этого значения с помощью block или inline в другом наборе правил.
Выставление свойств min-width, min-height в их значение по умолчанию, которое равно 0, по-прежнему присваивает элементу hasLayout, но IE7 воспринимает для таких целей нестандартное значение auto, которое и сбрасывает hasLayout.
Скриптовое свойство hasLayout
Мы предпочитаем называть hasLayout «скриптовым свойством», чтобы отличать его от свойств CSS, с которыми мы имеем дело. Нет никакого способа напрямую выставить или сбросить это скриптовое свойство hasLayout. Свойство hasLayout может быть использовано для того, чтобы проверить, есть ли у элемента layout или нет. Например, если у него id равно eid, то простая запись в адресной строке IE5.5+ выведет его состояние: javascript: alert(eid.currentStyle.hasLayout).
CSS-хаки
Следующие хаки устанавливают свойство hasLayout, они были проверены в IE7 и более ранних версиях.
John Gallant и Holly Bergevin опубликовали Holly hack:
/* \*/
* html .gainlayout { height: 1%; }
/* */
* Обеспечивает layout в IE5–6 для любого элемента, за исключением линейных в IE6 в стандартном режиме.
* Работает, в общем случае, хорошо за исключением тех случаев, когда height:0 или 1px является более стабильным решением.
* Не совместим с overflow: hidden, кроме IE6 в стандартном режиме (в нем height: 1% приводится к height: auto, если у родительского элемента не задана высота).
* Не имеет никакого эффекта в IE7 в стандартном режиме, в нем * html не соответствует никакому элементу.
Чтобы назначить layout в IE6 и более ранних версиях, мы можем использовать хак с подчеркиванием:
.gainlayout { _height: 0; }
И чтобы назначить layout для IE7, можно воспользоваться свойством min-height:
.gainlayout { min-height: 0; }
Альтернативным путем, и, возможно, выдержавшим проверку временем, является использованием условных комментариев:
<!–[if lte IE 6]><style>.gainlayout { height: 1px; }</style><![endif]–>
<!–[if IE 7]><style>.gainlayout { zoom: 1; }</style><![endif]–>
Еще одним безопасным и изящным решением будет использование внешней таблицы стилей, вызываемой из условных комментариев, для каких бы то ни было исправлений, в которых нуждается IE-Win:
<link rel=»stylesheet» href=»allbrowsers.css» type=»text/css» />
<!–[if lte IE 7]><link rel=»stylesheet» href=»iefix.css» type=»text/css» /><![endif]–>
Для IE6 и более ранних версий стоит всегда использовать height (если мы хотим обеспечить поддержку IE5.0, у нас будет не такой большой выбор), хотя это свойство и вызывает конфликты с чем-то другим (overflow: hidden). Что касается значения, то 1%, 1px, 0 будет более или менее равноправны, но 1% может (хотя и очень редко) вызывать некоторые проблемы.
height не может использоваться для линейных элементов в стандартном режиме, и этого свойства стоит избегать в IE7 (или использовать с крайней осторожностью: только значения в процентах, и только при отсутствии у родительского элемента заданной высоты). В таких случаях рекомендуется использовать display: inline-block или zoom: 1.
Мы наблюдали некоторое количество безуспешных попыток применить так называемые «Holy hacks» к плавающим элементам или к элементам с заданной шириной. Запомните: целью данного хака является не задание элементу высоты, но вызов hasLayout = true.
Не присваивайте layout всему подряд: * {_height: 1px;}. В таких количествах layout вместо лечебного воздействия способен убить вашу верстку на корню.
Управление хаками
Как показал релиз IE7, невозможно предсказать, будут ли следующие версии IE по-прежнему нуждаться в hasLayout для исправления некоторых ошибок и как они будут реагировать на фильтры, которые используются на текущий момент. В этом свете рекомендуется использовать исключительно MS-свойство zoom и(ли) условные комментарии.
<!–[if lt IE 7]><style>/* style for IE6 + IE5.5 + IE5.0 */.gainlayout { height: 0; }</style><![endif]–>
<!–[if IE 7]><style>.gainlayout { zoom: 1; }</style><![endif]–>
* zoom: 1; присваивает layout в IE5.5+ любому элементу (включая линейные), но не имеет никакого эффекта в IE5.0
* Не известно побочных эффектов (хотя линейные элементы ведут себя как линейные блоки).
* Если требуется валидность страницы, zoom должен быть спрятан внутри условных комментариев.
Наша интерпретация является не более, чем попыткой объяснить, почему дело обстоит именно так в известных случаях, и ее следует использовать в качестве руководства для случаев, которые еще не полностью изучены. Попытка разгадать, что находится внутри черного ящика, путем исследования его поведения в некоторых тестовых случаях обречена на неудачу. Также вопрос «Почему?» не имеет ответа. Мы пытается понять внутренний механизм, благодаря которому работает вся модель hasLayout, и как она влияет на отображение веб-документов. За пределами понимания этого механизма возможно разработать только руководства к действию (и только руководства, а не универсальные решения). Мы думаем, что речь идет о небольшом выделенном «окне». Содержание элемента с layout может быть полностью независимо от всего, что вовне. А также оно не может влиять на внешние элементы. MS-свойство hasLayout является в какой-то мере флагом: когда он выставлен, у элемента есть его «layout-качество», которое поддерживает некоторые специальные возможности: (например, обеспечивающие обработку «плавающих» блоков и стека) как самого элемента, так и вложенных в него элементов без layout-свойства. Большая независимость layout-элементов обуславливает то, что в большинстве случаев они ведут себя более стабильно, и исчезают некоторые ошибки в отображении. Ценой такого поведения может быть как значительное отклонение от стандартов, так и дальнейшие ошибки/проблемы в отображении границ элементов. Модель «MS-страницы», в терминах семиотики (научной дисциплины о знаках и знаковых системах), можно представить как набор маленьких несвязанных блоков текста, тогда как HTML- и W3C-модель подразумевает «страницу» как связанные последовательные текстовые блоки. «Плавающие» блоки автоматически содержат layout-элементы. Это одна из причин, из-за которых большинство новичков приходят в крайнее недоумение, обнаружив в браузерах, поддерживающих стандарты, что на странице, сверстанной под IE, «плавающие» блоки выпирают из родительского контейнера, если не убрано дальнейшее обтекание. В IE «плавающий» блок всегда «принадлежит» его родительскому контейнеру с layout. При этом последующие элементы учитывают только этот родительский блок, а не вложенный в него «плавающий». Тем самым, поведение IE6, когда он расширяет контейнер, чтобы в него поместился другой, более широкий элемент («несанкционированное расширение»), можно рассматривать как аспект правила «определения по ограничивающему прямоугольнику». Но ситуация еще хуже со свойством clear, которое не влияет на «плавающие» блоки вне контейнера с layout, в котором находится элемент с таким свойством. «Плавающая» разметка, которая использует эту ошибку в IE, не может быть перенесена в другие браузеры без полного изменения самой верстки. Свойство IE автоматически заполнять «плавающие» блоки, которого иногда нельзя избежать, может быть смоделировано и в других браузерах
Элементы, следующие за «плавающими»
Когда блок следует за «плавающим» элементом, он должен, являясь блочным элементом, игнорировать обтекание, но его содержимое должно быть вытеснено этим «плавающим» блоком: текст в блочном элементе, следующим за элементом, для которого задано float:left, будет располагаться справа от этого «плавающего» блока и затем (если текста больше, чем высота у «плавающего» блока) продолжится ниже его. Но если у блока с текстом есть layout, скажем, у него задана ширина по некоторым причинам, весь элемент будет вытеснен обтеканием, как если бы он сам был «плавающим», а текст в нем больше не будет обтекать «плавающий» блок (он весь разместится справа в виде прямоугольника). Ширина блока в процентах в IE5 вычисляется, исходя из доступного места сбоку от «плавающего» элемента, а в IE6 на основе всей доступной ширины родительского элемента. Поэтому в IE6 width: 100% выливается в некоторые нестыковки по ширине «плавающего» блока со всеми остальными (и в массу других проблем, которые из этих нестыковок могут следовать).
Тесты для случаев, когда hasLayout-блоки расположены рядом с «плавающими»:
* используя width
* используя min-width (IE7) и zoom (IE6)
Похожим образом ведут себя и элементы с относительным позиционированием (position:relative), которые следуют за «плавающими». По идее, они должны иметь смещение относительно «угла отступа» (padding edge) родительского элемента (например, при left: 0 такой элемент должен располагаться сверху «плавающего» элемента (float:left), за которым следует). В IE6 смещение left: значение; начинается от правого внешнего угла «плавающего» элемента, вызывая неверное расположение общей внешней ширины для «плавающего» блока (решением может быть использование margin-left, но возможно некоторые «неувязки» с процентными значениями).
Списки
Списки реагируют на layout, переданный как самим списками (ol, ul), так и пунктам списка (li).
Разные версии IE ведут себя по-разному. Наиболее характерными проявлениями являются маркеры списка (списки с полностью измененным дизайном, где маркеры не требуются, лишены этих проблем). Маркеры, по видимому, создаются некими внутренними элементами, которые «присоединяются» к пунктам списка (обычно прикрепляются снаружи) и являются очень не стабильными. К несчастью, так как они являются внутренними, нет никакой возможности скорректировать их неправильное поведение.
Наиболее распространенные эффекты:
* Если списку присвоен layout, то маркеры исчезают либо начинают вести себя непредсказуемо или ошибочно.
Иногда это можно исправить, добавив полей к элементам списка. Это выглядит как следствие того, что layout-элемент стремится обрезать содержание вложенных элементов, выходящих за его пределы.
* Если пункту списка присвоен layout, это приводит ко всем описанным выше проблемам и некоторым дополнительным (увеличение расстояния по вертикали между пунктами списка).
Следующей проблемой является то, что (в нумерованных списках) у любого элемента с layout, по-видимому, присутствует свой собственный счетчик. Возьмем, например, нумерованный список с пятью элементами, где только у третьего есть layout. Мы получим примерно следующее:
1… 2… 1… 4… 5…
Далее, если элемент списка с layout состоит из нескольких строк, то маркер будет выровнен по нижнему краю (а не по верхнему, как ожидается). Некоторые из описанных проблем могут быть устранены следующим образом. Например, если так необходимо иметь маркеры, то лучше избегать присвоения layout спискам и пунктам в них. Если требуется задать какие-либо размеры, то их можно применить к другим элементам: например, ширину можно назначить внешнему контейнеру, а высоту — каждому пункту списка.
Другая распространенная проблема со списками в IE возникает, когда содержание любого li является якорем с display: block. В этом случае пустое пространство между элементами списка не игнорируется и обычно показывается как дополнительная строка для каждого li. Одним из методом во избежание дополнительного пространства по вертикали является присвоение layout этим блочным якорям. Дополнительным преимуществом этого метода является создание всей прямоугольной области таких блоков кликабельной.
Таблицы.
У таблицы всегда есть layout, они всегда ведут себя как объекты с заданной шириной. В IE6 table-layout: fixed обычно равносильно таблице шириной в 100% со всеми проблемами, которые это влечет (неверные вычисления процентных значений). В качестве небольшого отклонения, можно рассмотреть пару примеров описанного поведения в IE5.5 и «режиме обратной совместимости» в IE6.
Элементы с относительным позиционированием
Заметьте, что position: relative не вызывает hasLayout, и это ведет к некоторым ошибкам отображения, в основном, к исчезновению или неправильному размещению элементов. Несоответствия могут быть замечены при обновлении страницы, изменении размеров окна, прокрутке и выборе или выделении элементов. Выставляя это свойство, IE смещает элемент, но, по-видимому, «забывает» послать сигнал «перерисовать себя» дочерним элементам из того же layout (элемент с layout отправляет такой сигнал совершенно нормально).
* Относительное позиционирование и исчезновение «плавающих» потомков,
* неожиданное исчезновение фона у списка описывают эти эффекты.
Возьмите себе за правило никогда не позиционировать элементы относительно без присвоения им layout. Также может потребоваться проверить, нужен ли еще и родительскому элементу такой конструкции layout и(ли) position: relative, это становится действительно необходимым при работе с «плавающими» блоками.
Элементы с абсолютным позиционированием: Принадлежат блоку — какому блоку?
Важно хорошо понимать CSS-концепцию блока-контейнера, который отвечает за элемент с абсолютным позиционированием (АП), за его смещение и в зависимости от чего считать его размеры в процентах. Для АП элементов таким блоком является ближайший родительский элемент, у которого выставлено позиционирование (position). Если таких элементов найти не удается, то используется первичный контейнер для html. Обычно такой контейнер выставляется путем при назначении элементу position: relative. АП элементы могут вычислять свои размеры и точки отсчета независимо от основного потока документа. Например, их внедряют в код, чтобы реализовать такой принцип, как «содержание — вперед» для повышения доступности страницы или чтобы облегчить себе жизнь при верстке сложных резиновых макетов. Эта концепция подвергнута некоторой модификации в IE: смещение АП элементов могут быть вычислены правильно только при наличии у их контейнера layout, в противном случае ширина АП элементов, выраженная в процентах, может относиться к неверному родительского элементу. В этом случае IE5 и IE6 ведут себя по-разному, но в обоих браузерах наблюдаются проблемы. У IE7b2 более устойчивое поведение, но все равно в некоторых случаях ошибочное. По возможности, пытайтесь назначить layout родительскому блоку для АП элемента (т.е. чтобы не было никаких других блоков между этими двумя). Если мы хотим корректно работать со смещениями элемента, для родительского не-layout блока которого выставлено относительное позиционирование, мы должны присвоить этому родительскому блоку layout.
Фильтры
Фильтры, являясь исключительно MS-свойствами, применяются только к элементам с layout. Это отражает их собственный поток обработки документа.
Изменение потока отображения
После визуализации всех элементов IE может изменить поток отображения для блока, содержащего layout, при событии a:hover (например, изменение background для ссылки). Иногда элементы перемещаются из-за того, что в тот момент, когда произошло это событие, для всех элементов в IE уже известны их размеры и смещения. При первой же загрузке страницы она отображается постепенно, и часть данных не известна (в том числе, может быть не известна ширина из-за эффекта «несанкционированного расширения»). Это может привести к неожиданным перемещениям элемента при наведении (hover).
* «Прыжки» при :hover
* Ошибочные проценты при изменении основного потока отображения
Эти и другие, подобным ошибки возникают из-за неверного изменения потока отображения и являются серьезной проблемой при верстке резиновых макетов: для этих макетов очень часто используются процентные значения для полей и отступов.
Происхождение фона
MS-свойство hasLayout влияет на расширяемость и позиционирование фона (background). Например, по CSS-спецификации background-position: 0 0 должно соответствовать «углу отступа» (padding edge) элемента. В IE/Win это соответствует «углу границы» (border edge) при hasLayout = false и «углу отступа» при hasLayout = true:
* Фон, граница, hasLayout
Схлопывание» полей.
MS-свойство hasLayout также влияет на «схлопывание» полей между блоком и его блочными потомками. По спецификации верхнее поле блока при отсутствии у него верхнего отступа и верхней границы должно «схлопнуться» с верхнем полем его первого блочного потомка в стандартном потоке:
* «Схлопывание» полей
* Отсутствие «схлопывания» полей
В IE/Win этого не происходит, если у блока есть layout: по-видимому, он запрещает дочерним полям выходить за пределы родительского контейнера.
Последовательность мыслей должна быть при виде глюка IE:
1) проверить Ява-скриптом значение Layout у проблемного Div-а и близ лежащих. Применить последовательно правила, включающие hasLayout, на глючащий элемент. В 9 случаях из 10 этого хватает для укрощения ИЕ. Через JS проверять излишне.
2) пытаться все переключить на hasLayout=false при помощи трюков типа zoom:1. Т.о. наша цель включить hasLayout (true) для глючащего элемента.
Если же глюк есть у блока, в то время как hasLayout=true, значит не в нем причина.