Skip to content

Window sizes and scrolling #279

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 22, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 69 additions & 69 deletions 2-ui/1-document/10-size-and-scroll-window/article.md
Original file line number Diff line number Diff line change
@@ -1,50 +1,50 @@
# Window sizes and scrolling
# Розміри вікна і прокрутка

How do we find the width and height of the browser window? How do we get the full width and height of the document, including the scrolled out part? How do we scroll the page using JavaScript?
Як визначити ширину та висоту вікна браузера? Як отримати повну ширину та висоту документа, включаючи прокручену частину? Як можна прокрутити сторінку за допомогою JavaScript?

For this type of information, we can use the root document element `document.documentElement`, that corresponds to the `<html>` tag. But there are additional methods and peculiarities to consider.
Для цього ми можемо використовувати кореневий елемент документа `document.documentElement`, який відповідає тегу `<html>`. Але є додаткові методи та особливості, які слід враховувати.

## Width/height of the window
## Ширина/висота вікна

To get window width and height, we can use the `clientWidth/clientHeight` of `document.documentElement`:
Щоб отримати ширину та висоту вікна, ми можемо використовувати `clientWidth/clientHeight` із `document.documentElement`:

![](document-client-width-height.svg)

```online
For instance, this button shows the height of your window:
Наприклад, ця кнопка показує висоту вашого вікна:

<button onclick="alert(document.documentElement.clientHeight)">alert(document.documentElement.clientHeight)</button>
```

````warn header="Not `window.innerWidth/innerHeight`"
Browsers also support properties like `window.innerWidth/innerHeight`. They look like what we want, so why not to use them instead?
````warn header="Не `window.innerWidth/innerHeight`"
Браузери також підтримують властивості `window.innerWidth/innerHeight`. Здається, це те, що нам потрібно, то чому б їх не використати?

If there exists a scrollbar, and it occupies some space, `clientWidth/clientHeight` provide the width/height without it (subtract it). In other words, they return the width/height of the visible part of the document, available for the content.
Якщо є смуга прокрутки, і вона займає деякий простір, то властивості `clientWidth/clientHeight` нададуть ширину/висоту без прокрутки (віднявши її). Інакше кажучи, вони повертають ширину/висоту видимої частини документа, доступної для вмісту.

`window.innerWidth/innerHeight` includes the scrollbar.
`window.innerWidth/innerHeight` включає смугу прокрутки.

If there's a scrollbar, and it occupies some space, then these two lines show different values:
Якщо є смуга прокрутки, і вона займає деякий простір, то ці два рядки покажуть різні значення:
```js run
alert( window.innerWidth ); // full window width
alert( document.documentElement.clientWidth ); // window width minus the scrollbar
alert( window.innerWidth ); // повна ширина вікна
alert( document.documentElement.clientWidth ); // ширина вікна мінус смуга прокрутки
```

In most cases, we need the *available* window width in order to draw or position something within scrollbars (if there are any), so we should use `documentElement.clientHeight/clientWidth`.
У більшості випадків нам потрібна *доступна* ширина вікна, щоб намалювати або розташувати щось у межах смуг прокрутки (якщо такі є), тому ми повинні використовувати `documentElement.clientHeight/clientWidth`.
````

```warn header="`DOCTYPE` is important"
Please note: top-level geometry properties may work a little bit differently when there's no `<!DOCTYPE HTML>` in HTML. Odd things are possible.
```warn header="`DOCTYPE` важливий"
Зверніть увагу: геометричні властивості верхнього рівня можуть працювати дещо інакше, якщо в HTML немає `<!DOCTYPE HTML>`. Можливі дивні речі.

In modern HTML we should always write `DOCTYPE`.
У сучасному HTML ми завжди повинні прописувати `DOCTYPE`.
```

## Width/height of the document
## Ширина/висота документа

Theoretically, as the root document element is `document.documentElement`, and it encloses all the content, we could measure the document's full size as `document.documentElement.scrollWidth/scrollHeight`.
Теоретично, оскільки кореневим елементом документа є `document.documentElement`, і він охоплює весь вміст, ми можемо виміряти повний розмір документа як `document.documentElement.scrollWidth/scrollHeight`.

But on that element, for the whole page, these properties do not work as intended. In Chrome/Safari/Opera, if there's no scroll, then `documentElement.scrollHeight` may be even less than `documentElement.clientHeight`! Weird, right?
Але для цього елемента для всієї сторінки ці властивості не працюють належним чином. У Chrome/Safari/Opera, якщо немає прокрутки, то `documentElement.scrollHeight` може бути навіть меншим, ніж `documentElement.clientHeight`! Дивно, правда?

To reliably obtain the full document height, we should take the maximum of these properties:
Щоб надійно отримати повну висоту документа, ми повинні взяти максимум з цих властивостей:

```js run
let scrollHeight = Math.max(
Expand All @@ -53,110 +53,110 @@ let scrollHeight = Math.max(
document.body.clientHeight, document.documentElement.clientHeight
);

alert('Full document height, with scrolled out part: ' + scrollHeight);
alert('Повна висота документа з прокрученою частиною: ' + scrollHeight);
```

Why so? Better don't ask. These inconsistencies come from ancient times, not a "smart" logic.
Чому так? Краще не питати. Ці невідповідності походять із давніх часів, це не "логічно".

## Get the current scroll [#page-scroll]
## Отримання поточної позиції прокрутки [#page-scroll]

DOM elements have their current scroll state in their `scrollLeft/scrollTop` properties.
Елементи DOM містять свій поточний стан прокрутки у властивостях `scrollLeft/scrollTop`.

For document scroll, `document.documentElement.scrollLeft/scrollTop` works in most browsers, except older WebKit-based ones, like Safari (bug [5991](https://bugs.webkit.org/show_bug.cgi?id=5991)), where we should use `document.body` instead of `document.documentElement`.
Стан прокрутки документа міститься в `document.documentElement.scrollLeft/scrollTop`, та працює в більшості браузерів, за винятком старіших веб-переглядачів створених на WebKit, таких як Safari (помилка [5991](https://bugs.webkit.org/show_bug.cgi?id=5991)), де ми повинні використовувати `document.body` замість `document.documentElement`.

Luckily, we don't have to remember these peculiarities at all, because the scroll is available in the special properties, `window.pageXOffset/pageYOffset`:
На щастя, нам не потрібно пам’ятати про ці особливості, оскільки прокрутка доступна у спеціальних властивостях `window.pageXOffset/pageYOffset`:

```js run
alert('Current scroll from the top: ' + window.pageYOffset);
alert('Current scroll from the left: ' + window.pageXOffset);
alert('Поточна прокрутка зверху: ' + window.pageYOffset);
alert('Поточна прокрутка зліва: ' + window.pageXOffset);
```

These properties are read-only.
Ці властивості доступні лише для читання.

```smart header="Also available as `window` properties `scrollX` and `scrollY`"
For historical reasons, both properties exist, but they are the same:
- `window.pageXOffset` is an alias of `window.scrollX`.
- `window.pageYOffset` is an alias of `window.scrollY`.
```smart header="Також доступні як властивості `window` `scrollX` та `scrollY`"
З історичних причин обидві властивості існують, але вони однакові:
- `window.pageXOffset` є псевдонімом `window.scrollX`.
- `window.pageYOffset` є псевдонімом `window.scrollY`.
```

## Scrolling: scrollTo, scrollBy, scrollIntoView [#window-scroll]
## Прокрутка: scrollTo, scrollBy, scrollIntoView [#window-scroll]

```warn
To scroll the page with JavaScript, its DOM must be fully built.
Щоб прокручувати сторінку за допомогою JavaScript, її DOM має бути повністю створено.

For instance, if we try to scroll the page with a script in `<head>`, it won't work.
Наприклад, якщо ми спробуємо прокрутити сторінку за допомогою сценарію в `<head>`, це не спрацює.
```

Regular elements can be scrolled by changing `scrollTop/scrollLeft`.
Звичайні елементи можна прокручувати, змінюючи `scrollTop/scrollLeft`.

We can do the same for the page using `document.documentElement.scrollTop/scrollLeft` (except Safari, where `document.body.scrollTop/Left` should be used instead).
Ми можемо зробити те ж саме для сторінки, використовуючи `document.documentElement.scrollTop/scrollLeft` (крім Safari, де замість цього слід використовувати `document.body.scrollTop/Left`).

Alternatively, there's a simpler, universal solution: special methods [window.scrollBy(x,y)](mdn:api/Window/scrollBy) and [window.scrollTo(pageX,pageY)](mdn:api/Window/scrollTo).
Крім того, є більш просте та універсальне рішення: спеціальні методи [window.scrollBy(x,y)](mdn:api/Window/scrollBy) і [window.scrollTo(pageX,pageY)](mdn:api/Window/scrollTo) .

- The method `scrollBy(x,y)` scrolls the page *relative to its current position*. For instance, `scrollBy(0,10)` scrolls the page `10px` down.
- Метод `scrollBy(x,y)` прокручує сторінку *відносно її поточної позиції*. Наприклад, `scrollBy(0,10)` прокручує сторінку на `10px` вниз.

```online
The button below demonstrates this:
Кнопка нижче це демонструє:

<button onclick="window.scrollBy(0,10)">window.scrollBy(0,10)</button>
```
- The method `scrollTo(pageX,pageY)` scrolls the page *to absolute coordinates*, so that the top-left corner of the visible part has coordinates `(pageX, pageY)` relative to the document's top-left corner. It's like setting `scrollLeft/scrollTop`.
- Метод `scrollTo(pageX,pageY)` прокручує сторінку *до абсолютних координат*, так що верхній лівий кут видимої частини має координати `(pageX, pageY)` відносно верхнього лівого кута документа. Це те ж саме, що призначити `scrollLeft/scrollTop`.

To scroll to the very beginning, we can use `scrollTo(0,0)`.
Щоб прокрутити до самого початку, ми можемо використовувати `scrollTo(0,0)`.

```online
<button onclick="window.scrollTo(0,0)">window.scrollTo(0,0)</button>
```

These methods work for all browsers the same way.
Ці методи працюють для всіх браузерів однаково.

## scrollIntoView

For completeness, let's cover one more method: [elem.scrollIntoView(top)](mdn:api/Element/scrollIntoView).
Для повноти розглянемо ще один метод: [elem.scrollIntoView(top)](mdn:api/Element/scrollIntoView).

The call to `elem.scrollIntoView(top)` scrolls the page to make `elem` visible. It has one argument:
Виклик `elem.scrollIntoView(top)` прокручує сторінку таким чином, щоб зробити `elem` видимим. Він має один аргумент:

- If `top=true` (that's the default), then the page will be scrolled to make `elem` appear on the top of the window. The upper edge of the element will be aligned with the window top.
- If `top=false`, then the page scrolls to make `elem` appear at the bottom. The bottom edge of the element will be aligned with the window bottom.
- Якщо `top=true` (це значення за замовчуванням), то сторінка буде прокручена, щоб `elem` з'явився у верхній частині вікна. Верхній край елемента буде вирівняний з верхньою частиною вікна.
- Якщо `top=false`, то сторінка прокручується, щоб `elem` з'явився внизу. Нижній край елемента буде вирівняний з нижньою частиною вікна.

```online
The button below scrolls the page to position itself at the window top:
Кнопка нижче прокручує сторінку, щоб розмістити себе у верхній частині вікна:

<button onclick="this.scrollIntoView()">this.scrollIntoView()</button>

And this button scrolls the page to position itself at the bottom:
А ця кнопка прокручує сторінку, щоб розмістити себе у нижній частині вікна:

<button onclick="this.scrollIntoView(false)">this.scrollIntoView(false)</button>
```

## Forbid the scrolling
## Заборона прокручування

Sometimes we need to make the document "unscrollable". For instance, when we need to cover the page 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 `document.body.style.overflow = "hidden"`. The page will "freeze" at its current scroll position.
Щоб зробити документ недоступним для прокручування, достатньо встановити `document.body.style.overflow = "hidden"`. Сторінка «завмере» у поточній позиції прокручування.

```online
Try it:
Спробуй:

<button onclick="document.body.style.overflow = 'hidden'">document.body.style.overflow = 'hidden'</button>

<button onclick="document.body.style.overflow = ''">document.body.style.overflow = ''</button>

The first button freezes the scroll, while the second one releases it.
Перша кнопка зупиняє прокрутку, а друга відновлює.
```

We can use the same technique to freeze the scroll for other elements, not just for `document.body`.
Ми можемо використовувати ту саму техніку, щоб заморозити прокрутку для інших елементів, а не лише для `document.body`.

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 `clientWidth` before and after the freeze. If it increased (the scrollbar disappeared), then add `padding` to `document.body` in place of the scrollbar to keep the content width the same.
Це виглядає трохи дивно, але це можна обійти, якщо порівняти `clientWidth` до і після заборони прокручування. Якщо ширина збільшилась (смуга прокрутки зникла), додайте `padding` до `document.body` замість смуги прокрутки, щоб зберегти ширину вмісту такою ж.

## Summary
## Підсумки

Geometry:
Геометрія:

- Width/height of the visible part of the document (content area width/height): `document.documentElement.clientWidth/clientHeight`
- Width/height of the whole document, with the scrolled out part:
- Ширина/висота видимої частини документа (ширина/висота області вмісту): `document.documentElement.clientWidth/clientHeight`
- Ширина/висота всього документа з прокрученою частиною:

```js
let scrollHeight = Math.max(
Expand All @@ -166,11 +166,11 @@ Geometry:
);
```

Scrolling:
Прокрутка:

- Read the current scroll: `window.pageYOffset/pageXOffset`.
- Change the current scroll:
- Прочитати поточну прокрутку: `window.pageYOffset/pageXOffset`.
- Змінити поточну прокрутку:

- `window.scrollTo(pageX,pageY)` -- absolute coordinates,
- `window.scrollBy(x,y)` -- scroll relative the current place,
- `elem.scrollIntoView(top)` -- scroll to make `elem` visible (align with the top/bottom of the window).
- `window.scrollTo(pageX,pageY)` -- абсолютні координати,
- `window.scrollBy(x,y)` -- прокрутити відносно поточного місця,
- `elem.scrollIntoView(top)` -- прокрутити, щоб зробити `elem` видимим (вирівняти з верхньою/нижньою частиною вікна).