diff --git a/2-ui/1-document/15-metrics/article.md b/2-ui/1-document/15-metrics/article.md index 88f29d20..9a598825 100644 --- a/2-ui/1-document/15-metrics/article.md +++ b/2-ui/1-document/15-metrics/article.md @@ -6,64 +6,6 @@ [cut] -## Не стоит брать width/height из CSS - -Какой способ первый приходит на ум, когда есть задача определить ширину/высоту элемента. - -Если вы внимательно читали до этого момента, то уж точно знаете, что CSS-высоту и ширину можно установить с помощью `elem.style` и извлечь, используя `getComputedStyle`, которые в подробностях обсуждаются в главе [](/styles-and-classes). - -Получение ширины может быть таким: - -```js -//+ run -var elem = document.body; -alert( getComputedStyle(elem).width ); // вывести CSS-ширину для body -``` - -Всегда ли такой подход сработает? Увы, нет! - -
    -
  1. Во-первых, CSS-свойства `width/height` зависят от другого свойства -- `box-sizing`, которое определяет, что такое, собственно, эти ширина и высота. Получается, что изменение этого свойства, к примеру, для более удобной вёрстки, может сломать JavaScript.
  2. -
  3. Во-вторых, свойства `width/height` могут быть равны `auto`. - -Например, для инлайн-элемента: - -```html - -Привет! - - -``` -
  4. -
- - -Конечно, с точки зрения CSS размер `auto` -- совершенно нормально, но нам-то в JavaScript нужен конкретный размер в пикселях, который мы могли бы использовать для вычислений. Получается, что в данном случае ширина `width` из CSS вообще бесполезна. - -## Полоса прокрутки и содержимое - -Полоса прокрутки -- причина многих проблем и недопониманий. Как говорится, "дьявол кроется в деталях". Недопустимо, чтобы наш код работал на элементах без прокрутки и начинал "глючить" с ней. Поэтому мы с самого начала будем её учитывать. - -При наличии вертикальной полосы прокрутки -- во многих операционных системах и браузерах она забирает себе место у "внутренней части" элемента. - -...Но при этом некоторые браузеры отражают реальное уменьшение ширины в результате `getComputedStyle(elem).width`, а некоторые -- нет. - -В примере ниже у элемента с текстом в стилях указано `width:300px`. А вот `getComputedStyle` возвращает значение от `280px` до `300px`, в зависимости от ОС и браузера. - -[online] -Если ваш браузер в принципе показывает полосу прокрутки (например, под Windows почти все браузеры так делают), то вы можете протестировать это сами, нажав на кнопку в ифрейме ниже: -[/online] - -[iframe src="cssWidthScroll" link border=1] - -Описанные разночтения касаются только чтения свойства `getComputedStyle(...).width` из JavaScript, визуальное отображение корректно в обоих случаях -- ширина текста при наличии прокрутки в обоих случаях уменьшается. - -Этот пример важен, чтобы ещё лучше понять: свойство `width` из CSS не следует использовать в JavaScript. И, конечно, далее мы будем вспоминать о полосе прокрутке там, где она важна. - ## Метрики: образец документа Мы будем использовать для примера вот такой элемент, у которого есть рамка (border), поля (padding), отступы (margin) и прокрутка: @@ -209,7 +151,7 @@ function isHidden(elem) -Поэтому в тех случаях, когда мы точно знаем, что `padding` нет, их используют для определения внутренних размеров элемента. И, в отличие от `getComputedStyle(elem).width`, эти свойства всегда работают верно. +Поэтому в тех случаях, когда мы точно знаем, что `padding` нет, их используют для определения внутренних размеров элемента. ## scrollWidth/Height @@ -261,6 +203,65 @@ element.style.height = element.scrollHeight + 'px'; [/smart] +## Не стоит брать width/height из CSS + +Теперь несколько слов о том, как не надо делать. + +Как мы знаем, CSS-высоту и ширину можно установить с помощью `elem.style` и извлечь, используя `getComputedStyle`, которые в подробностях обсуждаются в главе [](/styles-and-classes). + +Получение ширины элемента может быть таким: + +```js +//+ run +var elem = document.body; + +alert( getComputedStyle(elem).width ); // вывести CSS-ширину для elem +``` + +Всегда ли такой подход сработает? Увы, нет! + +
    +
  1. Во-первых, CSS-свойства `width/height` зависят от другого свойства -- `box-sizing`, которое определяет, что такое, собственно, эти ширина и высота. Получается, что изменение этого свойства, к примеру, для более удобной вёрстки, может сломать JavaScript.
  2. +
  3. Во-вторых, свойства `width/height` могут быть равны `auto`, например, для инлайн-элемента: + +```html + +Привет! + + +``` +
  4. +
+ + +Конечно, с точки зрения CSS размер `auto` -- совершенно нормально, но нам-то в JavaScript нужен конкретный размер в пикселях, который мы могли бы использовать для вычислений. Получается, что в данном случае ширина `width` из CSS вообще бесполезна. + +## Полоса прокрутки и содержимое + +Полоса прокрутки -- причина многих проблем и недопониманий. Как говорится, "дьявол кроется в деталях". Недопустимо, чтобы наш код работал на элементах без прокрутки и начинал "глючить" с ней. + +При наличии вертикальной полосы прокрутки, она как правило забирает себе место из "области содержимого" элемента. + +И это учитывают свойства `clientWidth/clientHeight`. + +...Но при этом некоторые браузеры отражают реальное уменьшение ширины в результате `getComputedStyle(elem).width`, а некоторые -- нет. + +В примере ниже у элемента с текстом в стилях указано `width:300px`. А вот `getComputedStyle` возвращает значение от `280px` до `300px`, в зависимости от ОС и браузера. + +[online] +Если ваш браузер в принципе показывает полосу прокрутки (например, под Windows почти все браузеры так делают), то вы можете протестировать это сами, нажав на кнопку в ифрейме ниже: +[/online] + +[iframe src="cssWidthScroll" link border=1] + +Описанные разночтения касаются только чтения свойства `getComputedStyle(...).width` из JavaScript, визуальное отображение корректно в обоих случаях -- ширина текста при наличии прокрутки в обоих случаях уменьшается. + +Этот пример важен, чтобы ещё лучше понять: CSS-свойство `width` не следует использовать в JavaScript. А стоит использовать метрики, которые мы разобрали в этом разделе. + ## Итого diff --git a/2-ui/1-document/16-metrics-window/1-get-document-scrolls/solution.md b/2-ui/1-document/16-metrics-window/1-get-document-scrolls/solution.md deleted file mode 100644 index 40278b0b..00000000 --- a/2-ui/1-document/16-metrics-window/1-get-document-scrolls/solution.md +++ /dev/null @@ -1,47 +0,0 @@ -`top` -- можно кроссбраузерно получить, как указано в главе [](/metrics-window): - -```js -function getDocumentScrollTop() { - var html = document.documentElement; - var body = document.body; - - var scrollTop = html.scrollTop || body && body.scrollTop || 0; - scrollTop -= html.clientTop; // IE<8 - - return scrollTop; -} -``` - -`bottom` -- это `top` плюс высота видимой части: - -```js -function getDocumentScrollBottom() { - return getDocumentScrollTop() + document.documentElement.clientHeight; -} -``` - -Полная высота -- максимум двух значений, детали см. в [](/metrics-window): - -```js -function getDocumentScrollHeight() { - var scrollHeight = document.documentElement.scrollHeight; - var clientHeight = document.documentElement.clientHeight; - - scrollHeight = Math.max(scrollHeight, clientHeight); - - return scrollHeight; -} -``` - -Итого, ответ, использующий описанные выше функции: - -```js -function getDocumentScroll() { - return { - top: getDocumentScrollTop(), - bottom: getDocumentScrollBottom(), - height: getDocumentScrollHeight() - }; -} -``` - 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 deleted file mode 100644 index 7a5b8cd4..00000000 --- a/2-ui/1-document/16-metrics-window/1-get-document-scrolls/task.md +++ /dev/null @@ -1,15 +0,0 @@ -# TODO: перенеси меня в координаты? Тут координат еще не было - -[importance 5] - -Напишите функцию `getDocumentScroll()`, которая возвращает объект с координатами области видимости относительно документа. - -Свойства объекта результата: - - - -В задаче можно учитывать только вертикальную прокрутку (горизонтальную отдельно нет смысла разбирать, она делается аналогично, а нужна сильно реже). \ No newline at end of file diff --git a/2-ui/1-document/16-metrics-window/article.md b/2-ui/1-document/16-metrics-window/article.md index f91c999d..af8c235c 100644 --- a/2-ui/1-document/16-metrics-window/article.md +++ b/2-ui/1-document/16-metrics-window/article.md @@ -1,27 +1,31 @@ # Размеры и прокрутка страницы -Многие метрики для страницы работают совсем не так, как для элементов. +Как найти ширину окна браузера? Как узнать всю высоту страницы, с учётом прокрутки? +Как прокрутить её из JavaScript? -Поэтому мы рассмотрим решения типичных задач для страницы отдельно. + +С точки зрения HTML, документ -- это `document.documentElement`. У этого элемента, соответствующего тегу ``, есть все стандартные свойства и метрики и, в теории, они и должны нам помочь. Однако, на практике есть ряд нюансов, именно их мы рассмотрим в этой главе. [cut] ## Ширина/высота видимой части окна -Свойства `clientWidth/Height` для элемента `document.documentElement` позволяют получить ширину/высоту видимой области окна. +Свойства `clientWidth/Height` для элемента `document.documentElement` -- это как раз ширина/высота видимой области окна. + + +[online] Например, кнопка ниже выведет размер такой области для этой страницы: - -Этот способ -- кросс-браузерный. +[/online] [warn header="Не `window.innerWidth/Height`"] -Все браузеры, кроме IE8-, также поддерживают свойства `window.innerWidth/innerHeight`. Они хранят текущий размер окна. +Все браузеры, кроме IE8-, также поддерживают свойства `window.innerWidth/innerHeight`. Они хранят текущий размер *окна браузера*. -Выглядят они короче, чем `document.documentElement.clientWidth`, однако есть один нюанс. +В чём отличие? Оно небольшое, но чрезвычайно важное. -Свойства `clientWidth/Height` берут в расчёт полосу прокрутку, а эти свойства -- нет. +Свойства `clientWidth/Height`, если есть полоса прокрутки, возвращают именно ширину/высоту документа, за вычетом прокрутки, а эти свойства -- игнорируют её наличие. Если справа часть страницы занимает полоса прокрутки, то эти строки выведут разное: ```js @@ -30,18 +34,18 @@ alert( window.innerWidth ); // вся ширина окна alert( document.documentElement.clientWidth ); // ширина минус прокрутка ``` -Обычно нам нужна именно *доступная* ширина окна, например, чтобы нарисовать что-либо, то есть за вычетом полосы прокрутки. Поэтому используем `document.documentElement.clientWidth`. +Обычно нам нужна именно *доступная* ширина окна, например, чтобы нарисовать что-либо, то есть за вычетом полосы прокрутки. Поэтому используем `documentElement.clientWidth`. [/warn] ## Ширина/высота страницы с учётом прокрутки -Если прокрутка на странице заведомо присутствует, то полные размеры страницы можно взять в `document.documentElement.scrollWidth/scrollHeight`. +Теоретически, видимая часть страницы -- это `documentElement.clientWidth/Height`, а полный размер с учётом прокрутки -- по аналогии, `documentElement.scrollWidth/scrollHeight`. -Проблемы с этими свойствами возникают, когда *прокрутка то есть, то нет*. В этом случае они работают некорректно. +Это верно для обычных элементов. -В браузерах Chrome/Safari и Opera при отсутствии прокрутки значение `document.documentElement.scrollHeight` в этом случае может быть даже меньше, чем `document.documentElement.clientHeight` (нонсенс!). +А вот для страницы с этими свойствами возникает проблема, когда *прокрутка то есть, то нет*. В этом случае они работают некорректно. В браузерах Chrome/Safari и Opera при отсутствии прокрутки значение `documentElement.scrollHeight` в этом случае может быть даже меньше, чем `documentElement.clientHeight`, что, конечно же, выглядит как совершеннейшая чепуха и нонсенс. -Эта проблема возникает именно для `document.documentElement`, то есть для всей страницы. С обычными элементами здесь всё в порядке. +Эта проблема возникает именно для `documentElement`, то есть для всей страницы. Надёжно определить размер страницы с учетом прокрутки можно, взяв максимум из нескольких свойств: @@ -56,11 +60,17 @@ var scrollHeight = Math.max( alert('Высота с учетом прокрутки: ' + scrollHeight); ``` -Почему так? Лучше и не спрашивайте, это одно из редких мест, где просто обходятся ошибки в браузерах. Глубокой логики здесь нет. +Почему так? Лучше и не спрашивайте, это одно из редких мест, где просто ошибки в браузерах. Глубокой логики здесь нет. ## Получение текущей прокрутки [#page-scroll] -Значение текущей прокрутки страницы хранится в свойствах `window.pageXOffset/pageYOffset`. +У обычного элемента текущую прокрутку можно получить в `scrollLeft/scrollTop`. + +Что же со страницей? + +Большинство браузеров корректно обработает запрос к `documentElement.scrollLeft/Top`, однако Safari/Chrome/Opera есть ошибки (к примеру [157855](https://code.google.com/p/chromium/issues/detail?id=157855), [106133](https://bugs.webkit.org/show_bug.cgi?id=106133)), из-за которых следует использовать `document.body`. + +Чтобы вообще обойти проблему, можно использовать специальные свойства `window.pageXOffset/pageYOffset`: ```js //+ run @@ -74,11 +84,9 @@ alert('Текущая прокрутка слева: ' + window.pageXOffset);
  • Их можно только читать, а менять нельзя.
  • -Если IE8- не волнует, то для чтения прокрутки лучше способа нет, рецепт готов. +Если IE8- не волнует, то просто используем эти свойства. -Альтернативный вариант -- это свойства `document.documentElement.scrollLeft/Top`, но они не работают в Safari/Chrome/Opera, которые используют `document.body` (это неправильно). - -Так что кросс-браузерный вариант с учётом IE8: +Кросс-браузерный вариант с учётом IE8 предусматривает откат на `documentElement`: ```js //+ run @@ -95,9 +103,9 @@ alert("Текущая прокрутка: " + scrollTop); На обычных элементах свойства `scrollTop/scrollLeft` можно изменять, и при этом элемент будет прокручиваться. -Никто не мешает точно так же поступать и со страницей. Во всех браузерах, кроме Chrome/Safari/Opera можно осуществить прокрутку установкой `document.documentElement.scrollTop`, а в указанных -- использовать для этого `document.body.scrollTop`. И будет работать. +Никто не мешает точно так же поступать и со страницей. Во всех браузерах, кроме 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). +Но есть и другое, простое и универсальное решение -- специальные методы прокрутки страницы [window.scrollBy(x,y)](https://developer.mozilla.org/en/Window.scrollBy) и [window.scrollTo(pageX,pageY)](https://developer.mozilla.org/en/Window.scrollTo).