Отображение HTML: копаем вглубь
В этом посте я расскажу про то, что находится у каждой страницы выше тега <html> , и, возможно, это поможет глубже понять механизм работы браузера.
В последнее время я заметил на форуме повышенный интерес к определению размеров окна браузера. Возможно, мы с упорством локомотива мчимся к web 2.1, и снова возвращаемся к желанию девелопера контролировать не просто разметку, а все окно.
Хорошо это, или плохо, не знаю. Но, так или иначе, что же скрывается там, где еще нет DOM?
Начнем с того, что же мы отображаем.
В результате рендера браузером мы имеем на руках некоего размера прямоугольную простыню пикселей. Это самое, то, что нужно отобразить пользователю, — канва («canvas»). Не следует путать эту канву с элементом HTML5 <canvas> , это совершенно разные вещи!
Размеры канвы определяют размеры страницы, а сами же определяются достаточно нехитрым образом. Слева-сверху канвы находится точка отсчета с координатами (0, 0). Ширина канвы — расстояние от этой точки до самого правого прорендеренного пикселя, и то же самое по высоте.
Очевидно, размеры канвы могут быть больше, чем поместится в окне браузера. В этом случае на экране показывается не вся канва, а только та ее часть, которая попадает в определенную область. А при необходимости ее можно сдвинуть с помощью механизма прокрутки и посмотреть другую часть. Эта самая прямоугольная область, где отображается содержимое, называется портом просмотра («vewport»).
Идем дальше. Первым делом в канве отрисовывается тег <html> . Он должен быть какой-то ширины и высоты, и, причем, эти размеры должны от чего-то зависеть явным образом. Но размеры канвы-то сами зависят от ширины <html> , поэтому она нам не поможет.
Для разрешения этой ситуации вводится корневой несущий блок («initially containing block», «ICB»). Его верхний левый край находится в верхнем левом углу канвы, а размеры равны размерам порта просмотра. Этот блок является непосредственным родителем <html> и определяет его размеры.
Ну, и что же у нас получилось?
Получилась координатная плоскость с единичными отрезками, по которой скользит «окно» для просмотра содержимого. Стройно и логично, молодцы W3C!
Ну, и что же у нас следует из этого всего?
Оговорюсь сразу, что не все из перечисленного относится к IE6. Но к нему мы вернемся.
Хотя полосы прокрутки окна и тесно связаны с портом просмотра, они в него не входят. Как только появляется вертикальная полоса прокрутки, window.innerWidth перестает быть равным ширине вьюпорта.
То той же причине невозможно перекрыть никаким элементом полосы прокрутки окна, только убирать через css. (Хотя полосы прокрутки элемента перекрыть можно.)
Коль скоро, ширина канвы считается от нулевой точки, все содержимое левее или выше нее в канву не попадает. Замечали, что если на странице есть «выпирающие» элементы, слева и сверху они обрезаются, а справа и снизу вызывают полосы прокрутки? Это не баг, это фича!
Хоть это, вроде бы, и не указано явно, но размеры канвы не могут быть меньше размера порта просмотра. Размерами порта просмотра определяются размеры корневого несущего блока, а тот, в свою очередь, растягивает канву.
Элемент <HTML> по умолчанию той же ширины, что и корневой блок. Однако, к нему применим css: width, margin (даже отрицательный), даже css3 transform.
Вот и выходит, что допущение, что documentElement.clientWidth вовсе не обязательно равно ширине канвы или ширине корневого блока.
Если верить W3C, позиционирование элементов с установленным position: absolute производится от ближайшего родителя с position не равным static . Обычно этот элемент легко и просто определяется при помощи конструкции element.offsetParent .
Однако, это не совсем верно. Корнем цепи .offsetParent является <body> , а позиционируется элемент по-умолчанию от ICB.
Живой пример:
<!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" xml:lang="en" lang="en">
<head>
<title>Title</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<style type="text/css">
html {height: 100px; width: 100px; margin: 20px; border: solid red 1px}
body {height: 100px; width: 100px; margin: 20px; border: solid green 1px}
#tester {width: 10px; height: 10px; background: black; position: absolute; top: 0px; left: 0px;}
</style>
</head>
<body>
<div id="tester"></div>
<script type="text/javascript">
var tester = document.getElementById("tester");
alert(tester.offsetParent.tagName + "\r\n" + tester.offsetTop); // BODY, 0
alert(document.body.offsetParent + "\r\n" + document.body.offsetTop); // Null, ???
</script>
</body>
</html>
Вроде как, если верить первому алерту, тестовый див позиционируется от body и расположен в его углу, однако, это не так — позиция дива явно задается от ICB, да и расстояние до body не меньше 20 пикселей.
Второй алерт показывает, что у body нет offsetParent , однако, в Firefox body находится на расстоянии -1 пиксель от этого ничего.
Не забывайте про эту разницу, иначе может выйти неприятная ситуация, когда, к примеру, шильд затемнения в лайтбоксе показывается не на весь экран.
Да, и кстати, из-за позиционирования от корневого несущего блока бережно выставленные по низу экрана стики-футеры и уезжают вверх при прокрутке.
Как бы этого ни хотелось, document не представляет из себя ни канву, ни ICB. document.width не существует.
Элемент с position: fixed , собственно говоря, позиционируется от порта просмотра. И, пожалуй, единственный способ узнать размеры вьюпорта — добавить в документ див с position: fixed шириной и высотой по 100%, а потом померить clientWidth и clientHeight .
У «шестерки» все по-другому, элемент <html> корневой и берет на себя роли канвы и ICB. Его видимая часть — вроде как, порт просмотра, но при таком раскладе полноценно реализовать поддержку position: fixed и не получилось бы.
Но есть в этом и прелести. Например, при помощи правила html {border: 0px} можно выиграть пару пикселей.
Надеюсь, этот текст дал Вам что-то новое и позволит избежать досадных ошибок. Буду рад комментам. Если где-то накосячил, не судите строго, время уже позднее.
В блоге премногоуважаемого ppk я встретил упоминание того, что document.documentElement.clientWidth и document.documentElement.clientHeight всегда возвращают ширину и высоту вьюпорта, и это особое исключение.
На самом деле, на практике это не совсем так. Вернее, не всегда. Этот приём работает только в standards mode.
В quirks mode в опере, файрфоксе и хроме ширина вьюпорта возвращается верно, в качестве высоты же возвращается именно clientHeight элемента <html> . В IE8 в quirks mode и clientWidth и clientHeight возвращают ноль. В других браузерах, увы, проверить не удалось.
|
У меня все смешалось в голове после прочтения этой статьи . Что же такое document если он не канва и не ICB ? Насколько я понял "портом просмотра" - это window. Я думаю тут бы помогла еще одна схема на которой было бы показано кто за чем следует включаю document и window.
Это не одно дерево, а два.
С точки зрения отображения:
Окно браузера (
window
) содержит порт просмотра. Порт просмотра содержит (в каком-то смысле) канву. На канве расположен корневой блок (который ICB). В ICB содержится<html>
(document.documentElement
).С точки зрения яваскрипта:
window
содержитdocument
(корень DOM),document
содержит<html>
(document.documentElement
).Думаю, я разрожусь второй частью поста, тем более, что кое-какие детали я осветить забыл.
И картинок, пожалуйста, побольше . Визуально легче воспринимаются такие построения.
Добавил [ html ]'у параметр height. Спасибо за интересную запись.
Насколько прибамбасы к тегу html являются валидными с точки зрения W3C спецификаций?
«Прибамбасы»?
HTML является вполне себе нормальным с точки зрения валидатора элементом, единственное ограничение — он должен быть строго один, не больше и не меньше. Он может иметь аттрибуты id, class, style, title, lang, dir, xml:lang, xmlns. (Курсивом отмечены те, которые может принимать вообще любой элемент.)
Или о чём Вы?
UPD: Илья имел в виду BBcode-тег
[h
tml]
, а не аттрибут элемента языка html.Roksa pl kraśnik
Отправить комментарий
Приветствуются комментарии:Для остальных вопросов и обсуждений есть форум.