diff --git a/2-ui/1-document/14-styles-and-classes/article.md b/2-ui/1-document/14-styles-and-classes/article.md index cd73976a..5659f895 100644 --- a/2-ui/1-document/14-styles-and-classes/article.md +++ b/2-ui/1-document/14-styles-and-classes/article.md @@ -287,46 +287,11 @@ function getStyle(elem) { Он основан на свойствах `runtimeStyle` и `pixelLeft`, работающих только в IE. -В следующем примере функция `getIEComputedStyle(elem, prop)` получает значение в пикселях для свойства `prop`, используя `elem.currentStyle` и метод Дина Эдвардса. +В [edit src="getiecomputedstyle"]песочнице[/edit] вы можете найти функцию `getIEComputedStyle(elem, prop)`, которая получает значение в пикселях для свойства `prop`, используя `elem.currentStyle` и метод Дина Эдвардса, и пример её применения. Если вам интересно, как он работает, ознакомьтесь со свойствами с runtimeStyle и pixelLeft в MSDN и раскройте код. -```js -//+ src="getIEComputedStyle.js" hide="Раскрыть код" - -``` - - - -Рабочий пример (только IE): - -```html - -
Тестовый элемент с margin 1%
- - -``` - -[pre] - -
Тестовый элемент с margin 1%
- - - -[/pre] -Современные Javascript-фреймворки и полифиллы используют этот прием для эмуляции `getComputedStyle` в старых IE. +Конечно, это актуально только для IE8- и полифиллов. [/smart] @@ -343,5 +308,5 @@ function getStyle(elem) { При этом `currentStyle` возвращает значение из CSS, до окончательных вычислений, а `getComputedStyle` -- окончательное, непосредственно применённое к элементу (как правило). -Более полная информация о `style`, включающая другие, реже используемые методы работы с ним, доступна здесь: [CSSStyleDeclaration](https://developer.mozilla.org/en-US/docs/DOM/CSSStyleDeclaration). +Более полная информация о свойстве `style`, включающая другие, реже используемые методы работы с ним, доступна [в документации](https://developer.mozilla.org/en-US/docs/DOM/CSSStyleDeclaration). diff --git a/2-ui/1-document/14-styles-and-classes/getIEComputedStyle.js b/2-ui/1-document/14-styles-and-classes/getIEComputedStyle.js deleted file mode 100755 index df64388e..00000000 --- a/2-ui/1-document/14-styles-and-classes/getIEComputedStyle.js +++ /dev/null @@ -1,18 +0,0 @@ -function getIEComputedStyle(elem, prop) { - var value = elem.currentStyle[prop] || 0 - - // we use 'left' property as a place holder so backup values - var leftCopy = elem.style.left - var runtimeLeftCopy = elem.runtimeStyle.left - - // assign to runtimeStyle and get pixel value - elem.runtimeStyle.left = elem.currentStyle.left - elem.style.left = (prop === "fontSize") ? "1em" : value - value = elem.style.pixelLeft + "px"; - - // restore values for left - elem.style.left = leftCopy - elem.runtimeStyle.left = runtimeLeftCopy - - return value -} diff --git a/2-ui/1-document/14-styles-and-classes/getiecomputedstyle.view/getiecomputedstyle.js b/2-ui/1-document/14-styles-and-classes/getiecomputedstyle.view/getiecomputedstyle.js new file mode 100644 index 00000000..263899c5 --- /dev/null +++ b/2-ui/1-document/14-styles-and-classes/getiecomputedstyle.view/getiecomputedstyle.js @@ -0,0 +1,18 @@ +function getIEComputedStyle(elem, prop) { + var value = elem.currentStyle[prop] || 0; + + // we use 'left' property as a place holder so backup values + var leftCopy = elem.style.left; + var runtimeLeftCopy = elem.runtimeStyle.left; + + // assign to runtimeStyle and get pixel value + elem.runtimeStyle.left = elem.currentStyle.left; + elem.style.left = (prop === "fontSize") ? "1em" : value; + value = elem.style.pixelLeft + "px"; + + // restore values for left + elem.style.left = leftCopy; + elem.runtimeStyle.left = runtimeLeftCopy; + + return value; +} \ No newline at end of file diff --git a/2-ui/1-document/14-styles-and-classes/getiecomputedstyle.view/index.html b/2-ui/1-document/14-styles-and-classes/getiecomputedstyle.view/index.html new file mode 100644 index 00000000..da281cf4 --- /dev/null +++ b/2-ui/1-document/14-styles-and-classes/getiecomputedstyle.view/index.html @@ -0,0 +1,24 @@ + + + + + + + + + + +
Тестовый элемент с margin 1%
+ + + + \ No newline at end of file diff --git a/2-ui/1-document/15-metrics/1-get-scroll-height-bottom/task.md b/2-ui/1-document/15-metrics/1-get-scroll-height-bottom/task.md index 944fe06c..d2684e18 100644 --- a/2-ui/1-document/15-metrics/1-get-scroll-height-bottom/task.md +++ b/2-ui/1-document/15-metrics/1-get-scroll-height-bottom/task.md @@ -2,8 +2,8 @@ [importance 5] -Свойство `elem.scrollTop` содержит размер прокрученной области при отсчете сверху. А как подсчитать его снизу? +Свойство `elem.scrollTop` содержит размер прокрученной области при отсчете сверху. А как подсчитать размер прокрутки снизу? Напишите соответствующее выражение для произвольного элемента `elem`. -Проверьте: если прокрутки нет или элемент полностью прокручен -- оно должно давать ноль. \ No newline at end of file +Проверьте: если прокрутки нет вообще или элемент полностью прокручен -- оно должно давать ноль. \ No newline at end of file diff --git a/2-ui/1-document/15-metrics/3-div-placeholder/solution.md b/2-ui/1-document/15-metrics/3-div-placeholder/solution.md index b6cf804d..13b7d7e5 100644 --- a/2-ui/1-document/15-metrics/3-div-placeholder/solution.md +++ b/2-ui/1-document/15-metrics/3-div-placeholder/solution.md @@ -8,7 +8,7 @@ **Всё, кроме `margin`, можно получить из свойств DOM-элемента, а `margin` -- только через `getComputedStyle`.** -Причём `margin` мы обязаны поставить, так как иначе элемент не будет отодвинут от внешних. +Причём `margin` мы обязаны поставить, так как иначе наш элемент при вставке будет вести себя иначе, чем исходный. Код: diff --git a/2-ui/1-document/15-metrics/3-div-placeholder/task.md b/2-ui/1-document/15-metrics/3-div-placeholder/task.md index be5da97c..130cdb84 100644 --- a/2-ui/1-document/15-metrics/3-div-placeholder/task.md +++ b/2-ui/1-document/15-metrics/3-div-placeholder/task.md @@ -38,7 +38,9 @@ div.style.right = div.style.top = 0; **Допишите код Валеры, сделав так, чтобы текст оставался на своем месте после того, как `DIV` будет смещен.** -Сделайте это путем создания вспомогательного `DIV` с теми же размерами (`width`, `height`, `border`, `margin`, `padding`), что и у желтого `DIV`. Используйте только JavaScript, без CSS. +Сделайте это путем создания вспомогательного `DIV` с теми же `width`, `height`, `border`, `margin`, `padding`, что и у желтого `DIV`. + +Используйте только JavaScript, без CSS. Должно быть так (новому блоку задан фоновый цвет для демонстрации): diff --git a/2-ui/1-document/15-metrics/4-put-ball-in-center/task.md b/2-ui/1-document/15-metrics/4-put-ball-in-center/task.md index fa6e4500..305acfb8 100644 --- a/2-ui/1-document/15-metrics/4-put-ball-in-center/task.md +++ b/2-ui/1-document/15-metrics/4-put-ball-in-center/task.md @@ -5,17 +5,15 @@ Поместите мяч в центр поля. Исходный документ выглядит так: -[iframe src="source" edit link] +[iframe src="source" edit link height=180] **Используйте JavaScript, чтобы поместить мяч в центр:** -[iframe src="solution"] +[iframe src="solution" height=180] - - -P.S. Да, это можно сделать при помощи чистого CSS, но задача именно на JavaScript. Далее будет развитие темы и более сложные ситуации, когда JavaScript будет уже точно необходим. +P.S. Да, центрирование можно сделать при помощи чистого CSS, но задача именно на JavaScript. Далее будут другие темы и более сложные ситуации, когда JavaScript будет уже точно необходим, это -- своего рода "разминка". diff --git a/2-ui/1-document/15-metrics/5-expand-element/solution.md b/2-ui/1-document/15-metrics/5-expand-element/solution.md index 1c4e554b..4e8e4de1 100644 --- a/2-ui/1-document/15-metrics/5-expand-element/solution.md +++ b/2-ui/1-document/15-metrics/5-expand-element/solution.md @@ -1,23 +1,36 @@ -**Вначале рассмотрим неверный вариант.** +# Решение через width: auto -Он выглядит так: +Вначале рассмотрим решение через "умную" установку CSS-свойства. +Они могут быть разными. Самое простое выглядит так: + +```js +elem.style.width = 'auto'; +``` + +Такой способ работает, так как `
` по умолчанию распахивается на всю ширину. + +Конечно, такое решение не будет работать для элементов, которые сами по себе не растягиваются, например в случае со `` или при наличии `position: absolute`. + +Обратим внимание, такой вариант был бы неверен: ```js elem.style.width = '100%'; ``` -Если вы его попробуете, то увидите, что элемент начинает вылезать за рамки родителя. +По умолчанию в CSS ширина `width` -- это то, что *внутри `padding`*, а проценты отсчитываются от ширины родителя. То есть, ставя ширину в `100%`, мы говорим: "внутренняя область должна занимать `100%` ширины родителя". А в элементе есть ещё `padding`, которые в итоге вылезут наружу. -Так происходит потому, что ширина -- это то, что *внутри `padding`*. То есть, ставя ширину в `100%`, вы говорите: "внутренняя область должна занимать `100%` доступной ширины". А на `padding` остаётся `0%`. В результате поля вылезают наружу. +Можно бы поменять блочную модель, указав `box-sizing` через свойство `elem.style.boxSizing`, но такое изменение потенциально может затронуть много других свойств, поэтому нежелательно. -**Правильное решение через `clientWidth`.** +# Точное вычисление -Доступную внутреннюю ширину родителя можно получить, вычитая `padding` из `clientWidth`, и присвоить элементу: +Альтернатива -- вычислить ширину родителя через `clientWidth`. + +Доступную внутреннюю ширину родителя можно получить, вычитая из `clientWidth` размеры `paddingLeft/paddingRight`, и затем присвоить её элементу: ```js var bodyClientWidth = document.body.clientWidth; -var style = window.getComputedStyle ? getComputedStyle(elem, '') : elem.currentStyle; +var style = getComputedStyle(elem); *!* var bodyInnerWidth = bodyClientWidth - parseInt(style.paddingLeft) - parseInt(style.paddingRight); @@ -26,20 +39,5 @@ var bodyInnerWidth = bodyClientWidth - parseInt(style.paddingLeft) - parseInt(st elem.style.width = bodyInnerWidth + 'px'; ``` -Этот вариант сломается, если в IE<9 значение `padding` указано не в пикселях. Получение пикселей из процентов и других единиц измерения рассмотрено в главе [](/styles-and-classes). +Такое решение будет работать всегда, вне зависимости от типа элемента. -**Правильный вариант с CSS.** - -**Самое лучшее решение получится, если вспомнить, что элемент и сам рад растянуться по всей доступной ширине, и делает это по умолчанию.** - -Достаточно вернуть ему стандартный алгоритм вычисленя ширины, установив `width: auto`: - -```js -elem.style.width = 'auto'; -``` - -Но.. **Это не будет работать для элементов, которые сами по себе не растягиваются**, например в случае `position: absolute` или `float`. - -Такой элемент можно расширить, используя предыдущее решение. - -[edit src="solution"]Документ с обоими решениями[/edit] \ No newline at end of file diff --git a/2-ui/1-document/15-metrics/5-expand-element/source.view/index.html b/2-ui/1-document/15-metrics/5-expand-element/source.view/index.html index d2c10ef0..a33552bd 100755 --- a/2-ui/1-document/15-metrics/5-expand-element/source.view/index.html +++ b/2-ui/1-document/15-metrics/5-expand-element/source.view/index.html @@ -4,11 +4,11 @@ -``` - -Результат выглядит так: - - - -Вы можете открыть документ [edit src="metric"]по этой ссылке[/edit]. -## Получение width/height из CSS - -Какой способ первый приходит на ум, когда есть задача определить `width/height`? - -Если вы внимательно читали до этого момента, то уж точно знаете, что CSS-высоту и ширину `width/height` можно установить с помощью `elem.style` и извлечь, используя `getComputedStyle()/currentStyle`, которые в подробностях обсуждаются в главе [](/styles-and-classes). - -Решение может быть таким: +Получение ширины может быть таким: ```js //+ run var elem = document.body; - -var style = window.getComputedStyle ? getComputedStyle(elem, "") : elem.currentStyle; -alert(style.width); // вывести CSS-ширину body +alert( getComputedStyle(elem).width ); // вывести CSS-ширину для body ``` Всегда ли такой подход сработает? Увы, нет!
    -
  1. Во-первых, CSS-свойства `width/height` зависят от другого свойства -- `box-sizing`, которое определяет, что такое, собственно, эти ширина и высота. По умолчанию они относятся к размеру внутренней части элемента, которая лежит внутри `padding`, а если нужно узнать полную высоту/ширину?
  2. -
  3. В IE8- могут быть нестыковки с единицами измерения -- как мы помним, `currentStyle` не пересчитывает размеры в пиксели.
  4. -
  5. И, наконец, самое главное, свойства `width/height` могут быть равны `auto`! +
  6. Во-первых, CSS-свойства `width/height` зависят от другого свойства -- `box-sizing`, которое определяет, что такое, собственно, эти ширина и высота. Получается, что изменение этого свойства, к примеру, для более удобной вёрстки, может сломать JavaScript.
  7. +
  8. Во-вторых, свойства `width/height` могут быть равны `auto`. Например, для инлайн-элемента: @@ -62,110 +33,83 @@ alert(style.width); // вывести CSS-ширину body Привет! ``` - -Конечно, с точки зрения CSS размер `auto` -- совершенно нормально, но нам-то в JavaScript нужен конкретный размер в пикселях, который мы сможем использовать для вычислений.
-## Полоса прокрутки +Конечно, с точки зрения CSS размер `auto` -- совершенно нормально, но нам-то в JavaScript нужен конкретный размер в пикселях, который мы могли бы использовать для вычислений. Получается, что в данном случае ширина `width` из CSS вообще бесполезна. + +## Полоса прокрутки и содержимое Полоса прокрутки -- причина многих проблем и недопониманий. Как говорится, "дьявол кроется в деталях". Недопустимо, чтобы наш код работал на элементах без прокрутки и начинал "глючить" с ней. Поэтому мы с самого начала будем её учитывать. -**При наличии вертикальной полосы прокрутки -- она забирает себе часть ширины элемента.** +При наличии вертикальной полосы прокрутки -- во многих операционных системах и браузерах она забирает себе место у "внутренней части" элемента. -Ширина полосы прокрутки обычно составляет около `14-18px`, в зависимости от браузера и операционной системы. Бывает и `0` для полупрозрачной прокрутки, не отъедающей место. В примере подразумевается, что прокрутка место ест, поэтому внутренняя область будет уже не `300px`, а около `284px`. +...Но при этом некоторые браузеры отражают реальное уменьшение ширины в результате `getComputedStyle(elem).width`, а некоторые -- нет. -**Несмотря на то, что на рисунке полоса прокрутки находится визуально в правом поле -- отнимает место она не у `padding`, а у внутренней области элемента.** - -...Но при этом некоторые браузеры отражают это уменьшение ширины в результате `getComputedStyle(...).width`, а некоторые -- нет. - -В примере ниже в стилях указано `width:300px`. А вот `getComputedStyle` возвращает `300px/284px`, в зависимости от браузера. +В примере ниже у элемента с текстом в стилях указано `width:300px`. А вот `getComputedStyle` возвращает значение от `280px` до `300px`, в зависимости от ОС и браузера. +[online] Если ваш браузер в принципе показывает полосу прокрутки (например, под Windows почти все браузеры так делают), то вы можете протестировать это сами, нажав на кнопку в ифрейме ниже: +[/online] [iframe src="cssWidthScroll" link border=1] Описанные разночтения касаются только чтения свойства `getComputedStyle(...).width` из JavaScript, визуальное отображение корректно в обоих случаях -- ширина текста при наличии прокрутки в обоих случаях уменьшается. -**Здесь и далее, мы будем понимать под `width` именно реальную ширину внутренней области (около `284px`), а не результат чтения CSS-свойства `width`, который может быть разным в зависимости от браузера/OS.** +Этот пример важен, чтобы ещё лучше понять: свойство `width` из CSS не следует использовать в JavaScript. И, конечно, далее мы будем вспоминать о полосе прокрутке там, где она важна. -## JavaScript-метрики +## Метрики: образец документа -В JavaScript существует ряд дополнительных свойств, содержащих размеры элементов. Мы будем называть их "метриками". +Мы будем использовать для примера вот такой элемент, у которого есть рамка (border), поля (padding), отступы (margin) и прокрутка: -**Метрики JavaScript, в отличие от свойств CSS, содержат числа, всегда в пикселях и без единиц измерения на конце.** +```html +
+ ...Текст... +
+ ``` -Обратите внимание, в `clientHeight` входят и верхнее и нижнее поля, несмотря на то, что нижнее поле заполнено текстом. +Результат выглядит так: -**Если `padding` нет, то `clientWidth/Height` покажет реальный размер области данных, внутри рамок и полосы прокрутки.** + - +Вы можете открыть этот документ [edit src="metric"]в песочнице[/edit]. -### scrollWidth/Height +У элементов существует ряд свойств, содержащих их внешние и внутренние размеры. Мы будем называть их "метриками". -Ширина и высота контента *с учетом прокручиваемой области*. +Метрики, в отличие от свойств CSS, содержат числа, всегда в пикселях и без единиц измерения на конце. - +Вот общая картина: -**`scrollWidth/Height` то же самое, что и `clientWidth/Height`, но включает в себя прокручиваемую область.** + - +На картинке все они с трудом помещаются, но, как мы увидим далее, их значения просты и понятны. -Эти свойства можно использовать, чтобы "распахнуть" элемент на всю ширину/высоту: +Будем исследовать их снаружи элемента и вовнутрь. -```js -element.style.height = element.scrollHeight + 'px'; -``` +## offsetWidth/Height -Нажмите на кнопку, чтобы распахнуть элемент: - -
текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст текст
- - - -### scrollTop/scrollLeft - -Размеры текущей прокрученной части элемента -- вертикальной и горизонтальной. - -Следующее изображение иллюстрирует `scrollHeight` и `scrollTop` для блока с вертикальной прокруткой. - - - -[smart header="`scrollLeft/scrollTop` можно изменять"] -**В отличие от большинства свойств, которые доступны только для чтения, значения `scrollLeft/scrollTop` можно изменить, и браузер выполнит прокрутку элемента**. - -При клике на следующий элемент будет выполняться код `elem.scrollTop += 10`. Поэтому он будет прокручиваться на `10px` вниз: - -
Кликни
Меня
1
2
3
4
5
6
7
8
9
-[/smart] - - -### offsetWidth/Height - -Внешняя ширина/высота блока, полный размер, включая рамки, исключая внешние отступы `margin`. +Эти свойства дают "внешнюю" ширину/высоту элемента, то есть его полный размер, включая рамки `border`, но исключая внешние отступы `margin`. +В примере выше: -Все свойства, кроме `scrollLeft/scrollTop` доступны только для чтения. Изменение этих свойств заставляет браузер прокручивать элемент. +Все свойства, доступны только для чтения, кроме `scrollLeft/scrollTop`. Изменение этих свойств заставляет браузер прокручивать элемент. -Краткая схема: - - - -**Прокрутку *элемента* можно прочитать или изменить через свойства `scrollLeft/Top`.** - -В этой главе мы считали, что страница находится в режиме соответствия стандартам. В режиме совместимости -- всё так же, но некоторые старые браузеры требуют `document.body` вместо `documentElement`. +В этой главе мы считали, что страница находится в режиме соответствия стандартам. В режиме совместимости -- некоторые старые браузеры требуют `document.body` вместо `documentElement`, в остальном всё так же. Конечно, по возможности, стоит использовать только режим соответствия стандарту. diff --git a/2-ui/1-document/15-metrics/summary2.png b/2-ui/1-document/15-metrics/summary2.png new file mode 100644 index 00000000..a68b1c74 Binary files /dev/null and b/2-ui/1-document/15-metrics/summary2.png differ diff --git a/2-ui/1-document/16-metrics-window/1-get-document-scrolls/task.md b/2-ui/1-document/16-metrics-window/1-get-document-scrolls/task.md index b04f07cd..7a5b8cd4 100644 --- a/2-ui/1-document/16-metrics-window/1-get-document-scrolls/task.md +++ b/2-ui/1-document/16-metrics-window/1-get-document-scrolls/task.md @@ -1,4 +1,4 @@ -# Получить прокрутки документа +# TODO: перенеси меня в координаты? Тут координат еще не было [importance 5] diff --git a/2-ui/1-document/16-metrics-window/article.md b/2-ui/1-document/16-metrics-window/article.md index e1d93a2b..f91c999d 100644 --- a/2-ui/1-document/16-metrics-window/article.md +++ b/2-ui/1-document/16-metrics-window/article.md @@ -1,7 +1,11 @@ # Размеры и прокрутка страницы -Многие метрики для страницы работают совсем не так, как для элементов. Поэтому рассмотрим решения типичных задач для страницы отдельно. +Многие метрики для страницы работают совсем не так, как для элементов. + +Поэтому мы рассмотрим решения типичных задач для страницы отдельно. + [cut] + ## Ширина/высота видимой части окна Свойства `clientWidth/Height` для элемента `document.documentElement` позволяют получить ширину/высоту видимой области окна. @@ -12,49 +16,69 @@ Этот способ -- кросс-браузерный. -## Ширина/высота всей страницы, с учётом прокрутки +[warn header="Не `window.innerWidth/Height`"] +Все браузеры, кроме IE8-, также поддерживают свойства `window.innerWidth/innerHeight`. Они хранят текущий размер окна. -Если прокрутка на странице присутствует, то полные размеры страницы можно взять в `document.documentElement.scrollWidth/scrollHeight`. +Выглядят они короче, чем `document.documentElement.clientWidth`, однако есть один нюанс. + +Свойства `clientWidth/Height` берут в расчёт полосу прокрутку, а эти свойства -- нет. + +Если справа часть страницы занимает полоса прокрутки, то эти строки выведут разное: +```js +//+ run +alert( window.innerWidth ); // вся ширина окна +alert( document.documentElement.clientWidth ); // ширина минус прокрутка +``` + +Обычно нам нужна именно *доступная* ширина окна, например, чтобы нарисовать что-либо, то есть за вычетом полосы прокрутки. Поэтому используем `document.documentElement.clientWidth`. +[/warn] + +## Ширина/высота страницы с учётом прокрутки + +Если прокрутка на странице заведомо присутствует, то полные размеры страницы можно взять в `document.documentElement.scrollWidth/scrollHeight`. Проблемы с этими свойствами возникают, когда *прокрутка то есть, то нет*. В этом случае они работают некорректно. -В браузерах Chrome/Safari и Opera при отсутствии прокрутки значение `document.documentElement.scrollHeight` в этом случае может быть даже меньше, чем `document.documentElement.clientHeight` (нонсенс!). Эта проблема -- именно для `document.documentElement`, то есть для всей страницы. С обычными элементами здесь всё в порядке. +В браузерах Chrome/Safari и Opera при отсутствии прокрутки значение `document.documentElement.scrollHeight` в этом случае может быть даже меньше, чем `document.documentElement.clientHeight` (нонсенс!). -Надёжно определить размер с учетом прокрутки можно, взяв максимум из двух свойств: +Эта проблема возникает именно для `document.documentElement`, то есть для всей страницы. С обычными элементами здесь всё в порядке. + +Надёжно определить размер страницы с учетом прокрутки можно, взяв максимум из нескольких свойств: ```js //+ run -var scrollHeight = document.documentElement.scrollHeight; -var clientHeight = document.documentElement.clientHeight; - -*!* -scrollHeight = Math.max(scrollHeight, clientHeight); -*/!* +var scrollHeight = Math.max( + document.body.scrollHeight, document.documentElement.scrollHeight, + document.body.offsetHeight, document.documentElement.offsetHeight, + document.body.clientHeight, document.documentElement.clientHeight +); alert('Высота с учетом прокрутки: ' + scrollHeight); ``` -## Прокрутка страницы [#page-scroll] +Почему так? Лучше и не спрашивайте, это одно из редких мест, где просто обходятся ошибки в браузерах. Глубокой логики здесь нет. -### Получение текущей прокрутки +## Получение текущей прокрутки [#page-scroll] Значение текущей прокрутки страницы хранится в свойствах `window.pageXOffset/pageYOffset`. -Но эти свойства: +```js +//+ run +alert('Текущая прокрутка сверху: ' + window.pageYOffset); +alert('Текущая прокрутка слева: ' + window.pageXOffset); +``` + +Эти свойства: -Поэтому для кросс-браузерности рассмотрим другой способ -- свойство `document.documentElement.scrollLeft/Top`. +Если IE8- не волнует, то для чтения прокрутки лучше способа нет, рецепт готов. - +Альтернативный вариант -- это свойства `document.documentElement.scrollLeft/Top`, но они не работают в Safari/Chrome/Opera, которые используют `document.body` (это неправильно). -Таким образом, для IE8+ и других браузеров, работающих в режиме соответствия стандартам, получить значение прокрутки можно так: +Так что кросс-браузерный вариант с учётом IE8: ```js //+ run @@ -63,73 +87,39 @@ var scrollTop = window.pageYOffset || document.documentElement.scrollTop; alert("Текущая прокрутка: " + scrollTop); ``` -### С учётом IE7- и Quirks Mode [#getPageScroll] +## Изменение прокрутки: scrollTo, scrollBy, scrollIntoView [#window-scroll] -Если дополнительно нужна поддержка IE<8, то там тоже есть важная тонкость. Документ может быть смещен относительно начальной позиции (0,0). Это смещение хранится в `document.documentElement.clientLeft/clientTop`, и мы должны вычесть его. - -Если дополнительно добавить возможность работы браузера в Quirks Mode, то надёжный способ будет таким: - -```js -//+ run -var html = document.documentElement; -var body = document.body; - -var scrollTop = html.scrollTop || body && body.scrollTop || 0; -scrollTop -= html.clientTop; -alert("Текущая прокрутка: " + scrollTop); -``` - -Итого, можно создать кросс-браузерную функцию, которая возвращает значения прокрутки и поддерживает в том числе IE8-: - -```js -var getPageScroll = (window.pageXOffset != undefined) ? - function() { - return { - left: pageXOffset, - top: pageYOffset - }; - } : - function() { - var html = document.documentElement; - var body = document.body; - - var top = html.scrollTop || body && body.scrollTop || 0; - top -= html.clientTop; - - var left = html.scrollLeft || body && body.scrollLeft || 0; - left -= html.clientLeft; - - return { top: top, left: left }; - } -``` - -### Изменение прокрутки: scrollTo, scrollBy, scrollIntoView [#window-scroll] - -[smart] +[warn] Чтобы прокрутить страницу при помощи JavaScript, её DOM должен быть полностью загружен. -[/smart] +[/warn] На обычных элементах свойства `scrollTop/scrollLeft` можно изменять, и при этом элемент будет прокручиваться. -Никто не мешает точно так же поступать и со страницей. Во всех браузерах, кроме Chrome/Safari можно осуществить прокрутку установкой `document.documentElement.scrollTop`, а в Chrome/Safari -- использовать для этого `document.body.scrollTop`. И будет работать. - -Но есть и другое, полностью кросс-браузерное решение -- специальные методы прокрутки страницы [window.scrollBy(x,y)](https://developer.mozilla.org/en/Window.scrollBy) и [window.scrollTo(pageX,pageY)](https://developer.mozilla.org/en/Window.scrollTo). - +Никто не мешает точно так же поступать и со страницей. Во всех браузерах, кроме Chrome/Safari/Opera можно осуществить прокрутку установкой `document.documentElement.scrollTop`, а в указанных -- использовать для этого `document.body.scrollTop`. И будет работать. +Но есть и другое, полностью кросс-браузерное и универсальное решение -- специальные методы прокрутки страницы [window.scrollBy(x,y)](https://developer.mozilla.org/en/Window.scrollBy) и [window.scrollTo(pageX,pageY)](https://developer.mozilla.org/en/Window.scrollTo). +## scrollIntoView + Для полноты картины рассмотрим также метод [elem.scrollIntoView(top)](https://developer.mozilla.org/en/DOM/element.scrollIntoView). Метод `elem.scrollIntoView(top)` вызывается на элементе и прокручивает страницу так, чтобы элемент оказался вверху, если параметр `top` равен `true`, и внизу, если `top` равен `false`. Причем, если параметр `top` не указан, то он считается равным `true`. @@ -170,12 +160,11 @@ var getPageScroll = (window.pageXOffset != undefined) ?
  • Для получения размеров страницы с учётом прокрутки: ```js -var scrollHeight = document.documentElement.scrollHeight; -var clientHeight = document.documentElement.clientHeight; - -*!* -scrollHeight = Math.max(scrollHeight, clientHeight); -*/!* +var scrollHeight = Math.max( + document.body.scrollHeight, document.documentElement.scrollHeight, + document.body.offsetHeight, document.documentElement.offsetHeight, + document.body.clientHeight, document.documentElement.clientHeight +); ```
  • @@ -184,9 +173,9 @@ scrollHeight = Math.max(scrollHeight, clientHeight); **Прокрутка окна:**