PDA

Просмотр полной версии : Тонкий CSS для Internet Explorer


Regis Filius
10.03.2009, 11:22
Тонкий CSS для Internet Explorer
Автор статьи: Павел Корнилов (http://lusever.ru/proceedings/thin_css/index.html)

IE6 самое популярное и устаревшее приложение на рынке под названием Internet. Это самый замечательный браузер 2000 года. Он не просто так приобрел свою популярность, его было за что любить, и можно постараться полюбить сейчас, когда он слишком медленно идет к 50% рынка.

Доклад рассказывает о решениях в верстке, которые приводят IE в соответствие виденьем w3c стандартов. Я покажу, как делать тонкий css толстым javascript'ом :)

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

Ошибки HTML


Картинки. При отсутствующем аттрибуте title всплывает подсказка, текст которой берется у alt (ie6, ie7);
Label. Тегу label необходим аттрибут for. Даже для такой удобной конструкции <label>Имя: <input/></label> (ie6);
PNG. Не поддерживает прозрачность (ie6).


Ошибки CSS


Наведение (:hover). Работает только для тега a у которого стоит аттрибут href (ie6);
Прозрачность. Своя реализация (ie6, ie7). Некорректно отображает прозрачные элементы без фона;
Наследование. inherit (ie6, ie7);
:first-child, :last-child. Приходится присваивать элементу класс (ie6, ie7).


Решения

Семантически верная верстка диктует свои условия. Удобным инструментом стала связка с Firefox + Firebug + Web Developer Toolbar. Internet Explorer все чаще стал запускаться под эмуляцией на Linux и Mac, это приводит к тому, что под него сайт тестируется в последнюю очередь. Даже windows-разработчикам приходится ставить две версии одновременно — 6 и 7.

А дизайнеры присылают все более сложные макеты страниц, основываясь на новых достижениях браузеров. У них же стоит Safari3 :)

И чем глубже профессионал уходит в верстку, тем больше он спотыкается об Internet Explorer. Появляются необычные, неожиданные, странные решения. Потом ищется какой-нибудь скрипт который решает все их проблемы. Как обычно, универсального решения не находится, и приходится искать другие пути.

Подключаем стили

Так как стили бывают очень сложными, я предпочитаю не смешивать правила для 6 и 7 версий в одном файле, а тем более объединять их с основными стилями документа. Для седьмой версии обычно пишутся 10-20% строк шестой версии. Общие стили для обеих версий подключаются через конструкцию @import. Иногда достаточно, чтобы файл шестой версии просто дополнительно подключал файл от седьмой.

HTML:

<!--[if lte IE 6]><link rel="stylesheet" type="text/css" href="t/css/ie6.css"/><![endif]-->

<!--[if IE 7]><link rel="stylesheet" type="text/css" href="t/css/ie7.css"/><![endif]-->

ie6.css и ie7.css
@import url(ie.css);

DHTML Behaviors

Behaviors разрабатывались для описания специфичной функциональности и поведения элемента. Внутри htc находится полноценный JScript, привязанный к каким-то событиям элемента и документа, например, наведение мышки или окончание загрузки страницы.
Подключается очень простой CSS–конструкцией:
behavior: url(behavior.htc);
Давайте посмотрим на программные решения.

Whatever:hover

http://www.xs4all.nl/%7Epeterned/csshover.html

По-моему, единственное популярное расширение, которое меняет именно поведение. Хорошо лечит свойства :hover и :active.
Чтобы его установить нужно прописать в любом месте стилей:
body { behavior: url(hover.htc) }

Для работы нужна полностью загруженная страница, после чего он начинает парсить доступные стили. Найдя ключевое слово :hover и :active для стиля, он ищет в документе привязанные элементы. Если нашел, то создает события onmouseover и onmousedown, которые добавляют класс onhover.
К сожалению, у него есть минусы.
Тэг A без аттрибута href="" по-прежнему не работает;
При больших размерах документа или файла стилей, браузер может занять много ресурсов компьютера, потому что заново парсит документ;
Отрабатывает только в момент загрузки, для новых элементов, созданных например javascript'ом, требуется новое подключение, или такая запись * { behavior: url("hover.htc"); заметно нагружающая браузер;
С вложенными :hover ведет себя не всегда правильно.

IE PNG Fix

http://www.twinhelix.com/css/iepngfix/

Решает проблему с альфа каналом в png для 5.5 и 6 версий. Подключение:
img, div { behavior: url(iepngfix.htc) }
где div слой с фоном. Реализовано с помощью фильтра AlphaImageLoader. Что-бы решить проблему с некликабельными ссылками, ставит всем детям применяемого элемента style="position: relative". Отлично подходит для небольших изображений в маленьком количестве. На сложных макетах появляются врожденные минусы:
position: relative не помогает, если behavior накладывается на слой с position, отличным от static;
При медленной отдаче картинок сервером, или при их большом размере, пользователю некоторое время видны изображения без применения фильтра;
Показывает в статусной строке, сколько ему ещё осталось обработать изображений, примерно так: «Осталось 15»;
Для работы с картинками нужен прозрачный gif.

Expressions

Для того чтобы оценить плюсы и минусы «выражений» (expression), решения на основе behavior были описаны выше. Специальная конструкция expression по праву считается лекарством от всего. Благодаря ей полноценные JavaScript–выражения можно писать прямо в CSS–файле.

#id {
свойство: expression(javascript код);
}
Свойство может быть существующим или выдуманным. Китайские верстальщики часто используют несуществующее свойство star.
Самое сложное в понимании экспрешенов — это нестандартный синтаксис. Новые команды разделяются не точкой с запятой или переносом строк, а запятыми. Самое логичное, что можно предположить — это функция. Т.е. мы передаем параметры в функцию, и она уже их выполняет.
Есть несколько особенностей у expression:

выполняется постоянно, если не переопределить CSS–свойство (см. «Оптимизация»);
выполняется мгновенно;
ключевое слово this необязательно, выражение this.style равносильно простому style;
работают комментарии /* */, несмотря на то, что мы находимся внутри css;
можно использовать внешние функции или библиотеки, если они объявлены или подключены в html;
пробелы могут вызывать ошибку, хотя такое встречается редко;


Оптимизация

Основа оптимизации — это переопределение CSS–свойств, которые содержат expression. Получить к ним доступ можно с помощью:

style
currentStyle
runtimeStyle

style — это style конкретного html элемента. Свойства из currentStyle мы можем только читать, зато currentStyle содержит все CSS–свойства, даже те, которые не понимает сам браузер. Свойства из runtimeStyle мы можем как читать, так и переопределять, но только те свойства, которые понимает браузер. Стоит заметить, эти свойства относятся к ноду, а не к CSS–правилу. Если мы изменяем runtimeStyle, то изменения касаются конкретного нода.

Приведу пример оптимизации. Допустим, у нас есть кнопки с прозрачностью.
.button1 { opacity: .1 }
.button2 { opacity: .2 }
.button3 { opacity: .3 }
.button4 { opacity: .4 }
Для ie, записи обычно дублируются:
.button1 { filter: alpha(opacity=10) }
.button2 { filter: alpha(opacity=20) }
.button3 { filter: alpha(opacity=30) }
.button4 { filter: alpha(opacity=40) }
Но мы можем применить экспрешен:
.button1, .button2, .button3, .button4
{ filter: expression( 'alpha(opacity='+currentStyle.opacity*100+')' ) }
IE не завис? На четырех кнопках не должен. А вот на десяти и более, он иногда виснет и вылетает. Происходит это потому, что на каждое движение мышки по странице или выполнение JavaScript–кода происходит пересчет expression. Избежать пересчета удастся, просто переопределив фильтр в начале выражения:
.button1, .button2, .button3, .button4
{ filter: expression( runtimeStyle.filter = 'alpha(opacity='+currentStyle.opacity*100+')' ) }

В презентации к докладу, есть более развернутый пример.

Таким образом, наш экспрешен быстро применяется при загрузке страницы и последующем создании новых нодов скриптом.

Такой способ оптимизации подходит только для «статичных» элементов, которым не нужно менять свое отображение динамически. Изменение родительского класса, равнение по высоте окна и эмуляция position: static — все это проблемные участки оптимизации. Лучше их не оптимизировать, а использовать пореже.

Дополнительные материалы

Эти файлы я использую в работе, как шаблоны на многие случаи жизни:
ie6.css (http://lusever.ru/proceedings/thin_css/ie6.css)
ie7.css (http://lusever.ru/proceedings/thin_css/ie7.css)
ie.css (http://lusever.ru/proceedings/thin_css/ie.css)
Презентация доклада. (http://lusever.ru/proceedings/thin_css/thinCssForIe.ppt)

P.S. Ссылки на внешку. Если кого заинтерисует, залью в городской сети. Но там небольшие файлы, можно самому скачать. Информацию поискал в связи с недавним вопросом о узколобости IE )

Googleman
10.03.2009, 20:50
Давай заливай =) Мне как раз прозрачность в пнг пофиксить нада бы.
З.Ы. У меня в ксски для шестого стоит перл вида:
#leftcolumn { margin-bottom: -100%; }

Огромное спасибо!

Regis Filius
11.03.2009, 13:55
Схавать (http://file.ya1.ru/e6fe2ee78781d0b51df1c0d47a4b716f)
Там три css файла, два веб-архива и архив для png.

Hulio
16.03.2009, 12:02
Выложите пожалуйста здесь, внешки нету ((

Googleman
16.03.2009, 18:33
Hulio а чуть выше регис что оставил:

Схавать (http://file.ya1.ru/e6fe2ee78781d0b51df1c0d47a4b716f)
Там три css файла, два веб-архива и архив для png.

rohard
03.10.2009, 17:01
Вставлю пять копеек.

Картинки. При отсутствующем аттрибуте title всплывает подсказка, текст которой берется у alt (ie6, ie7);
1. Если title не нужен, то можно просто добавить title="".
2. Заставить остальные браузеры делать так же :)


var images = document.getElementsByTagName('img');
for (var i = 0; i < images.length; i++) {
if (images.title == ''){
images[i].title = images[i].alt;
}
}

Обернуть в функцию и в onload.

PNG. Не поддерживает прозрачность (ie6).
Помимо behavior'а можно использовать js, что иногда удобнее (плагин к jquery). Однако, все равно используется AlphaImageLoader и 1px прозрачного gif'а.

Наведение (:hover). Работает только для тега a у которого стоит аттрибут href (ie6);
Лечилок очень много, в основном js. Хотя, иногда хватает и правил a:hover span { ... }

Наследование. inherit (ie6, ie7);
Не совсем понял, что хотел сказать автор. Возможно, имелась в виду некорректная обработка class1.class2.class3, когда IE 6 берет во внимание только class3?

:first-child, :last-child. Приходится присваивать элементу класс (ie6, ie7).

Не обязательно использовать классы. Можно в expression проверять соседей по узлу на существование. Свойства previousSibling и nextSibling для first- и last-child соответственно. Что-то типа li {background-color: expression(this.nextSibling == null ? "#eee" : '#ccc') }. Или воспользоваться свойствами firstChild и lastChild (кто бы мог подумать, да, кэп?).

Еще несколько моментов, о которых умолчал автор:

min-max width и height. Разумеется, не воркают.
dotted 1px. Осел не умеет отрисовывть самостоятельно. Выглядит как dashed.
float и margin. У всплывающих блоков ([I]float: left, например) удваивается отступ со стороны выравнивания (в случае с примером, слева).

И менее значимые:

Блочная модель документов без доктайпа ведет себя как в IE5. Например, известная "фича" с шириной, когда считается только "полезное" пространство блока (то есть отступы, поля и бордеры идут лесом).
Verdana и юникод. Для того, что бы работало, нужно лезть в реестр.
Переносы строк считаются нодами. Остальные движки считают их текстом.
Свойства selectionStart, selectionEnd и метод setSelectionRange() не робят. Есть .htc, лечащий сей недостаток.

Et cetra.

min-max width и height.
min-width, imho, самый простой способ - 1px прозрачный GIF растянуть на необходимую ширину и посадить в wrapper.
Или обернуть один div в другой, при этом первый имеет padding-left: min_width_value; а второй margin-left: -min_width_value; Но есть ньюанс ;)


<!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> True min-width </title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- style START -->
<style type="text/css" media="screen">
body, div, p, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre,
form, fieldset, input, textarea, blockquote, fieldset {
margin: 0;
padding: 0;
}
#main {
background: #eee;
padding: 20px;
}
#wrapper {
min-width: 840px;
margin: 0 auto;
padding: 25px 30px 20px 40px;
border: solid 1px;
border-color: #fff #ccc #ccc #fff;
background: #f6f6f6;
}
#container p {
margin-bottom: 1em;
text-align: justify;
text-indent: 2em;
font: 12px "Trebuchet MS";
color: #666;
}
</style>
<!--[if IE 6]>
<style type="text/css" media="screen">
#wrapper {
padding-left: 840px;
}
#heightWrapper {
height: 0;
}
#container {
position: relative;
height: 0;
margin-left: -800px;
}
</style>
<![endif]-->
<!-- style END -->
</head>
<body id="main">
<div id="wrapper">
<div id="heightWrapper">
<div id="container">
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris ullamcorper ultrices ante. Curabitur placerat vestibulum justo a faucibus. Integer et turpis vel velit luctus venenatis eu eget dui. Aliquam tincidunt vestibulum gravida. Morbi vel turpis dolor, at mollis mauris. Vestibulum ut vulputate magna. Mauris condimentum convallis accumsan. Vivamus a neque nulla. Proin luctus felis ac purus eleifend accumsan. Etiam pellentesque pretium blandit. Cras aliquam imperdiet lacus, id placerat est euismod non. Donec vel quam ut mi elementum interdum. Etiam gravida convallis quam, at hendrerit tellus sollicitudin non. Curabitur ac scelerisque neque. Praesent lorem justo, elementum nec tempor vel, placerat at velit.</p>
<p>Integer turpis ante, volutpat et condimentum ac, pharetra sed sem. Vestibulum nec erat odio, accumsan vulputate ipsum. Sed aliquam tempor auctor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Quisque accumsan sodales viverra. Donec tristique sapien at libero tristique varius. Etiam laoreet adipiscing erat et dapibus. Pellentesque vitae diam quis dolor semper venenatis sit amet et urna. Integer feugiat odio sit amet erat tempus non faucibus lectus varius. Nam vitae ipsum nunc, at tristique elit. Donec ut orci et neque euismod vulputate lacinia vel massa. Phasellus accumsan, mauris sed consequat feugiat, dui erat aliquet elit, nec consectetur erat enim vel velit. Suspendisse varius lobortis libero nec egestas. Ut viverra mattis ligula, non commodo lacus congue et. Cras tincidunt mauris et turpis pharetra hendrerit. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean vulputate consectetur ullamcorper.</p>
<p>Maecenas odio nunc, mollis sit amet tempor vel, malesuada eget libero. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Morbi nec eros in velit porta commodo. Integer odio mauris, tincidunt eget congue auctor, euismod non dui. Nulla luctus scelerisque sem, et posuere risus mollis vitae. In viverra libero id velit vehicula a mattis leo placerat. Fusce faucibus libero lectus, nec dignissim urna. Donec tellus neque, lobortis eget mattis id, aliquet nec enim. Ut sed convallis erat. Etiam nec elit ut nisl tincidunt sollicitudin. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nullam euismod ligula quis dui gravida pulvinar. Etiam in libero at massa sollicitudin eleifend. Donec sed erat sit amet erat hendrerit tempor. Aliquam adipiscing massa eu elit blandit dapibus.</p>
</div>
</div>
</div>
</body>
</html>


С min-height проще:


min-height: 400px;
height: auto !important;
height: 400px;

Ессно, можно воспользоваться expression или js (если есть проблемы с зависанием). Например:

width: expression(((document.documentElement.clientWidth || ? document.body.clientWidth) < 990) ? "990px" : ? ((document.body.clientWidth > 1280) ? "1280px" : "100%"))}

dotted 1px.
Интересно, что IE 6 спокойно рисует dotted 2px, но не умеет 1px. Лечится довольно интересно: интересуемы блок имеет zoom: 2, текст в блоке оборачиванием в еще один блок с zoom, но уже в 0.5. Тоже есть ньюансы... ;)


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> for IE 6 </title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<!-- style START -->
<style type="text/css" media="screen">
body, div, p, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre,
form, fieldset, input, textarea, blockquote, fieldset {
margin: 0;
padding: 0;
}
.dotted {
clear: left;
float: left;
width: 260px;
margin: 20px 0 0 20px;
padding: 10px;
border: dotted 1px;
text-align: justify;
text-indent: 2em;
font: 11px "Trebuchet MS";
}
.red {
border-color: #c99;
background: #fee;
color: #966;
}
.blue {
border-color: #99c;
background: #eef;
color: #669;
}
</style>
<!--[if IE 6]>
<style type="text/css" media="screen">
.forIE {
display: inline;
width: 520px;
padding: 20px;
border: dotted 2px;
zoom: 0.5;
}
.intoDotted {zoom: 2}
</style>
<![endif]-->
<!-- style END -->
</head>
<body id="main">
<div id="wrapper">
<div class="dotted red">
IE6 не умеет отрисовывать dotted 1px самостоятельно.
К тому же, плавающие блоки имеют двойной отступ со стороны выравнивания.
</div>
<div class="dotted forIE blue">
<div class="intoDotted">
Но если блок сначала уменьшить, а затем увеличить, то все встает на свои места.
А отступ лечится добавлением display: inline;
</div>
</div>
</div>
</body>
</html>


float и margin. Лечится очень просто, в стиль всплывающему блоку дописывается display: inline;

На десерт. Есть .js, который, как утверждает автор, заставляет старых ослов вести себя как IE 7. Не юзал, но интересно (кстати, сам скрипт небольшой). Страница проекта на google.code (http://code.google.com/p/ie7-js/)