2019-03-26 16:03 μμ±λ¨
π
π» A banner created from here
MDN λ¬Έμμμλ μ΄λ κ² μ€λͺ νκ³ μμ΅λλ€
The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.
(μμ)
κ΄μ°°νκ³ μ νλ μ리먼νΈμ μ‘°μ μλ¦¬λ¨ΌνΈ λλ viewport κ°μ
κ΅μ°¨(Intersection)μ λ³νλ₯Ό λΉλκΈ°μ μΌλ‘ κ°μ§νλ λ°©λ²μ
λλ€
μ... λ¬΄μ¨ μκΈ°μΈμ§ λͺ¨λ₯΄κ² λ€κ΅¬μ? μ λμ
κ΅μ°¨μ λ³νλ₯Ό κ°μ§νλ€λ κ²μ λ¨νΈμ μΌλ‘ μ€λͺ
νμλ©΄
μ리먼νΈμ visibility (κ°μμ±) μ κ΄ν λ³νλ₯Ό κ°μ§νλ€λ μλ―Έ μ
λλ€
Intersection Observer APIλ μ리먼νΈκ° viewport μμ
λλ λ€λ₯Έ μ리먼νΈμμ κ΄κ³μμ "보μ΄λμ§ μ보μ΄λμ§"
λ₯Ό μ½κ² μ μ μλλ‘ κΈ°λ₯μ μ 곡νλ μν μ ν©λλ€
APIλ μλμ κ°μ 보νΈμ μΈ κ²½μ°μ μ¬μ©λ©λλ€
μ΄μ μ, .getBoundingClientRect()
μ window.innerHeight
λ₯Ό μ¬μ©ν΄
μ΄λ ΅μ¬λ¦¬ κ°μμ±μ λν μ 보λ₯Ό μ»μλ€λ©΄
Intersection Observer API λ₯Ό μ¬μ©ν΄ μ‘°κΈ λ νΈνκ² μμ
ν μ μμ΅λλ€
μ£Όμ λΈλΌμ°μ μ μ§μ νν©μ
λλ€
Google Chrome: 51^
Microsoft Edge: 15^
Firefox: 55^
IE: No Support (λ κΈ°λνμ κ±°μ£ ?)
Opera: No Support
Safari: No Support
Chrome κ³Ό Safari λͺ¨λ Webkit κΈ°λ° λΈλΌμ°μ μΈλ° Chrome λ§ μ§μνλκ² μ’ ν₯λ―Έλ‘μ μ΅λλ€
λ―Έ μ§μ λΈλΌμ°μ λ₯Ό μν polyfill μ΄ μμ§λ§, μλ²½νμ§λ μμ΅λλ€
(w3c Github μ ν΄λ¦¬νμ΄ μμ΅λλ€. λ§ν¬)
κ·Έλ¦¬κ³ λΉμ°νμ§λ§, IE μμλ μ§μ λμ§ μκ³ , μ§μ κ³νλ μμ΅λλ€
(μ£½μ΄κ³ μΆμ IE μ μ μμ 10μ )
polyfill μ μ¬μ©νλ©΄ λλκ² μλλꡬμ?
λΈλΌμ°μ λ§λ€ λμμ΄ λ€λ¦
λλ€... (μμλ λͺ»ν λμ γ΄(Β°0Β°)γ±)
μ΄ νμ΄μ§λ₯Ό μ€νλΌλ μ¬ν리, ννμΌλ‘
νμΈν΄λ³΄μλ©΄ ν΄λ¦¬νμ μ μ© νμμλ λΆκ΅¬νκ³ λ―Έλ¬νκ² λμμ΄ λ€λ¦
λλ€
(μ΄λ―Έμ§κ° μ 보μ΄λ©΄ μλ μ¬λΌμ ΈμΌ νλλ° μ μ¬λΌμ§λ€κ±°λ...)
IntersectionObserver API μ κ°λ° μ§ν μν©μ μ΄ κ³³ μμ νμΈν μ μμ΅λλ€
빨리 μ¬λ¬ λΈλΌμ°μ μμ μ¬μ© κ°λ₯νμΌλ©΄ μ’κ² λ€μ (IE λΉΌκ³ π€ͺ)
Intersection Observer API λ₯Ό μ΄ν΄νκΈ° μν΄ λͺ κ°μ§ κ΄λ ¨ μ©μ΄λ₯Ό μ 리ν©λλ€
Observer κ°μ²΄μ μμ±μ λ€μκ³Ό κ°μ΄ ν©λλ€
const io = new IntersectionObserver(callback, {
root: null, // λλ scrollable ν element
rootMargin: '0px', // μ§μ νμ§ μμΌλ©΄ κΈ°λ³Έκ°μ 0px μ
λλ€,
threshold: 0.5 // 0.0 λΆν° 1.0 μ¬μ΄μ μ«μλ₯Ό μ§μ ν μ μμ΅λλ€. λ°°μ΄ κ°λ κ°λ₯ν©λλ€
})
root μ κ°μ Element
λλ null
μ
λλ€
target (κ΄μΈ‘ λμ) μ κ°μΈλ element λ₯Ό μ§μ νλ€κ³ ν μ μμ΅λλ€
λ§μ½ null λ‘ μ§μ νλ€λ©΄ viewport κ° λ©λλ€
λ¬Έμ λ΄μ scrollable ν μμκ° μκ³ <div class="scroll"></div>
κ΄μΈ‘ νκ³ μ νλ element κ° κ·Έ μμ μλ€κ³ κ°μ νλ©΄ <img class="lazy-loaded-image">
root μ κ°μ document.querySelector('.scroll')
μ΄ λ©λλ€
root μμλ₯Ό κ°μΈλ margin κ°μ μ§μ ν©λλ€
λ¬Έμμ΄λ‘ μμ±ν΄μΌ νλ©°, cssμ margin μ²λΌ px λλ % λ¨μλ‘ μμ±ν μ μμ΅λλ€
threshold λ κ΅μ°¨ μνλ₯Ό μ κ²ν λ margin κ°μ΄ μ§μ λμ΄ μλ€λ©΄, μ΄λ₯Ό νμ©νμ¬ κ³μ°ν©λλ€
target element κ° root μ λͺ % κ΅μ°¨νμ λ, callback μ μ€νν μ§ κ²°μ νλ κ° μ
λλ€
κ°μ float κ°μΌλ‘ λ λ¨μΌκ° λλ λ°°μ΄κ°μ μ
λ ₯ν μ μκ³
0.0 λΆν° 1.0 κΉμ§μ λΆλ μμμ κ°μ μ
λ ₯ν©λλ€
μλ₯Ό λ€μ΄, μ€ν¬λ‘€ νλ μ€μ element κ° 50% 보μμ λ μ€νν callback μ΄ μλ€κ³ νλ©΄
threshold κ°μ 0.5 κ° λ©λλ€
λλ, element κ° λ³΄μ΄λ κ²μ μ¬λ¬ ꡬκ°μμ callback μ μ€ννκ³ μΆλ€λ©΄
λ°°μ΄λ‘ μμ±νλ©΄ λ©λλ€
element μ λ
ΈμΆλ 10% λΉ λ¬΄μΈκ°μ λμμ νκ³ μΆλ€λ©΄, threshold λ
[0.0, 0.1, 0.2, 0.3, ..., 0.9, 1.0] μ΄ λκ² μ£ !
callback μ target element μ visibility κ° threshold λ§νΌ λλ¬νμ λ νΈμΆλ ν¨μ μ
λλ€
Element λ°°μ΄μΈ entries
μ μκΈ° μμ μΈ observer
κ° λ§€κ° λ³μλ‘ μ λ¬ λ©λλ€
μ΄λ₯Όν λ©΄ μ΄λ μ΅λλ€
new IntersectionObserver((entries: Element[], observer: IntersectionObserver) => { /* ... */ }, {})
entries
λ target μΌλ‘ μ§μ ν DOM element κ°μ²΄μ
λλ€.
보ν΅μ callback ν¨μλ μ€μ μμΉ (threshold) μ λλ¬ νμ λ μ€νν λμμ μμ±ν©λλ€!
μ¬μ© μμλ μμ MDN μμ μ μν κ² μ²λΌ
Lazy loading, Infinite Scrolling λ±μ μ¬μ©ν μ μμ΅λλ€
λ λ€ ν μ μλ€κ³ νλκΉ λ λ€ λ§λ€μ΄ λ³ΌκΉμ?
λ¨Όμ κΈκ³Ό μ΄λ―Έμ§κ° μκ³ , μ€ν¬λ‘€μ΄ νμν HTML λ¬Έμκ° μλ€κ³ κ°μ νκ² μ΅λλ€
<head>
<style>
div.image-box {
display: block;
margin: 20px auto 0;
width: 200px;
height: 200px;
background-color: #ACACAC;
color: #FFFFFF;
text-align: center;
line-height: 200px;
font-size: 24px;
}
</style>
</head>
<body>
<div class="image-box" data-src="https://source.unsplash.com/random/200x200">No Image</div>
</body>
μ μ΄λ―Έμ§μ κ°μμ± (Visibility) κ° 80% μ λλ¬νμ λ, μ΄λ―Έμ§λ₯Ό μ€μ λ‘ λΆλ¬μ 보λλ‘ νκ² μ΅λλ€
μ΄λ―Έμ§κ° λ‘λ©λμ§ μμ λ°μ€λ, νμ λ°°κ²½μ No Image λ‘ μΆλ ₯λ©λλ€
μλμ κ°μ΄ IntersectionObserver λ₯Ό λ§λλλ€
const images = document.querySelectorAll('.image-box');
const threshold = 0.8;
const lazyLoadOption = { root: null, threshold };
const lazyLoadImage = (entries, observer) => {
entries.forEach(entry => {
const { target } = entry;
if (entry.isIntersecting) {
target.style.backgroundImage = `url("${target.dataset.src}")`;
target.style.backgroundSize = `cover`;
target.style.backgroundColor = `transparent`;
target.textContent = '';
}
})
};
const lazyLoadingIO = new IntersectionObserver(lazyLoadImage, lazyLoadOption);
images.forEach(image => lazyLoadingIO.observe(image));
κ°μμ± 80% μΌ λ, μ΄λ―Έμ§λ₯Ό λΆλ¬μμΌ νλ―λ‘ 2λ²μ§Έ λΌμΈ μ²λΌ threshold = 0.8 μ λλ€
entry.isIntersecting
νλ‘νΌν°λ κ΄μ°°νκ³ μ νλ DOM κ°μ²΄κ°
μ€μ ν root μ threshold λ§νΌ κ΅μ°¨νλμ§μ λν true/false κ° μ
λλ€
(κ΄μ°°νλ €λ DOM μ κ°μμ±μ΄ threshold λ₯Ό μ΄κ³Όνλ©΄ true, μλλ©΄ false μ
λλ€)
κΈ°μ‘΄ div.box-image
μ리먼νΈμ λ°°κ²½κ³Ό κΈμλ₯Ό μ§μ°κ³ background-image λ₯Ό μΆκ°νλ©΄
μλμ κ°μ΄ μ λμ ν©λλ€!
μ½λλ Gist μμ νμΈν μ μμ΅λλ€!
(Native Infinite Scrolling with the IntersectionObserver API λ₯Ό μΌλΆ μ°Έκ³ νμ΅λλ€)
Infinite Scrolling μ ꡬννλ €λ©΄ DOM μμμ scrollTop, scrollHeight λ±μ κ°κ³Ό
'scroll' μ΄λ²€νΈλ λ±λ‘νκ³ , μ€ν¬λ‘€μ΄ λκΉμ§ λ΄λ €κ°λ 체ν¬νκ³ ... λ± ν μΌμ΄ λ§μ΅λλ€ π
Intersection Observer λ₯Ό μ°λ©΄ κ°κ²°νκ²...? μλ¬΄νΌ ν° μ΄λ €μ μμ΄ κ΅¬νν μ μμ΅λλ€
(λ¬Όλ‘ μ§μ λ°λλΌ μλ°μ€ν¬λ¦½νΈλ‘ ꡬνν μΌλ μμΌμκ² μ£ π
μ¬λ¬ νλ₯ν λΌμ΄λΈλ¬λ¦¬λ€μ΄ μμ΄μ)
κ·ΈλΌ κ°λ¨νκ² κ΅¬ν ν΄λ³΄κ² μ΅λλ€
μλμ κ°μ Layout μ΄ μμ΅λλ€
<body>
<h1 class="title">Obey the cats π</h1>
<ul class="infinite-container"></ul>
</body>
ul.infinite-container
μ li
λ₯Ό κ³μν΄μ λ§λ€μ΄ λ΄λ ννλ₯Ό ꡬνν©λλ€
const ioOptions = {
root: null,
threshold: 1,
};
const io = new IntersectionObserver(handleInfiniteScrolling, ioOptions);
λ¨Όμ IntersectionObserver κ°μ²΄λ₯Ό λ§λ€μ΄ μ€λλ€.
μ€ν¬λ‘€μ΄ μμ ν λλ¬μ λ, μλ‘μ΄ μμ΄ν
μ λ‘λ©ν΄μΌ νλ―λ‘ threshold = 1 μ
λλ€.
root λ μ°μ μ viewport κΈ°μ€μ΄λ―λ‘ null κ° μ
λλ€
(λ§μ½, λ³λμ μμ μ€ν¬λ‘€ μμμ μ μ©νμ λ€λ©΄ root λ₯Ό μ§μ ν΄ μ£Όμλ©΄ λ©λλ€!)
κ·ΈλΌ handleInfiniteScrolling
ν¨μλ₯Ό ꡬνν΄ λ³Όκ²μ
const loadingNewCats = (newCats) => {
const loadingTemplate = `<li><span>Loading New Cats...</span></li>`;
return new Promise((resolve, reject) => {
container.insertAdjacentHTML('beforeend', loadingTemplate);
setTimeout(() => {
resolve(newCats);
container.removeChild(container.lastChild);
}, 1000)
})
};
const handleInfiniteScrolling = (entries, observer) => {
const $last = [...entries].pop();
if ($last.isIntersecting) {
loadingNewCats(createNewCats()).then(newCats => {
container.append(...newCats);
currentLast = lastChild();
observer.unobserve($last.target);
observer.observe(currentLast);
});
}
};
(loadingNewCats
λ κ·Έλ₯ μλ‘μ΄ κ³ μμ΄ μ¬μ§ 10κ°λ₯Ό λΆλ¬μ€λ ν¨μ μ
λλ€)
μ€ν¬λ‘€μ΄ λ§μ§λ§μ λλ¬ νμ λ νΈμΆλ ν¨μμΈ handleInfiniteScrolling
λ
ul
컨ν
μ΄λμ λ§μ§λ§ li
μ visibility κ° 1μ΄ λμμ λ μ κ³ μμ΄ μ¬μ§λ€μ λ‘λ©ν©λλ€li
μ리먼νΈλ unobserve νκ³ μ li
μ리먼νΈλ₯Ό observe ν΄μ£Όλ©΄ λ©λλ€μ½λλ Gist λλ Codepen μμ νμΈ κ°λ₯ν©λλ€
JavaScript30 μ 13μΌμ°¨ Slide in on Scroll λ₯Ό μ€μ΅νλ μ€
μ΄λ―Έμ§κ° μΌμ μ€ν¬λ‘€μ λλ¬νμ λ, transition μ λλ©μ΄μ
μΌλ‘ slide-in μ²λ¦¬λ₯Ό μν΄
μ΄λ€ λ°©λ²μ΄ μμκΉ νλ€κ° κ³Όκ±°μ λ μ§ κΈ°μ λΈλ‘κ·Έ μμ λ³Έ κΈμ κΈ°μ΅ν΄
μ€μ λ‘ μ μ©ν΄ 보μμ΅λλ€
μ΄λ―Έμ§ lazy-loading μ΄λ 무ν μ€ν¬λ‘€ λ±μ μ§μ ꡬνν μ μλ λ₯λ ₯μ΄ μλ€κ³ μκ°νλλ°
Intersection Observer API λ₯Ό μ΄μ©νλ μ½κ² ꡬν ν μ μμμ΅λλ€
λΉ λ₯Έ μμΌλ΄μ μ¬λ¬ λΈλΌμ°μ μμ μ¬μ©ν μ μμΌλ©΄ μ’κ² λ€μ!
μλͺ»λ λΆλΆμ΄λ μ§μ μ μΈμ λ νμν©λλ€
κΈ΄ κΈ μ½μ΄μ£Όμ
μ κ°μ¬ν©λλ€
WebKit Bugzilla
W3 Working Draft
DWB
λ μ§ κΈ°μ λΈλ‘κ·Έ
codepink.github.io
Unsplash Source β λ€μ±λ‘μ΄ μ΄λ―Έμ§λ₯Ό λΆλ¬μ¬ λ κ΅μ₯ν νΈλ¦¬ν©λλ€ μΆμ² γ
γ
γ
Using the Intersection Observer API to Trigger Animations and Transitions
μ’μ κΈ κ°μ¬ν©λλ€ :)
곡λΆνλ λ° λ§μ λμμ΄ λμμ΄μ!
Lazy loading μμ μ κ²½μ°
if (entry.isIntersecting) λΈλ‘ λ§μ§λ§μ
observer.unobserve(target);
μ΄ μ½λλ₯Ό μΆκ°ν΄μ μ΄λ―Έμ§λ₯Ό λ‘λ©ν μ리먼νΈμ λν΄μλ
observingμ μ’
λ£ ν΄μ£Όλκ²λ μ’μ κ² κ°μμ π
λΈλΌμ°μ λ³ λμ μμ λ§ν¬κ° κΉ¨μ§λλ€(γ γ ). μ’μ κΈ μ μ½κ³ κ°λλ€. λμμ΄ λ§μ΄ λμμ΄μ.
good