diff --git a/2-ui/1-document/11-coordinates/2-position-at/source.view/index.html b/2-ui/1-document/11-coordinates/2-position-at/source.view/index.html index 730f815d9..675573450 100755 --- a/2-ui/1-document/11-coordinates/2-position-at/source.view/index.html +++ b/2-ui/1-document/11-coordinates/2-position-at/source.view/index.html @@ -41,7 +41,13 @@ * relative to the anchor element. */ function showNote(anchor, position, html) { - // ... your code ... + + let note = document.createElement('div'); + note.className = "note"; + note.innerHTML = html; + document.body.append(note); + + positionAt(anchor, position, note); } // test it diff --git a/2-ui/1-document/11-coordinates/2-position-at/task.md b/2-ui/1-document/11-coordinates/2-position-at/task.md index 1d4f06771..3aaa47f03 100644 --- a/2-ui/1-document/11-coordinates/2-position-at/task.md +++ b/2-ui/1-document/11-coordinates/2-position-at/task.md @@ -4,12 +4,15 @@ importance: 5 # Show a note near the element -Create a function `positionAt(anchor, position, elem)` that positions `elem`, depending on `position` either at the top (`"top"`), right (`"right"`) or bottom (`"bottom"`) of the element `anchor`. +Create a function `positionAt(anchor, position, elem)` that positions `elem`, depending on `position` near `anchor` element. -Call it inside the function `showNote(anchor, position, html)` that shows an element with the class `"note"` and the text `html` at the given position near the anchor. +The `position` must be a string with any one of 3 values: +- `"top"` - position `elem` right above `anchor` +- `"right"` - position `elem` immediately at the right of `anchor` +- `"bottom"` - position `elem` right below `anchor` -Show the notes like here: +It's used inside function `showNote(anchor, position, html)`, provided in the task source code, that creates a "note" element with given `html` and shows it at the given `position` near the `anchor`. -[iframe src="solution" height="350" border="1" link] +Here's the demo of notes: -P.S. The note should have `position:fixed` for this task. +[iframe src="solution" height="350" border="1" link] diff --git a/2-ui/1-document/11-coordinates/article.md b/2-ui/1-document/11-coordinates/article.md index b45a152a1..4775ff0eb 100644 --- a/2-ui/1-document/11-coordinates/article.md +++ b/2-ui/1-document/11-coordinates/article.md @@ -4,56 +4,68 @@ To move elements around we should be familiar with coordinates. Most JavaScript methods deal with one of two coordinate systems: -1. Relative to the window(or another viewport) top/left. -2. Relative to the document top/left. +1. **Relative to the window** - similar to `position:fixed`, calculated from the window top/left edge. + - we'll denote these coordinates as `clientX/clientY`, the reasoning for such name will become clear later, when we study event properties. +2. **Relative to the document** - similar to `position:absolute` in the document root, calculated from the document top/left edge. + - we'll denote them `pageX/pageY`. -It's important to understand the difference and which type is where. +When the page is scrolled to the very beginning, so that the top/left corner of the window is exactly the document top/left corner, these coordinates equal each other. But after the document shifts, window-relative coordinates of elements change, as elements move across the window, while document-relative coordinates remain the same. -## Window coordinates: getBoundingClientRect +On this picture we take a point in the document and demonstrate its coordinates before the scroll (left) and after it (right): -Window coordinates start at the upper-left corner of the window. +![](document-and-window-coordinates-scrolled.svg) -The method `elem.getBoundingClientRect()` returns window coordinates for `elem` as an object with properties: +When the document scrolled: +- `pageY` - document-relative coordinate stayed the same, it's counted from the document top (now scrolled out). +- `clientY` - window-relative coordinate did change (the arrow became shorter), as the same point became closer to window top. -- `top` -- Y-coordinate for the top element edge, -- `left` -- X-coordinate for the left element edge, -- `right` -- X-coordinate for the right element edge, -- `bottom` -- Y-coordinate for the bottom element edge. +## Element coordinates: getBoundingClientRect -Like this: +The method `elem.getBoundingClientRect()` returns window coordinates for a minimal rectangle that encloses `elem` as an object of built-in [DOMRect](https://www.w3.org/TR/geometry-1/#domrect) class. -![](coords.png) +Main `DOMRect` properties: +- `x/y` -- X/Y-coordinates of the rectangle origin relative to window, +- `width/height` -- width/height of the rectangle (can be negative). -Window coordinates do not take the scrolled out part of the document into account, they are calculated from the window's upper-left corner. +Additionally, there are derived properties: -In other words, when we scroll the page, the element goes up or down, *its window coordinates change*. That's very important. +- `top/bottom` -- Y-coordinate for the top/bottom rectangle edge, +- `left/right` -- X-coordinate for the left/right rectangle edge. ```online -Click the button to see its window coordinates: +For instance click this button to see its window coordinates: - +

-If you scroll the page, the button position changes, and window coordinates as well. +If you scroll the page and repeat, you'll notice that as window-relative button position changes, its window coordinates (`y/top/bottom` if you scroll vertically) change as well. ``` -Also: +Here's the picture of `elem.getBoundingClientRect()` output: -- Coordinates may be decimal fractions. That's normal, internally browser uses them for calculations. We don't have to round them when setting to `style.position.left/top`, the browser is fine with fractions. -- Coordinates may be negative. For instance, if the page is scrolled down and the top `elem` is now above the window. Then, `elem.getBoundingClientRect().top` is negative. -- Some browsers (like Chrome) provide additional properties, `width` and `height` of the element that invoked the method to `getBoundingClientRect` as the result. We can also get them by subtraction: `height=bottom-top`, `width=right-left`. +![](coordinates.svg) -```warn header="Coordinates right/bottom are different from CSS properties" -If we compare window coordinates versus CSS positioning, then there are obvious similarities to `position:fixed`. The positioning of an element is also relative to the viewport. +As you can see, `x/y` and `width/height` fully describe the rectangle. Derived properties can be easily calculated from them: -But in CSS, the `right` property means the distance from the right edge, and the `bottom` property means the distance from the bottom edge. +- `left = x` +- `top = y` +- `right = x + width` +- `bottom = y + height` Please note: @@ -119,8 +131,6 @@ The method `document.elementFromPoint(x,y)` only works if `(x,y)` are inside the If any of the coordinates is negative or exceeds the window width/height, then it returns `null`. -In most cases such behavior is not a problem, but we should keep that in mind. - Here's a typical error that may occur if we don't check for it: ```js @@ -132,11 +142,11 @@ elem.style.background = ''; // Error! ``` ```` -## Using for position:fixed +## Using for "fixed" positioning -Most of time we need coordinates to position something. In CSS, to position an element relative to the viewport we use `position:fixed` together with `left/top` (or `right/bottom`). +Most of time we need coordinates in order to position something. -We can use `getBoundingClientRect` to get coordinates of an element, and then to show something near it. +To show something near an element, we can use `getBoundingClientRect` to get its coordinates, and then CSS `position` together with `left/top` (or `right/bottom`). For instance, the function `createMessageUnder(elem, html)` below shows the message under `elem`: @@ -183,7 +193,7 @@ The reason is obvious: the message element relies on `position:fixed`, so it rem To change that, we need to use document-based coordinates and `position:absolute`. -## Document coordinates +## Document coordinates [#getCoords] Document-relative coordinates start from the upper-left corner of the document, not the window. @@ -191,24 +201,6 @@ In CSS, window coordinates correspond to `position:fixed`, while document coordi We can use `position:absolute` and `top/left` to put something at a certain place of the document, so that it remains there during a page scroll. But we need the right coordinates first. -For clarity we'll call window coordinates `(clientX,clientY)` and document coordinates `(pageX,pageY)`. - -When the page is not scrolled, then window coordinate and document coordinates are actually the same. Their zero points match too: - -![](document-window-coordinates-zero.png) - -And if we scroll it, then `(clientX,clientY)` change, because they are relative to the window, but `(pageX,pageY)` remain the same. - -Here's the same page after the vertical scroll: - -![](document-window-coordinates-scroll.png) - -- `clientY` of the header `"From today's featured article"` became `0`, because the element is now on window top. -- `clientX` didn't change, as we didn't scroll horizontally. -- `pageX` and `pageY` coordinates of the element are still the same, because they are relative to the document. - -## Getting document coordinates [#getCoords] - There's no standard method to get the document coordinates of an element. But it's easy to write it. The two coordinate systems are connected by the formula: @@ -231,6 +223,26 @@ function getCoords(elem) { } ``` +If in the example above we used it with `position:absolute`, then the message would stay near the element on scroll. + +The modified `createMessageUnder` function: + +```js +function createMessageUnder(elem, html) { + let message = document.createElement('div'); + message.style.cssText = "*!*position:absolute*/!*; color: red"; + + let coords = *!*getCoords(elem);*/!* + + message.style.left = coords.left + "px"; + message.style.top = coords.bottom + "px"; + + message.innerHTML = html; + + return message; +} +``` + ## Summary Any point on the page has coordinates: diff --git a/2-ui/1-document/11-coordinates/coords.png b/2-ui/1-document/11-coordinates/coords.png deleted file mode 100644 index 79d1fdbb6..000000000 Binary files a/2-ui/1-document/11-coordinates/coords.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/coords@2x.png b/2-ui/1-document/11-coordinates/coords@2x.png deleted file mode 100644 index d3e340f50..000000000 Binary files a/2-ui/1-document/11-coordinates/coords@2x.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png deleted file mode 100644 index 29c59aad1..000000000 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png b/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png deleted file mode 100644 index 3c188ee45..000000000 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-scroll@2x.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png b/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png deleted file mode 100644 index 3dc17f025..000000000 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-zero.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png b/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png deleted file mode 100644 index 45f6b9e7f..000000000 Binary files a/2-ui/1-document/11-coordinates/document-window-coordinates-zero@2x.png and /dev/null differ diff --git a/2-ui/1-document/11-coordinates/head.html b/2-ui/1-document/11-coordinates/head.html index d5b366a9d..c00340039 100644 --- a/2-ui/1-document/11-coordinates/head.html +++ b/2-ui/1-document/11-coordinates/head.html @@ -1,30 +1,30 @@