Javascript.RU

Отображение 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 возвращают ноль. В других браузерах, увы, проверить не удалось.

+11

Автор: Гость (не зарегистрирован), дата: 18 марта, 2010 - 12:41
#permalink

У меня все смешалось в голове после прочтения этой статьи Sad . Что же такое document если он не канва и не ICB ? Насколько я понял "портом просмотра" - это window. Я думаю тут бы помогла еще одна схема на которой было бы показано кто за чем следует включаю document и window.


Автор: subzey, дата: 18 марта, 2010 - 19:12
#permalink

Это не одно дерево, а два.

С точки зрения отображения:
Окно браузера (window) содержит порт просмотра. Порт просмотра содержит (в каком-то смысле) канву. На канве расположен корневой блок (который ICB). В ICB содержится <html> (document.documentElement).

С точки зрения яваскрипта:
window содержит document (корень DOM), document содержит <html> (document.documentElement).

Думаю, я разрожусь второй частью поста, тем более, что кое-какие детали я осветить забыл.


Автор: Гость (не зарегистрирован), дата: 21 марта, 2010 - 01:06
#permalink

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


Автор: Илья Кантор, дата: 22 марта, 2010 - 19:20
#permalink

Добавил [ html ]'у параметр height. Спасибо за интересную запись.


Автор: Деловой (не зарегистрирован), дата: 10 апреля, 2010 - 20:14
#permalink

Насколько прибамбасы к тегу html являются валидными с точки зрения W3C спецификаций?


Автор: subzey, дата: 14 апреля, 2010 - 18:01
#permalink

«Прибамбасы»?
HTML является вполне себе нормальным с точки зрения валидатора элементом, единственное ограничение — он должен быть строго один, не больше и не меньше. Он может иметь аттрибуты id, class, style, title, lang, dir, xml:lang, xmlns. (Курсивом отмечены те, которые может принимать вообще любой элемент.)

Или о чём Вы?

UPD: Илья имел в виду BBcode-тег [html], а не аттрибут элемента языка html.


Автор: Гость (не зарегистрирован), дата: 13 апреля, 2022 - 06:30
#permalink

Отправить комментарий

Приветствуются комментарии:
  • Полезные.
  • Дополняющие прочитанное.
  • Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
    Для остальных вопросов и обсуждений есть форум.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.
Содержание этого поля является приватным и не предназначено к показу.
  • Адреса страниц и электронной почты автоматически преобразуются в ссылки.
  • Разрешены HTML-таги: <strike> <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <u> <i> <b> <pre> <img> <abbr> <blockquote> <h1> <h2> <h3> <h4> <h5> <p> <div> <span> <sub> <sup>
  • Строки и параграфы переносятся автоматически.
  • Текстовые смайлы будут заменены на графические.

Подробнее о форматировании

CAPTCHA
Антиспам
17 + 0 =
Введите результат. Например, для 1+3, введите 4.
 
Поиск по сайту
Другие записи этого автора
subzey
Содержание

Учебник javascript

Основные элементы языка

Сундучок с инструментами

Интерфейсы

Все об AJAX

Оптимизация

Разное

Дерево всех статей

Популярные таги
Последние комментарии
Последние темы на форуме
Forum