avatar
Published on

Intersection Observer

Author
  • avatar
    Name
    yceffort

Intersection Observer

Intersection Observer๋Š” ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ viewport์— ๋…ธ์ถœ๋˜๊ณ  ์žˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•ด์ฃผ๋Š” API๋‹ค. ๊ฐ„๋‹จํžˆ ๋งํ•ด ๋ธŒ๋ผ์šฐ์ €์˜ ์–ด๋–ค ์š”์†Œ๊ฐ€ ํ™”๋ฉด์— ๋…ธ์ถœ๋˜๊ณ  ์žˆ๋Š”์ง€ ์•ˆ๋˜๊ณ  ์žˆ๋Š”์ง€๋ฅผ ํ™•์ธํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ผ๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ ์—†์ด ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ๋…ธ์ถœ์ค‘์ธ์ง€ ํ™•์ธํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ? ์ด์ „๊นŒ์ง€ ์ฃผ๋กœ ์‚ฌ์šฉ๋˜๋˜ API๋Š” getBoundingClientRect๋‹ค. ์ด ๋ฉ”์„œ๋“œ๋Š” ํ•ด๋‹น ์—˜๋ฆฌ๋จผํŠธ์˜ ํฌ๊ธฐ์™€ viewport์—์„œ์˜ ์ƒ๋Œ€์ ์ธ ์œ„์น˜๋ฅผ ์•Œ๋ ค์ค€๋‹ค.

function isInViewport(element) {
  // viewport์˜ height, width
  const viewportHeight = document.documentElement.clientHeight
  const viewportWidth = document.documentElement.clientWidth
  // ์—˜๋ฆฌ๋จผํŠธ์˜ rect
  const rect = element.getBoundingClientRect()

  if (!rect.width || !rect.height) {
    return false
  }

  var top = rect.top >= 0 && rect.top < viewportHeight
  var bottom = rect.bottom >= 0 && rect.bottom < viewportHeight
  var left = rect.left >= 0 && rect.left < viewportWidth
  var right = rect.right >= 0 && rect.right < viewportWidth

  return (top || bottom) && (left || right)
}

๋ฌผ๋ก  ์ด ํ•จ์ˆ˜๋กœ๋„ ์ถฉ๋ถ„ํžˆ ํ™•์ธ์ด ๊ฐ€๋Šฅํ•˜๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ๋ฌธ์ œ๋Š” ์ด ํ•จ์ˆ˜๋ฅผ ์‹œ๋„ ๋•Œ๋„ ์—†์ด ๋ถˆ๋Ÿฌ์•ผ ํ• ๊ฒƒ์ด๋‹ค. ๋ฌธ์„œ๊ฐ€ ์ฒ˜์Œ๋กœ๋”ฉ ๋˜์—ˆ์„๋•Œ, ์Šคํฌ๋กค ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„๋•Œ, ๋ธŒ๋ผ์šฐ์ € ํฌํ‚ค๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ์„๋•Œ, (๋ชจ๋ฐ”์ผ์˜ ๊ฒฝ์šฐ) ํ™”๋ฉด์ด ํšŒ์ „ ๋์„ ๋•Œ ๋“ฑ ๊ณ ๋ คํ•ด์•ผํ•  ์‚ฌํ™ฉ์ด ๋„ˆ๋ฌด๋‚˜๋„ ๋งŽ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฒฐ์ •์ ์œผ๋กœ getBoudingClientRect()๋Š” ํ˜ธ์ถœ์ด ๋  ๋•Œ ๋งˆ๋‹ค ๋ ˆ์ด์•„์›ƒ์„ ๋‹ค์‹œ ๊ทธ๋ฆฌ๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ์— ๋งค์šฐ ๋ถ€๋‹ด์ด ๊ฐ„๋‹ค. ์ฐธ๊ณ 

์‚ฌ์šฉ๋ฒ•

var options = {
  root: document.querySelector('#scrollArea'),
  rootMargin: '0px',
  threshold: 1.0,
}

var observer = new IntersectionObserver(callback, options)

root: ์—˜๋ฆฌ๋จผํŠธ ๋…ธ์ถœ์„ ๊ฐ์‹œํ•  viewport ์˜์—ญ์ด๋‹ค. null์ด ๊ธฐ๋ณธ๊ฐ’์ด๊ณ , null์ด๋ผ๋ฉด ๋ธŒ๋ผ์šฐ์ € ์ „์ฒด๋ฅผ ๊ฐ์‹œํ•˜๊ฒŒ ๋œ๋‹ค.

rootMargin: css์˜ margin property์™€ ์‚ฌ์šฉ๋ฒ•์ด ๊ฐ™๋‹ค. root์— margin ๊ฐ’์„ ์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.

threshold: number, ๋˜๋Š” array of number๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค. ์—ฌ๊ธฐ์„œ ์ˆซ์ž๋Š” viewport์˜ n%๊ฐ€ ๋˜์—ˆ์„ ๋•Œ ์ด๋ฒคํŠธ๋ฅผ ํ˜ธ์ถœํ•  ๊ฒƒ์ธ์ง€ ๊ฒฐ์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค. array๋ผ๋ฉด ํ•ด๋‹น ์ˆซ์ž๋งŒํผ ๋…ธ์ถœ๋ ๋•Œ ๋งˆ๋‹ค ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜๊ฒŒ ๋œ๋‹ค. ๊ธฐ๋ณธ๊ฐ’์€ 0 ์œผ๋กœ, ๋‹จ 1ํ”ฝ์…€์ด๋ผ๋„ ๋…ธ์ถœ๋  ๊ฒฝ์šฐ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒ๋œ๋‹ค.

callback: ํƒ€๊ฒŸ์ด ๋…ธ์ถœ๋ ๋•Œ ์‹คํ–‰๋˜๋Š” ์ฝœ๋ฐฑํ•จ์ˆ˜๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” ๋‘๊ฐœ์˜ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

entries: IntersectionObserverEntry์˜ array์ด๋ฉฐ, ๊ฐ๊ฐ ์–ผ๋งˆ๋‚˜ ๋…ธ์ถœ๋˜์—ˆ๋Š”์ง€ ๊ฐ’์ด ๋‚˜์˜จ๋‹ค.

observer: callback์„ ํ˜ธ์ถœํ•œ IntersectionObserver

var target = document.querySelector('#listItem')
observer.observe(target)

https://codepen.io/yceffort/pen/RzVyML

์ฝ˜์†”์ฐฝ์„ ๋ณด๋ฉด, 50% ์ด์ƒ ๋‚˜์˜จ ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ์ฐํžˆ๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

ํ™œ์šฉํ•ด๋ณด๊ธฐ

๋ฌดํ•œ์Šคํฌ๋กค

https://codepen.io/yceffort/pen/VJbxNM

๋ฌธ์„œ์˜ ๋งจ ๋งˆ์ง€๋ง‰์— height๊ฐ€ 10px์ธ #watch_end_of_document๋ฅผ ๋„ฃ์–ด์„œ, ์ด ์—˜๋ฆฌ๋จผํŠธ๊ฐ€ ๋ธŒ๋ผ์šฐ์ €์— ๋…ธ์ถœ๋˜๊ฒŒ ๋˜๋ฉด ์Šคํฌ๋กค์˜ ๋งˆ์ง€๋ง‰์— ์˜จ๊ฒƒ์œผ๋กœ ๊ฐ„์ฃผํ•˜๊ณ  ๊ทธ ๋•Œ๋งˆ๋‹ค ์ƒˆ๋กœ์šด ์•„์ดํ…œ๋“ค์„ ๋กœ๋”ฉํ•˜๋„๋ก ๋ช…๋ น์„ ๋‚ด๋ ธ๋‹ค.

์ด๋ฏธ์ง€ ๋ ˆ์ด์ง€ ๋กœ๋”ฉ

https://codepen.io/yceffort/pen/YoVvya

์ฝ˜์†” - ๋„คํŠธ์›Œํฌ ์ฐฝ์œผ๋กœ ๊ฐ€๋ณด๋ฉด ์ด๋ฏธ์ง€๊ฐ€ ๋™์ ์œผ๋กœ ๋กœ๋”ฉ๋˜๋Š” ๊ฒƒ์„ ์•Œ ์ˆ˜ ์žˆ๋‹ค. ๊ฐ๊ฐ์˜ ์—˜๋ฆฌ๋จผํŠธ๋“ค์— observer๋ฅผ ๊ฑธ์–ด๋‘๊ณ , viewport์— ๊ฑธ์น ๋•Œ ๋งˆ๋‹ค backgroundImage๋ฅผ ์ค˜์„œ ์ด๋ฏธ์ง€๊ฐ€ ๋กœ๋”ฉ๋˜๋„๋ก ํ•˜์˜€๋‹ค.