Размеры и прокрутка окна

Forbid the scrolling

Sometimes we need to make the document “unscrollable”. For instance, when we need to cover it with a large message requiring immediate attention, and we want the visitor to interact with that message, not with the document.


To make the document unscrollable, it’s enough to set . The page will freeze on its current scroll.

Try it:

document.body.style.overflow = ‘hidden’

document.body.style.overflow = ‘’

The first button freezes the scroll, the second one resumes it.

We can use the same technique to “freeze” the scroll for other elements, not just for .

The drawback of the method is that the scrollbar disappears. If it occupied some space, then that space is now free, and the content “jumps” to fill it.

That looks a bit odd, but can be worked around if we compare before and after the freeze, and if it increased (the scrollbar disappeared) then add to in place of the scrollbar, to keep the content width the same.

Ширина/высота страницы с учётом прокрутки

Теоретически, видимая часть страницы – это , а полный размер с учётом прокрутки – по аналогии, .

Это верно для обычных элементов.

А вот для страницы с этими свойствами возникает проблема, когда прокрутка то есть, то нет. В этом случае они работают некорректно. В браузерах Chrome/Safari и Opera при отсутствии прокрутки значение в этом случае может быть даже меньше, чем , что, конечно же, выглядит как совершеннейшая чепуха и нонсенс.

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


Надёжно определить размер страницы с учётом прокрутки можно, взяв максимум из нескольких свойств:

Почему так? Лучше и не спрашивайте, это одно из редких мест, где просто ошибки в браузерах. Глубокой логики здесь нет.

offsetWidth/Height

Теперь переходим к самому элементу.

Эти два свойства – самые простые. Они содержат «внешнюю» ширину/высоту элемента, то есть его полный размер, включая рамки .

Для нашего элемента:

  • – внешняя ширина блока, её можно получить сложением CSS-ширины (, но её часть на рисунке выше отнимает прокрутка, поэтому ), полей() и рамок ().
  • – внешняя высота блока.

Метрики для невидимых элементов равны нулю.

Координаты и размеры в JavaScript устанавливаются только для видимых элементов.

Для элементов с или находящихся вне документа дерево рендеринга не строится. Для них метрики равны нулю. Кстати, и для таких элементов тоже .

Это даёт нам замечательный способ для проверки, виден ли элемент:

  • Работает, даже если родителю элемента установлено свойство .
  • Работает для всех элементов, кроме , с которым возникают некоторые проблемы в разных браузерах. Обычно, проверяются не , поэтому всё ок.
  • Считает элемент видимым, даже если позиционирован за пределами экрана или имеет свойство .
  • «Схлопнутый» элемент, например пустой без высоты и ширины, будет считаться невидимым.

Являются ли они адаптивными?

Если нам нужно реализовать адаптивные эффекты прокрутки, необходимо определить следующие свойства:

  1. Свойство окна браузера ;
  2. Свойство окна браузера .

Без этих свойств наши эффекты прокрутки будут “статичными”, и не будут правильно работать при изменении пользователем размера окна по вертикали или по горизонтали.


Эти значения легко можно получить с помощью jQuery методов width() и height().

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

$(window).scroll(function() {
    if ($(this).width() < 992) {
        if ($(this).height() <= 768) {
            if ($(this).scrollTop() < 500) {
                // apply effects
            }
            if($(this).scrollTop() > 1000) {
            // apply effects
            }
        }
    }
});

Теперь, когда мы определились с основами эффектов прокрутки, давайте посмотрим на них в действии на основе четырех примеров.

Для упрощения мы фокусируемся только на проверке того, как эффекты будут меняться в зависимости от различных значений свойств окна и .

Не стоит брать width/height из CSS

Мы рассмотрели метрики, которые есть у DOM-элементов, и которые можно использовать для получения различных высот, ширин и прочих расстояний.

Но как мы знаем из главы Стили и классы, CSS-высоту и ширину можно извлечь, используя .

Так почему бы не получать, к примеру, ширину элемента при помощи , вот так?

Почему мы должны использовать свойства-метрики вместо этого? На то есть две причины:

  1. Во-первых, CSS-свойства зависят от другого свойства – , которое определяет, «что такое», собственно, эти CSS-ширина и высота. Получается, что изменение , к примеру, для более удобной вёрстки, сломает такой JavaScript.

  2. Во-вторых, в CSS свойства могут быть равны , например, для инлайнового элемента:

    Конечно, с точки зрения CSS – совершенно нормально, но нам-то в JavaScript нужен конкретный размер в , который мы могли бы использовать для вычислений. Получается, что в данном случае ширина из CSS вообще бесполезна.

Есть и ещё одна причина: полоса прокрутки. Бывает, без полосы прокрутки код работает прекрасно, но стоит ей появиться, как начинают проявляться баги. Так происходит потому, что полоса прокрутки «отъедает» место от области внутреннего содержимого в некоторых браузерах. Таким образом, реальная ширина содержимого меньше CSS-ширины. Как раз это и учитывают свойства .


…Но с ситуация иная. Некоторые браузеры (например, Chrome) возвращают реальную внутреннюю ширину с вычетом ширины полосы прокрутки, а некоторые (например, Firefox) – именно CSS-свойство (игнорируя полосу прокрутки). Эти кроссбраузерные отличия – ещё один повод не использовать , а использовать свойства-метрики.

Если ваш браузер показывает полосу прокрутки (например, под Windows почти все браузеры так делают), то вы можете протестировать это сами, нажав на кнопку в ифрейме ниже.

У элемента с текстом в стилях указано CSS-свойство .

На ОС Windows браузеры Firefox, Chrome и Edge резервируют место для полосы прокрутки. Но Firefox отображает , в то время как Chrome и Edge – меньше. Это из-за того, что Firefox возвращает именно CSS-ширину, а остальные браузеры – «реальную» ширину за вычетом прокрутки.

Обратите внимание: описанные различия касаются только чтения свойства из JavaScript, визуальное отображение корректно в обоих случаях

License

(The MIT License)

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Синтаксис

var x = window.scrollX;

Value

Возвращаемое значение является числом с плавающей точкой двойной точности, показывающее количество пикселей в документе, которые были пролистаны на данный момент от начальной позиции, где положительное значение означает, что контент был пролистан налево. Если документ был просматривается на устройстве с возможностью вычислять точное число субпикселей, то возвращаемое значение также является точным количеством субпикселей и может иметь десятичную часть. Если контент не был пролистан ни в одну из сторон по X, то  = 0.

Если вам нужно получить целочисленное значение, используйте  для округления.

Говоря технически, то  возвращает X-координату от верхнего края текущего окна просмотра (viewport). Если окна просмотра нет, то возвращаемое значение = 0.

JavaScript

JS Array concat() constructor copyWithin() entries() every() fill() filter() find() findIndex() forEach() from() includes() indexOf() isArray() join() keys() length lastIndexOf() map() pop() prototype push() reduce() reduceRight() reverse() shift() slice() some() sort() splice() toString() unshift() valueOf()

JS Boolean constructor prototype toString() valueOf()

JS Classes constructor() extends static super

JS Date constructor getDate() getDay() getFullYear() getHours() getMilliseconds() getMinutes() getMonth() getSeconds() getTime() getTimezoneOffset() getUTCDate() getUTCDay() getUTCFullYear() getUTCHours() getUTCMilliseconds() getUTCMinutes() getUTCMonth() getUTCSeconds() now() parse() prototype setDate() setFullYear() setHours() setMilliseconds() setMinutes() setMonth() setSeconds() setTime() setUTCDate() setUTCFullYear() setUTCHours() setUTCMilliseconds() setUTCMinutes() setUTCMonth() setUTCSeconds() toDateString() toISOString() toJSON() toLocaleDateString() toLocaleTimeString() toLocaleString() toString() toTimeString() toUTCString() UTC() valueOf()

JS Error name message

JS Global decodeURI() decodeURIComponent() encodeURI() encodeURIComponent() escape() eval() Infinity isFinite() isNaN() NaN Number() parseFloat() parseInt() String() undefined unescape()

JS JSON parse() stringify()

JS Math abs() acos() acosh() asin() asinh() atan() atan2() atanh() cbrt() ceil() cos() cosh() E exp() floor() LN2 LN10 log() LOG2E LOG10E max() min() PI pow() random() round() sin() sqrt() SQRT1_2 SQRT2 tan() tanh() trunc()

JS Number constructor isFinite() isInteger() isNaN() isSafeInteger() MAX_VALUE MIN_VALUE NEGATIVE_INFINITY NaN POSITIVE_INFINITY prototype toExponential() toFixed() toLocaleString() toPrecision() toString() valueOf()

JS OperatorsJS RegExp constructor compile() exec() g global i ignoreCase lastIndex m multiline n+ n* n? n{X} n{X,Y} n{X,} n$ ^n ?=n ?!n source test() toString() (x|y) . \w \W \d \D \s \S \b \B \0 \n \f \r \t \v \xxx \xdd \uxxxx

JS Statements break class continue debugger do…while for for…in for…of function if…else return switch throw try…catch var while

JS String charAt() charCodeAt() concat() constructor endsWith() fromCharCode() includes() indexOf() lastIndexOf() length localeCompare() match() prototype repeat() replace() search() slice() split() startsWith() substr() substring() toLocaleLowerCase() toLocaleUpperCase() toLowerCase() toString() toUpperCase() trim() valueOf()

JavaScript

JS Array concat() constructor copyWithin() entries() every() fill() filter() find() findIndex() forEach() from() includes() indexOf() isArray() join() keys() length lastIndexOf() map() pop() prototype push() reduce() reduceRight() reverse() shift() slice() some() sort() splice() toString() unshift() valueOf()

JS Boolean constructor prototype toString() valueOf()

JS Classes constructor() extends static super

JS Date constructor getDate() getDay() getFullYear() getHours() getMilliseconds() getMinutes() getMonth() getSeconds() getTime() getTimezoneOffset() getUTCDate() getUTCDay() getUTCFullYear() getUTCHours() getUTCMilliseconds() getUTCMinutes() getUTCMonth() getUTCSeconds() now() parse() prototype setDate() setFullYear() setHours() setMilliseconds() setMinutes() setMonth() setSeconds() setTime() setUTCDate() setUTCFullYear() setUTCHours() setUTCMilliseconds() setUTCMinutes() setUTCMonth() setUTCSeconds() toDateString() toISOString() toJSON() toLocaleDateString() toLocaleTimeString() toLocaleString() toString() toTimeString() toUTCString() UTC() valueOf()

JS Error name message

JS Global decodeURI() decodeURIComponent() encodeURI() encodeURIComponent() escape() eval() Infinity isFinite() isNaN() NaN Number() parseFloat() parseInt() String() undefined unescape()

JS JSON parse() stringify()

JS Math abs() acos() acosh() asin() asinh() atan() atan2() atanh() cbrt() ceil() cos() cosh() E exp() floor() LN2 LN10 log() LOG2E LOG10E max() min() PI pow() random() round() sin() sqrt() SQRT1_2 SQRT2 tan() tanh() trunc()

JS Number constructor isFinite() isInteger() isNaN() isSafeInteger() MAX_VALUE MIN_VALUE NEGATIVE_INFINITY NaN POSITIVE_INFINITY prototype toExponential() toFixed() toLocaleString() toPrecision() toString() valueOf()

JS OperatorsJS RegExp constructor compile() exec() g global i ignoreCase lastIndex m multiline n+ n* n? n{X} n{X,Y} n{X,} n$ ^n ?=n ?!n source test() toString() (x|y) . \w \W \d \D \s \S \b \B \0 \n \f \r \t \v \xxx \xdd \uxxxx

JS Statements break class continue debugger do…while for for…in for…of function if…else return switch throw try…catch var while

JS String charAt() charCodeAt() concat() constructor endsWith() fromCharCode() includes() indexOf() lastIndexOf() length localeCompare() match() prototype repeat() replace() search() slice() split() startsWith() substr() substring() toLocaleLowerCase() toLocaleUpperCase() toLowerCase() toString() toUpperCase() trim() valueOf()

Пример №3

Третий эффект зависит не только от позиции полосы прокрутки, но также и от ширины окна. В частности, есть следующие условия:

  • Ширина окна составляет 549px или менее. В этом случае анимация запускается только в том случае, когда позиция полосы прокрутки превышает 1750px;
  • Ширина окна находится в промежутке между 550px и 991px. В этом случае анимация запускается только тогда, когда положение полосы прокрутки превышает 1150px;
  • Ширина окна больше 991px. В этом случае анимация запускается только когда положение полосы прокрутки превышает 850px.

Код для вышеприведенных случаев представлен ниже:

if ($(window).width() <= 549){
    if($(window).scrollTop() > 1750){
        secondAnimation();
    }
} else if ($(window).width() > 549 && $(window).width() <= 991) {
    if ($(window).scrollTop() > 1150) {
        secondAnimation();
    }
} else {
    if ($(window).scrollTop() > 850) {
        secondAnimation();
    }
}

Код выполняемой анимации выглядит следующим образом:

var secondAnimation = function() {
    $('.method:eq(0)').delay(1000).animate({
                opacity: 1
            }, 'slow',
            function() {
                $(this).find('h4').css('background-color', '#b5c3d5');
        }
    );

    $('.method:eq(1)').delay(2000).animate({
                opacity: 1
            }, 'slow',
            function() {
                $(this).find('h4').css('background-color', '#b5c3d5');
        }
    );

    $('.method:eq(2)').delay(3000).animate({
                opacity: 1
            }, 'slow',
            function() {
                $(this).find('h4').css('background-color', '#b5c3d5');
        }
    );

    $('.method:eq(3)').delay(4000).animate({
                opacity: 1
            }, 'slow',
            function() {
                $(this).find('h4').css('background-color', '#b5c3d5');
        }
    );
};

Вышеприведенный код последовательно изменяет свойство элементов , а затем меняет свойство дочерних элементов .


С этим читают