completefrontend
Open lab My progress
THE JAVASCRIPT FIELD GUIDE

Sizes, scrolling & coordinates

Measure element boxes, read viewport coordinates, control scrolling, handle scroll events safely, and find what is under a pointer.

By the end, you can
  • 01
    Measure real boxesPick the offset, client, or scroll size that fits the job.
  • 02
    Change positionsTurn a position in the viewport into a position on the whole page.
  • 03
    Scroll smoothlyScroll with code, react to scrolling without slowing the page, and find what is under a point.

How big is a box, and where is it?

Web pages often ask questions about real space on the screen:

  • How wide is this card?
  • How far has this list scrolled?
  • Where is this button on the screen?
  • Which element is under the mouse pointer right now?

The browser already knows the answers. Before it draws a page, it works out the size and place of every element. This work is called layout. In this lesson you learn to read the browser's answers instead of guessing them.

The answers come in two groups:

  • Sizes tell you how big an element's box is. The box has layers: the content, the padding around the content, and the border around the padding.
  • Coordinates tell you where a box or a point is. A coordinate is a distance in pixels from a starting edge, such as the top of the screen.

Many layout bugs come from mixing up these two groups.

The definition

Size properties answer "How big is this element?" Coordinate methods answer "Where is this element, or this point?" Scrolling links the two: it changes which part of a long page you can see.

Real-life analogyA measured box is like a framed picture

Imagine a picture hanging on a wall. Measure the whole framed picture, and you include the wooden frame. Measure inside the frame, and you get the mat and the picture, but not the frame. Now imagine the painting is very long, and most of it is rolled up behind the frame. The part you can see is smaller than the whole painting.

In real life: The picture
In JavaScript: the content
In real life: The paper mat around the picture
In JavaScript: the padding
In real life: The wooden frame
In JavaScript: the border
In real life: The empty wall around the frame
In JavaScript: the margin, which these sizes never count
In real life: The whole framed picture
In JavaScript: offsetWidth and offsetHeight
In real life: Everything inside the wooden frame
In JavaScript: clientWidth and clientHeight
In real life: A long painting rolled up behind the frame
In JavaScript: scrollWidth and scrollHeight

In the previous lesson, Styles & classes, you changed how elements look. Now you read the sizes and places that those styles produce. The Event loop lesson showed that the browser draws the page in steps. The same order helps here: change styles first, let the browser do layout, then read all your measurements together.

Three sizes: offset, client, and scroll

INTERACTIVE

Every element has three groups of size properties. The first word of each name tells you which part of the box it measures:

  • offsetWidth and offsetHeight measure the whole box: content, padding, and border. If the box has a scrollbar, they count it too. They are always whole numbers, and they ignore transforms. A transform is a CSS effect, such as transform: scale(1.5), that makes an element look bigger or smaller without changing the layout.
  • clientWidth and clientHeight measure inside the border: content and padding. They leave out the border and any scrollbar. clientTop and clientLeft are usually the widths of the top and left borders.
  • scrollWidth and scrollHeight measure all of the content, including the parts that stick out and are hidden. scrollTop and scrollLeft tell you how far the box has scrolled. You can also set them to scroll the box.

Try the playground below slowly:

  1. Turn on the padding. Both the offset sizes and the client sizes grow.
  2. Turn on the border. The offset sizes grow again, but the client sizes do not.
  3. Turn on the overflow. scrollHeight grows, because the hidden content still counts.
Border and hidden width from three sizesPop out in the code editor (opens in a new tab)JavaScript
const card = { offsetWidth: 220, clientWidth: 204, scrollWidth: 260 };
console.log(card.offsetWidth - card.clientWidth);
console.log(card.scrollWidth - card.clientWidth);

This small example uses numbers that were already measured, so it needs no real element.

  • Line 2 takes the inside width away from the whole width: 220 − 204 prints 16. Those 16 pixels are the border, plus a scrollbar if there is one.
  • Line 3 takes the inside width away from the width of all the content: 260 − 204 prints 56. Those 56 pixels of content stick out of view.
A box with a border around its content and padding, and hidden content sticking out to the right. offsetWidth, 220, spans the whole box with its border. clientWidth, 204, spans only the inside. scrollWidth, 260, spans the inside plus the hidden content.offsetWidth: 220content + paddingborderhiddenout of viewclientWidth: 204scrollWidth: 260
offsetWidth counts the border; clientWidth stops inside it. scrollWidth also counts the content that sticks out of view.
Box measurer: change the box and read the sizes
Is the box at the bottom?Pop out in the code editor (opens in a new tab)JavaScript
const hiddenBelow = element.scrollHeight - element.scrollTop - element.clientHeight;const atBottom = hiddenBelow <= 1;
Bottom check: scrollTop + clientHeight ≈ scrollHeight0 / 0
Live elementat bottom

Content

offsetWidth
0
offsetHeight
0
clientWidth
0
clientHeight
0
clientTop
0
clientLeft
0
scrollWidth
0
scrollHeight
0
scrollTop
0
Try it yourself

The box is at the bottom (within 1 pixel). scrollTop + clientHeight has reached scrollHeight.

Turn the padding, border, and overflow on and off. The numbers come from the real element in your browser.

Many pages need to know if a scrolling box has reached the bottom. For example, a chat app loads older messages, or a "Back to top" button appears. This code checks it:

Is the box scrolled to the bottom?Pop out in the code editor (opens in a new tab)JavaScript
const hiddenBelow = element.scrollHeight - element.scrollTop - element.clientHeight;const atBottom = hiddenBelow <= 1;
  • Line 1 works out hiddenBelow: the number of pixels still hidden below the part you can see.
  • Line 2 says the box is "at the bottom" when that number is 1 or less.

Why not check for exactly 0? Sizes on the screen can include parts of a pixel, such as 99.5. Some properties round them to whole numbers. So good code asks "Is it within one or two pixels?" instead of "Is it exactly equal?"

A scroll position has limits. It can go from 0 up to scrollHeight - clientHeight. If you set a bigger number, the browser uses the biggest allowed value instead. If you set a negative number, the browser uses 0. So after you set a scroll position, read it again to see where the box really is.

The browser keeps scroll positions in rangePop out in the code editor (opens in a new tab)JavaScript
const maxScroll = 300 - 60;
console.log(Math.min(500, maxScroll));
console.log(Math.max(-20, 0));

This example copies what the browser does. Math.min picks the smaller of two numbers, and Math.max picks the bigger one.

  • Line 1: the box has 300 pixels of content and shows 60 of them, so the biggest scroll position is 240.
  • Line 2 asks for 500 and gets 240.
  • Line 3 asks for -20 and gets 0.

Step through the bottom check

STEP THROUGH

Now step through the math one line at a time. The box has these measurements:

  • scrollHeight is 300: the height of all the content.
  • clientHeight is 100: the height you can see.
  • scrollTop is 199: the box has scrolled 199 pixels down.

So 1 pixel is still hidden. Does that count as "at the bottom"? The tolerance is how many pixels you allow. Pick one, guess the answer, then step through.

The content of a scrolling box, 300 pixels tall in all, split into three parts from top to bottom: 199 pixels scrolled past (scrollTop), 100 pixels on screen (clientHeight), and 1 pixel still below (hiddenBelow).scrollHeight300199scrollTopscrolled pastvisible100clientHeighton screen1hiddenBelowstill below
All 300 pixels of content split into three parts: 199 scrolled past, 100 on screen, and 1 still below. So hiddenBelow is 300 − 199 − 100 = 1.
Step through the bottom check
Step 0 of 4Ready
Your turn: follow the blue line

Guess whether the box is at the bottom. Change the tolerance, then step through the math.

Running in
  1. script
Next: line 1
const hiddenBelow = box.scrollHeight - box.scrollTop - box.clientHeight;const atBottom = hiddenBelow <= 2;console.log(atBottom);
CallStoreChangeResultBlue line: runs next. Ran: already executed.
Recent returnsNothing yet. Start with the blue line.
Choose the tolerance on line 3

Try 0 first, then 2. Real pages often need a small tolerance, because sizes can include parts of a pixel.

A guided replay recorded from real JavaScript calls, not an engine debugger. Step follows executed statements; Back reviews a snapshot. Reset starts a fresh run.
Which measurement is it?
  • offsetWidth
  • offsetHeight
  • clientWidth
  • clientTop
  • scrollWidth
  • scrollHeight
  • rect.top
  • elementFromPoint(x, y)
Try it yourself
0 of 8 correct

Sort each property by what it measures. The names look alike on purpose.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Where an element is: getBoundingClientRect

LIVE READOUT

The viewport is the part of the page you can see in the browser window right now.

element.getBoundingClientRect() tells you where an element is in the viewport. It returns an object called a DOMRect, which holds these numbers:

  • top and left: how far the element's top and left edges are from the top and left of the viewport.
  • bottom and right: how far the element's bottom and right edges are from the top and left of the viewport.
  • width and height: the size of the element's whole box, with content, padding, and border.

Because rect.top is measured from the viewport, it changes when you scroll the page.

Real-life analogyThe viewport is like a car windshield

Look at a road sign through the windshield of a car. As you drive, the sign moves across the glass. But the sign's place on the map stays the same.

getBoundingClientRect() gives the place in the glass. To get the place on the map, add scrollX and scrollY: how far the page has scrolled sideways and down.

In real life: What you see through the windshield
In JavaScript: the viewport
In real life: Where a road sign appears in the glass
In JavaScript: rect.top and rect.left
In real life: Driving forward
In JavaScript: scrolling the page
In real life: The sign’s place on a paper map
In JavaScript: rect.top + scrollY, the place on the whole page
A tall page with a browser window over its middle part, the viewport. A button sits inside the viewport. scrollY is the distance from the top of the page to the top of the viewport. rect.top is the distance from the top of the viewport to the button. rect.top + scrollY is the distance from the top of the page to the button.whole pageviewportbuttonscrollYrect.toprect.top+ scrollY
rect.top is measured from the top of the viewport. Add scrollY, the part of the page scrolled past, to measure from the top of the whole page.

The CSS width you write is not always the width you measure. It depends on the CSS box-sizing property:

  • With content-box (the default), width sets only the content. The padding and border are added outside it.
  • With border-box, width sets the whole box. The padding and border fit inside it.
How box-sizing changes the outer widthPop out in the code editor (opens in a new tab)JavaScript
const sizes = { cssWidth: 200, padding: 20, border: 4 };
const contentBox = sizes.cssWidth + sizes.padding * 2 + sizes.border * 2;
const borderBox = sizes.cssWidth;
const contentWidth = sizes.cssWidth - sizes.padding * 2 - sizes.border * 2;
console.log(contentBox, borderBox, contentWidth);

The example has a CSS width of 200, 20 pixels of padding on each side, and a 4-pixel border on each side.

  • Line 2: with content-box, the outer width is 200 + 40 + 8 = 248.
  • Line 3: with border-box, the outer width stays 200.
  • Line 4: then the content gets only 200 − 40 − 8 = 152.

So the program prints 248 200 152. When CSS rules are involved, measure the real element. Don't work out its width from one line of CSS.

The rectangle can include parts of a pixel, and it includes transforms. Turn on the scale in the playground below. The width from getBoundingClientRect() changes with the scale, but offsetWidth keeps the layout width.

Watch the rectangle while you scroll
From viewport position to page positionPop out in the code editor (opens in a new tab)JavaScript
const rect = target.getBoundingClientRect();const pageTop = rect.top + window.scrollY;
Measured targetnormal
Target box
rect.top
0
rect.left
0
rect.width
0
rect.height
0
pageTop
0
pageLeft
0
offsetWidth
0
Try it yourself

Scroll the page and watch rect.top change. pageTop stays the same, because it adds scrollY. rect.width is 0, and offsetWidth is 0.

The numbers update with requestAnimationFrame, once per frame, so scrolling doesn't make the browser do extra work.

There are two ways to measure a position. Viewport coordinates are measured from the top-left corner of what you can see. Document coordinates are measured from the top-left corner of the whole page. Mouse events give you both: event.clientX and event.clientY are viewport coordinates, and event.pageX and event.pageY are document coordinates.

The table below compares the two. Each row asks a question, then gives the answer for each kind of position.

Viewport or document coordinates: which one to use
QuestionViewport (what you can see)Document (the whole page)
Where is it on my screen?Use rect.top, rect.left, clientX, or clientY.This is the wrong kind for this question.
Where is it on the whole page?Add the scroll amount first.Use rect.top + scrollY, rect.left + scrollX, pageX, or pageY.
Does it change when you scroll?Yes. The numbers change as the page moves.Usually not. It changes only if the layout changes.
What is it good for?Finding what is under the pointer, sticky parts, and things drawn over what you see.Saving positions, placing things on the page, and scrolling to a spot.
Viewport or document coordinates?
  • event.clientX
  • rect.left
  • document.elementFromPoint(clientX, clientY)
  • event.pageY
  • rect.top + scrollY
  • place a pop-up on the page
Try it yourself
0 of 6 correct

Put each value with the kind of position it measures.

Choose a category for every card. You can change an answer at any time; Reset clears them all.

Scrolling with code

INTERACTIVE

Two methods scroll a box or the page:

  • scrollTo moves to an exact position, such as the very top.
  • scrollBy moves by an amount from where you are now, such as 80 pixels down.

Both work on elements and on window, the whole page. For example, window.scrollTo({ top: 0, behavior: "smooth" }) moves the page to the top. The option behavior: "smooth" makes it slide instead of jump. Only scroll the page when the reader asks, for example after they click a button. Moving the page by surprise is confusing.

element.scrollIntoView() is often easier. It asks the browser to scroll until that element can be seen. The option block: "nearest" means "move as little as needed." That works well when a scrolling box sits inside a scrolling page.

Scroll a list with code
Two ways to scrollPop out in the code editor (opens in a new tab)JavaScript
list.scrollBy({ top: 80, behavior: "smooth" });item.scrollIntoView({ behavior: "smooth", block: "nearest" });
Scrolling listsmooth scrolling
Item 1: scroll practice
Item 2: scroll practice
Item 3: scroll practice
Item 4: scroll practice
Item 5: scroll practice
Item 6: scroll practice
Item 7: scroll practice
Item 8: scroll practice
Item 9: scroll practice
Item 10: scroll practice
Item 11: scroll practice
Item 12: scroll practice
Try it yourself

Try the buttons. They scroll only this list, not the whole page.

The same methods work on window for the whole page. Use them only after the reader clicks or asks.
Two different viewport widths

window.innerWidth includes the browser's scrollbar. document.documentElement.clientWidth leaves it out. (document.documentElement is the <html> element.) Use the second one when you want the width that the page can really use.

How wide is the scrollbar?Pop out in the code editor (opens in a new tab)JavaScript
const innerWidth = 1024;
const clientWidth = 1007;
console.log(innerWidth - clientWidth);

In this example, the difference is 17 pixels. Some computers draw the scrollbar on top of the page instead of beside it. Then the difference is smaller, or 0. So measure it; don't assume 17.

Reacting to scrolling

ONCE PER FRAME

When the reader scrolls, the browser fires a scroll event, often many times a second. Heavy work in every event can make the page stutter.

There is one problem to watch for. Suppose each event changes a style, then reads a size, then changes a style, then reads again. Each read after a change makes the browser redo its layout at once. Doing this again and again is called layout thrashing. To avoid it:

  • Do all your reads together.
  • Then do all your writes together.
  • Use requestAnimationFrame for changes on the screen. It runs your function once, just before the browser draws the next frame (the next picture of the page).
Why reading a size can be slow

To give a correct answer, a read such as offsetWidth or getBoundingClientRect() can make the browser finish any style changes first. One read is fine. The problem is switching between writes and reads many times in a loop.

Scroll events vs. screen updates
Update the screen once per framePop out in the code editor (opens in a new tab)JavaScript
let ticking = false;box.addEventListener("scroll", () => {  if (!ticking) {    requestAnimationFrame(updateReadout);    ticking = true;  }}, { passive: true });
Event counterchecking

Scroll row 1

Scroll row 2

Scroll row 3

Scroll row 4

Scroll row 5

Scroll row 6

Scroll row 7

Scroll row 8

Scroll row 9

Scroll row 10

Scroll row 11

Scroll row 12

Scroll row 13

Scroll row 14

Scroll row 15

Scroll row 16

Scroll row 17

Scroll row 18

scroll events
0
screen updates
0
scrollend events
0
Try it yourself
Scroll the box with your trackpad, mouse wheel, or keyboard.

One scroll can fire many scroll events. The screen-update counter goes up at most once per frame, so the screen work runs far less often.

Use { passive: true } when the listener never calls preventDefault(). To track when things come into view, use IntersectionObserver; the later Observers lesson covers it.

Add { passive: true } to a scroll listener when it never calls preventDefault(). This tells the browser that your listener won't stop the scroll, so the browser can scroll without waiting for it.

To find out when a card comes into view, use IntersectionObserver instead. The later Observers lesson covers it. Newer browsers also have a scrollend event, which fires once when scrolling stops. Before you use it, check that the browser has it with "onscrollend" in document.

What is under a point: elementFromPoint

HIT TEST

document.elementFromPoint(x, y) asks the browser: "Which element is on top at this point of the viewport?" It uses viewport coordinates, the same kind as clientX and clientY from mouse events. If the point is outside the viewport, it returns null.

Real-life analogyelementFromPoint is like a finger on the screen

Put a finger on a phone screen covered in stickers and ask: "What is right under my finger?" You get the sticker on top, not the ones under it.

In real life: Your fingertip
In JavaScript: the x, y point in the viewport
In real life: The top sticker under your finger
In JavaScript: the element it returns
In real life: A finger off the edge of the screen
In JavaScript: null

Finding the element under a point is called hit testing. Drag-and-drop tools and page inspectors use it.

What is under my finger?
Find the element under the pointerPop out in the code editor (opens in a new tab)JavaScript
area.addEventListener("pointermove", (event) => {  const hit = document.elementFromPoint(event.clientX, event.clientY);});
Pointer areatopmost element
card A
card B overlaps
Try it yourself
The demo passes the pointer's clientX and clientY.

Move the pointer inside the area.

elementFromPoint returns the element on top at a point in the viewport, or null if the point is outside the viewport.

Where you’ll use this

You will meet these measurements in everyday pages:

  • A tooltip (a small pop-up label) reads its button's rectangle and places itself next to the button.
  • A chat app checks if the message list is near the bottom before it scrolls to a new message.
  • A very long list draws only the rows you can see. It uses clientHeight and scrollTop to pick them.
  • A drawing tool uses elementFromPoint to find the shape under the pointer.
Place a tooltip under a buttonJavaScript
const rect = button.getBoundingClientRect();const top = rect.bottom + window.scrollY;const left = rect.left + window.scrollX; tooltip.style.transform = `translate(${left}px, ${top}px)`;
  • Line 1 reads the button's rectangle in the viewport.
  • Lines 2 and 3 add the scroll amounts to get positions on the whole page.
  • Line 5 moves the tooltip there with one style change.

This code needs real elements, so it doesn't run here. The next example does the same math with numbers.

The tooltip math with numbersPop out in the code editor (opens in a new tab)JavaScript
const button = { bottom: 120, left: 40 };
const page = { scrollX: 10, scrollY: 300 };
console.log(button.left + page.scrollX, button.bottom + page.scrollY);
  • The button's bottom is 120 pixels below the top of the viewport, and the page has scrolled 300 pixels down. So on the whole page, the tooltip's top is 120 + 300 = 420.
  • The button's left edge is 40 pixels from the left of the viewport, and the page has scrolled 10 pixels sideways. So on the whole page, its left is 40 + 10 = 50.

Line 3 prints 50 420.

Is a card in view?Pop out in the code editor (opens in a new tab)JavaScript
const card = { top: -20, right: 300, bottom: 80, left: 40 };
const viewport = { width: 800, height: 600 };
const inView = card.bottom > 0 && card.right > 0 && card.top < viewport.height && card.left < viewport.width;
console.log(inView);

Line 3 checks four things. The card is in view only if all four are true:

  • Its bottom is below the top edge of the viewport: card.bottom > 0.
  • Its right edge is past the left edge of the viewport: card.right > 0.
  • Its top is above the bottom edge of the viewport: card.top < viewport.height.
  • Its left edge is before the right edge of the viewport: card.left < viewport.width.

This card starts 20 pixels above the viewport, but its bottom is 80 pixels below the top edge. So part of it can still be seen, and the program prints true. If you checked only the top, you would hide a card that is still partly on the screen.

Notice the order in the tooltip code: read the rectangle, work out the page position, then make one style change. If you need several measurements, read them all first. Then change the classes or styles. That stops the browser from redoing layout again and again.

Misconceptions and edge cases

  • "offsetWidth and getBoundingClientRect().width are the same." Not always. offsetWidth is a whole number and ignores transforms. getBoundingClientRect() can include parts of a pixel, and it includes transforms.
  • "clientWidth is the content width." It also includes the padding. It leaves out the border and any scrollbar.
  • "rect.top is a fixed place on the page." It is measured from the viewport, so it changes when you scroll. Add scrollY to get the place on the whole page.
  • "Reading sizes costs nothing." A read right after a style change can make the browser redo layout at once. Do the reads together, then the writes together.
  • "Hidden elements still have a size." An element with display: none takes up no space. Its sizes are 0, and its offsetParent is null. offsetParent is the parent element that offsetTop and offsetLeft are measured from.
  • "elementFromPoint uses pageX and pageY." It uses viewport coordinates. Pass it clientX and clientY.

Practice exercises

5 EXERCISES
Exercise 1 · Warm-upPredict the bottom check

Do the math in your head. What does the program print?

Starter codePop out in the code editor (opens in a new tab)JavaScript
const box = { scrollTop: 240, clientHeight: 60, scrollHeight: 300 };
const hiddenBelow = box.scrollHeight - box.scrollTop - box.clientHeight;
console.log(hiddenBelow <= 1);

Answer, then press Check. Spacing and letter case don’t matter.

    Exercise 2 · PracticeTurn a viewport top into a page top

    A target is 42 pixels below the top of the viewport. The page has scrolled 300 pixels down. How far is the target from the top of the whole page?

    Starter codePop out in the code editor (opens in a new tab)JavaScript
    const rect = { top: 42, left: 10 };
    const scrollY = 300;
    console.log(rect.top + scrollY);

    Answer, then press Check. Spacing and letter case don’t matter.

      Exercise 3 · PracticeFind the width of the scrollbar

      Use the difference between window.innerWidth and document.documentElement.clientWidth.

      Starter codePop out in the code editor (opens in a new tab)JavaScript
      const windowInnerWidth = 1024;
      const documentClientWidth = 1007;
      console.log(windowInnerWidth - documentClientWidth);

      Answer, then press Check. Spacing and letter case don’t matter.

        Exercise 4 · PracticeFix the position bug

        After the page scrolls, the popup appears too high. Fix the numbers so it sits under the button.

        Starter codePop out in the code editor (opens in a new tab)JavaScript
        const rect = menuButton.getBoundingClientRect();
        popup.style.top = rect.bottom + "px";
        popup.style.left = rect.left + "px";
          Exercise 5 · ChallengeWrite a light scroll listener

          A sticky header must update while the page scrolls. Write a listener that doesn't do heavy work in every scroll event.

          Starter codePop out in the code editor (opens in a new tab)JavaScript
          let ticking = false;
          window.addEventListener("scroll", () => {
            // your plan here
          });

            Check your understanding

            8 QUESTIONS
            Sizes and coordinates quiz · 8 questionsScore: first tries count
            1. Question 1 of 8Which size includes the width of the border?

              Choose an answer to see the explanation.

            2. Question 2 of 8What does this print?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const box = { scrollTop: 90, clientHeight: 100, scrollHeight: 200 };
              const hiddenBelow = box.scrollHeight - box.scrollTop - box.clientHeight;
              console.log(hiddenBelow);

              Choose an answer to see the explanation.

            3. Question 3 of 8getBoundingClientRect() measures positions from where?

              Choose an answer to see the explanation.

            4. Question 4 of 8This prints the target's top on the whole page. What is it?

              Read the code, then predictPop out in the code editor (opens in a new tab)JavaScript
              const rect = { top: 25 };
              const scrollY = 400;
              console.log(rect.top + scrollY);

              Choose an answer to see the explanation.

            5. Question 5 of 8Which call asks the browser to scroll until an element can be seen?

              Choose an answer to see the explanation.

            6. Question 6 of 8What is the best way to do heavy screen updates while the page scrolls?

              Choose an answer to see the explanation.

            7. Question 7 of 8What kind of point does elementFromPoint(x, y) expect?

              Choose an answer to see the explanation.

            8. Question 8 of 8Which sentence about transforms is true?

              Choose an answer to see the explanation.

            Key takeaways

            • offsetWidth and offsetHeight measure the whole box with its border, as whole numbers. They ignore transforms.
            • clientWidth and clientHeight measure inside the border. scrollWidth and scrollHeight measure all the content, even the hidden part.
            • getBoundingClientRect() tells you where the box is in the viewport. It can include parts of a pixel, and it includes transforms.
            • A position on the whole page = a position in the viewport + scrollX or scrollY.
            • Do layout reads together and writes together. Update the screen once per frame with requestAnimationFrame.
            • elementFromPoint takes viewport coordinates and returns the element on top at that point.

            Sizes tell you how big a box is. Coordinates tell you where a box or a point is, measured from the viewport or from the whole page.

            Up next: Selection & Range.

            CompleteFrontend Clear concepts. Working examples.