JavaScript Intersection Observer Quiz

JavaScript
0 Passed
0% acceptance

Answer 35 questions covering observers and targets, threshold/rootMargin tuning, lazy-loading patterns, visibility detection, cleanup best practices, entry properties, performance wins vs scroll events, and common use cases like infinite scroll, animations, and analytics.

35 Questions
~70 minutes
1

Question 1

What roles do the observer and target elements play in Intersection Observer?

A
The observer watches for visibility changes of target elements relative to a root
B
The observer defines CSS only while targets are scripts
C
Targets must be iframes
D
Observers mutate DOM automatically
2

Question 2

Why is Intersection Observer preferred over manually listening to scroll events for element visibility?

A
The browser batches visibility computations off the main thread, reducing layout thrash compared to per-scroll calculations
B
Scroll events are deprecated
C
Intersection Observer works only on mobile
D
Scroll events cannot be passive
3

Question 3

What prints?

javascript
const target = document.createElement('div')
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => console.log(entry.isIntersecting))
})
observer.observe(target)
console.log(observer.root)
A
null because root defaults to viewport
B
Document
C
Window
D
undefined
4

Question 4

What does the threshold option represent?

A
The ratio of target visibility at which the callback should fire
B
A pixel offset similar to rootMargin
C
A delay in milliseconds
D
The number of targets allowed
5

Question 5

What prints?

javascript
const observer = new IntersectionObserver(() => {}, {
  rootMargin: '10px 20px',
  threshold: [0, 1]
})
console.log(observer.rootMargin, observer.thresholds)
A
'10px 20px' and [0, 1]
B
'0px' and []
C
undefined and undefined
D
'20px 10px' and [1]
6

Question 6

How does rootMargin influence intersection detection?

A
It expands or contracts the root bounding box before testing visibility, allowing early or delayed triggers
B
It adds padding to targets
C
It changes threshold units from ratios to pixels
D
It disables threshold array
7

Question 7

What prints?

javascript
const img = document.createElement('img')
img.dataset.src = 'photo.jpg'
const observer = new IntersectionObserver((entries, obs) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      entry.target.src = entry.target.dataset.src
      obs.unobserve(entry.target)
      console.log('loaded', entry.target.src)
    }
  })
})
observer.observe(img)
img.getBoundingClientRect = () => ({ top: 0, bottom: 100, left: 0, right: 100, width: 100, height: 100 })
observer.root = document.documentElement
A
Nothing yet because no event loop step triggered callback
B
loaded photo.jpg immediately
C
Error: dataset undefined
D
loaded undefined
8

Question 8

Why unobserve targets after lazy loading images?

A
It frees resources and prevents duplicate network requests once the image is already loaded
B
Observers cannot handle multiple targets
C
unobserve is required before setting src
D
It clears the cache
9

Question 9

What prints?

javascript
const target = document.createElement('div')
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    console.log(entry.time < performance.now())
  })
})
observer.observe(target)
A
Eventually true once the callback fires
B
Always false
C
Throws because entry.time is undefined
D
Immediately true synchronously
10

Question 10

Which entry property reports how much of the target is visible?

A
intersectionRatio
B
targetRatio
C
visibility
D
threshold
11

Question 11

What prints?

javascript
const target = document.createElement('section')
const observer = new IntersectionObserver((entries) => console.log(entries.length))
observer.observe(target)
observer.disconnect()
observer.observe(target)
console.log(observer.takeRecords())
A
[] because takeRecords removes pending notifications even if observer was disconnected
B
Array with entry
C
Throws due to double observe
D
undefined
12

Question 12

Why call disconnect() on component unmount?

A
It prevents memory leaks and avoids stale callbacks when the DOM nodes no longer exist
B
It accelerates intersections
C
It is optional only for rootMargin changes
D
Observers auto-dispose immediately
13

Question 13

What prints?

javascript
const target = document.createElement('div')
const observer = new IntersectionObserver((entries) => {
  const entry = entries[0]
  console.log(entry.boundingClientRect === target.getBoundingClientRect())
})
observer.observe(target)
A
false because boundingClientRect is a snapshot object, not the live DOMRect from getBoundingClientRect()
B
true always
C
Throws due to circular reference
D
undefined
14

Question 14

Which property indicates whether the target is currently intersecting the root?

A
isIntersecting
B
isVisible
C
visibleRatio
D
intersectingRect
15

Question 15

Why does Intersection Observer avoid forced synchronous layout?

A
The browser calculates intersections when it already knows layout information, instead of at arbitrary points in user code
B
It never reads DOM sizes
C
It runs in a worker
D
It uses GPU shaders
16

Question 16

How do batched intersection callbacks improve performance?

A
Multiple target updates are delivered in a single callback tick, reducing handler overhead compared to per-target events
B
They run on microtasks only
C
They stop event bubbling entirely
D
They enforce throttle of 1 second
17

Question 17

What prints?

javascript
const nav = document.createElement('nav')
const observer = new IntersectionObserver((entries) => {
  if (!entries[0].isIntersecting) {
    nav.classList.add('sticky')
  } else {
    nav.classList.remove('sticky')
  }
})
observer.observe(document.querySelector('header'))
console.log(nav.classList.contains('sticky'))
A
false initially (depends on header visibility)
B
true initially
C
Throws because nav must be observed
D
sticky
18

Question 18

Which configuration suits triggering animations near the viewport?

A
rootMargin: "0px 0px -20%" to fire slightly before the element fully enters, threshold: 0
B
threshold: 1 with rootMargin 0 to ensure only fully visible elements animate
C
rootMargin: "50%" for faster triggers
D
Disable threshold entirely
19

Question 19

What prints?

javascript
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => console.log(entry.target.dataset.id, entry.intersectionRatio))
}, { threshold: [0, 0.25, 0.5, 0.75, 1] })
const section = document.createElement('section')
section.dataset.id = 'hero'
observer.observe(section)
A
Eventually logs hero with intersection ratios as they cross thresholds
B
Logs immediately synchronously
C
Throws because dataset cannot be read
D
Only logs once at ratio 1
20

Question 20

Why is Intersection Observer useful for infinite scroll?

A
It can observe a sentinel element near the bottom and request more data only when the sentinel becomes visible
B
It scrolls the page automatically
C
It caches API responses
D
It disables pagination
21

Question 21

Why is Intersection Observer good for analytics?

A
You can fire viewability beacons when an element becomes visible, ensuring metrics reflect actual exposure
B
It automatically logs events to analytics providers
C
It stores data offline
D
It hashes user IDs
22

Question 22

What prints?

javascript
const target = document.createElement('div')
const observer = new IntersectionObserver((entries, obs) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      console.log('intersecting once')
      obs.unobserve(entry.target)
    }
  })
})
observer.observe(target)
observer.observe(target)
console.log(observer.takeRecords().length)
A
0, because duplicate observe calls are ignored and no records yet
B
2 because target registered twice
C
Throws due to duplicate observation
D
1 because takeRecords auto-adds entry
23

Question 23

What prints?

javascript
const target = document.createElement('div')
target.getBoundingClientRect = () => ({ top: -50, bottom: 50, left: 0, right: 100, width: 100, height: 100 })
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => console.log(entry.intersectionRect.height))
})
observer.observe(target)
A
50 eventually if the upper half is in view
B
100 immediately
C
0 always
D
Throws because intersectionRect is read-only
24

Question 24

When would you set a non-null root element?

A
When monitoring visibility inside a scrollable container rather than the viewport
B
When needing to observe document.body only
C
When running in iframes only
D
Root must always be null
25

Question 25

What prints?

javascript
const root = document.createElement('div')
root.style.overflow = 'auto'
root.style.height = '200px'
const target = document.createElement('div')
const observer = new IntersectionObserver((entries) => {
  console.log(entries[0].rootBounds === root.getBoundingClientRect())
}, { root })
observer.observe(target)
A
false because rootBounds is a DOMRectReadOnly snapshot
B
true always
C
Throws due to unsupported root
D
undefined
26

Question 26

Why call takeRecords() before disconnecting?

A
It flushes pending entries that may need to run before the observer is discarded
B
It clears DOM nodes from memory
C
It reattaches observers
D
It is required by the spec always
27

Question 27

What prints?

javascript
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => console.log(entry.target === entry.target.ownerDocument.querySelector('#watched')))
})
const el = document.createElement('div')
el.id = 'watched'
observer.observe(el)
A
true eventually (once added to document and intersecting)
B
false always
C
Throws due to querySelector usage
D
true immediately synchronously
28

Question 28

What prints?

javascript
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => console.log(entry.rootBounds?.top))
}, { root: document.documentElement, rootMargin: '50px' })
observer.observe(document.createElement('div'))
A
Eventually logs the top coordinate minus 50 because rootBounds reflects the expanded root margin
B
Always 0 regardless of margin
C
undefined because rootBounds not available when root is viewport
D
Throws due to rootMargin with viewport root
29

Question 29

Why might you use different thresholds for hero banners vs advertisements?

A
Hero animations may trigger at threshold 0.1 for anticipation, while ads often require threshold 0.5 or higher to count as a view
B
Threshold units change per component
C
Ads cannot use Intersection Observer
D
Hero elements are ignored by observers
30

Question 30

How does a single observer handle multiple targets?

A
Call observe() for each target; the same callback receives entries for all observed elements
B
Create a new observer per target
C
Use threshold: "multiple"
D
It is not supported
31

Question 31

What prints?

javascript
class VisibilityWatcher {
  constructor(target) {
    this.target = target
    this.observer = new IntersectionObserver(this.handle.bind(this))
    this.observer.observe(target)
  }
  handle(entries) {
    if (!entries[0].isIntersecting) return
    console.log('visible once')
    this.observer.unobserve(this.target)
  }
}
new VisibilityWatcher(document.createElement('div'))
A
No log until the element intersects (likely never if not in DOM)
B
visible once immediately
C
Throws because bind is illegal
D
visible once twice
32

Question 32

When do multiple entries appear in a single callback?

A
When multiple observed targets change intersection state between callback invocations
B
Only when thresholds > 0.5
C
Never; there is exactly one entry per callback
D
Only when rootMargin is nonzero
33

Question 33

Why limit observers per page?

A
Each observer maintains bookkeeping; using a single observer per use case reduces memory and scheduling overhead
B
Browsers allow only one observer globally
C
Too many observers break thresholds
D
Observers block service workers
34

Question 34

What prints?

javascript
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.intersectionRatio > 0) {
      observer.disconnect()
      console.log('triggered')
    }
  })
})
const target = document.createElement('div')
observer.observe(target)
observer.observe(target)
console.log(observer.takeRecords().length)
A
0 because no intersections yet; duplicate observe is ignored
B
2 because target added twice
C
Throws due to disconnect in callback
D
1 because takeRecords adds placeholder
35

Question 35

Why document Intersection Observer thresholds and margins for teammates?

A
Future maintainers must understand when callbacks fire to avoid unintended UX changes when tweaking layouts or loading behavior
B
Documentation enforces thresholds automatically
C
It replaces tests
D
It unlocks passive events

QUIZZES IN JavaScript