throtling&debouncing - TECH-SHARING-STUDY/FE_STUDY GitHub Wiki
Event ๊ฐ์ฒด๋? ์บก์ฒ๋ง๊ณผ ๋ฒ๋ธ๋ง, ์ด๋ฒคํธ ์์, ์ฐ๋กํ๋ง & ๋๋ฐ์ด์ฑ
Event ๊ฐ์ฒด
- ์ด๋ฒคํธ์ ๋ํ ์ ๋ณด๋ฅผ ๋ด๊ณ ์๋ ๊ฐ์ฒด
- eventListener ํจ์์ ๋งค๊ฐ๋ณ์๋ก ์ ๋ฌ๋๋ค
Event ๋ฐ์
- ์ฌ์ฉ์ ์ก์ (๋ง์ฐ์ค ํด๋ฆญ ๋ฑ)
HTMLElement.click()
๋ฉ์๋ ํธ์ถ์ด๋EventTarget.dispatchEvent()
๋ฅผ ์ฌ์ฉํ์ฌ ์๋์ ์ผ๋ก ๋ง๋ค์ด๋ผ ์ ์์
ํ๋์ Element๋ ๋ค์์ listener๋ฅผ ๊ฐ์ง ์ ์๋ค. (๋์ผํ ์ด๋ฒคํธ์ ๋ํด์๋.)
Event Bubbling
ํน์ ํ๋ฉด ์์์์ ์ด๋ฒคํธ๊ฐ ๋ฐ์ํ์ ๋ ํด๋น ์ด๋ฒคํธ๊ฐ ๋ ์์์ ํ๋ฉด ์์๋ค๋ก ์ ๋ฌ๋์ด ๊ฐ๋ ํน์ฑ
Event Capturing
์ด๋ฒคํธ ๋ฒ๋ธ๋ง๊ณผ ๋ฐ๋ ๋ฐฉํฅ์ผ๋ก ์งํ๋๋ ์ด๋ฒคํธ ์ ํ ๋ฐฉ์
div.addEventListener("click", eventHandler, {
capture: true, // default ๊ฐ์ false
});
event.stopPropagation()
ํด๋น event๊ฐ ์ ํ๋๋ ๊ฒ์ ๋ง์. (๋๊น์ง๋ง ์คํ)
event.stopImmediatePropagation()
element์ click ์ด๋ฒคํธํธ๋ค๋ฌ๊ฐ ์ธ ๊ฐ(eH1, eH2, eH3) ์๋ค๊ณ ๊ฐ์ .
stopPropagation()์ ์ด๋์ ํ๋์ง ์ธ๊ฐ ๋ค ์คํํ์ง๋ง, immediateํจ์ ์คํํ๋ฉด ์ดํ ์ด๋ฒคํธ ํธ๋ค๋ฌ๊น์ง๋ ๋์ ์ํจ
Event Delegation (์ด๋ฒคํธ ์์)
๋ฒ๋ธ๋ง/์บก์ณ๋ง์ ํ์ฉํ ์ด๋ฒคํธ ํธ๋ค๋ง ํจํด.
element๋ง๋ค ํธ๋ค๋ฌ๋ฅผ ํ ๋นํ์ง ์๊ณ , ๊ณตํต ์กฐ์์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๋ฅผ ๋จ ํ๋๋ง ๋ฌ์์ ์ฌ๋ฌ ์์์ ์ด๋ฒคํธ๋ฅผ ํ๋ฒ์ ์ฒ๋ฆฌํ ์ ์์.
todo๋ฆฌ์คํธ ๊ฐ๋ฐํ ๋ ์ด๋ป๊ฒ ํ๋์ง ์๊ฐํด๋ณด์.
์์) ์นด๋๋ฅผ ํด๋ฆญํ์ฌ ์์ ํ ์ ์๋ค.
- ๋งจ์ฒ์์ ์ด๋ฒคํธ๋ฅผ ๋ค ๋ฌ์๋ค โ ์ ์นด๋๊ฐ ์ถ๊ฐ๋๋ฉด...? ๋๋ฌ์์ผ๋๋ค
- ํด๋์ค๋ก ๊ด๋ฆฌํ๋ค. ๊ฐ๊ฐ์ ์นด๋์ ์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ๋ฌ๋ ค์๋ค. โ ๋ง๋์๋์ง๋ง ์นด๋๊ฐ 10๋ง๊ฐ๊ณ ์นด๋๋น ์ด๋ฒคํธํธ๋ค๋ฌ๊ฐ ์ฌ๋ฌ๊ฐ๋ฉด ๋ฆฌ์์ค๊ฐ ๋ง์ด ๋ ๋ค.
- ์ด๋ฒคํธ ์์์ ํ์ฉํ๋ค. โ ์ด๋ฒคํธ๋ ๊ณตํต ์กฐ์์ ํ๋๋ง ๋ฌ๋ ค์๋ค. ๊ฑ๊ฐ ์ด๋ฒคํธ๋ฅผ ๋ฐ์์ ์ด๋ค ์นด๋๊ฐ ํด๋ฆญ๋๊ฑด์ง ํ๋จํ๊ณ , ์ฒ๋ฆฌํ๋ค. ์ ์นด๋๊ฐ ์ถ๊ฐ๋์ด๋ ๋๋ก์๋ค. ์นด๋๊ฐ ๋ง์์ ธ๋ ์๊ด์๋ค.
but, ์ด๋ฒคํธ๊ฐ ๋ค์ํด์ง๋ฉด ๋ค์ํด์ง์๋ก ๋น๊ต์ฐ์ฐ์ด ๋ง์ด ํ์ํ ์ ์๋ค.
const getData = (target: HTMLElement): CardData => ({
ids: getElementIds(target),
columnElm: target.closest('.column'),
cardElm: target.closest('.card'),
cardFormElm: target.closest('.card.new'),
createCardBtnElm: target.closest('.action-btn.new-card-btn'),
deleteCardBtnElm: target.closest('.delete-card-btn'),
editOkBtnElm: target.closest('.card-btn.edit'),
createCardOKBtnElm: target.closest('.card-btn.add'),
cancelBtnElm: target.closest('.card-btn.cancel'),
})
window.addEventListener('click', (e) => {
const target = e.target as HTMLElement
const data = getData(target)
const {
cardElm,
createCardBtnElm,
deleteCardBtnElm,
editOkBtnElm,
createCardOKBtnElm,
cancelBtnElm,
} = data
if (createCardBtnElm) {
createCardFormHandler(data)
return
}
if (createCardOKBtnElm) {
createCardHandler(data)
return
}
if (editOkBtnElm) {
const previousCardId = getPreviousCardNumber(cardElm)
editCardHandler(data, previousCardId)
return
}
if (cancelBtnElm) {
cancelCreateOrEditHandler(data)
return
}
if (deleteCardBtnElm) {
deleteCardHandler(data)
return
}
})
Throttling, Debouncing
์ด๋ฒคํธ ํธ๋ค๋ฌ๊ฐ ๋ง์ ์ฐ์ฐ(์ : ๋ฌด๊ฑฐ์ด ๊ณ์ฐ ๋ฐ ๊ธฐํ DOM ์กฐ์)์ ์ํ(์ด๋ฒคํธ ํธ๋ค๋ฌ์ย ๊ณผ๋ํ ํ์๊ฐ ๋ฐ์ํ๋ ๊ฒ)ํ๋ ๊ฒฝ์ฐ ์ ๋ํด ์ ์ฝ์ ๊ฑธ์ด ์ ์ดํ ์ ์๋ ์์ค์ผ๋ก ์ด๋ฒคํธ๋ฅผ ๋ฐ์(๊ทธ ํธ๋ค๋ฌ๋ฅผ ๋ ์ ๊ฒ ์คํํ๋ฉด ๋น ์ ธ ๋๊ฐ ์ ์์)์ํค๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ ๊ธฐ์
์? JS์ฐ์ฐ์ด ๋ง์์ง๋ฉด ๋ฆฌ์์ค๋ฅผ ๋ง์ด ์ก์๋จน์ โ ํ๋ ์๋จ์ด์ง โ ๋๋๋๋
debounce
์ฐ๋ฌ์ ํธ์ถ๋๋๊ฒ์ค์ ๋ง์ง๋ง or ์ ์ผ ์ฒ์ ํจ์๋ง ํธ์ถ
์ด๋ฒ์ ์๋์์ฑ์ ์ฌ์ฉ๋ ์ ์์. input์ onChange์ apiํธ์ถ ๊ฑธ๋ฉด '๋ฐ๋๋๋ง ์ฐ์ ' ์น ๋ ๋ชจ์๊ฐฏ์+์์๊ฐฏ์๋งํผ apiํธ์ถ์ผ์ด๋จ.
๋ฆฌ์์ค ๋ญ๋น์ผ ๋ฟ๋ง ์๋๋ผ ์ฑํฌ์ ๋ํ ๋ณด์ฅ๋ ๋ชปํจ.
throttle
์ฐ๋ฌ์ ํธ์ถ๋๋ ์ด๋ฒคํธ๋ฅผ ์ผ์ ํ ์ฃผ๊ธฐ๋ง๋ค ๋ฐ์ํ๊ฒ ํจ
๋ง์ฝ ์คํฌ๋กค์ debounce ๋ฌ๋ฉด ์คํฌ๋กค์ด ๋ฉ์ท์ ์ฏค ๋ฐ์ํจ. ๋ฌดํ์คํฌ๋กค ๊ตฌํํ๋ค๊ณ ๊ฐ์ ํ๋ฉด
์คํฌ๋กค ์ค์ฝ์ค์ฝ ๋๊น์ง ๋ด๋ ค์ ๊ทธ๋ง๋์ ๋ apiํธ์ถ๋จ. ์ด ๋ throttle์ด ๋ ์ ํฉํจ.
์คํฌ๋กค ๋ด๋ฆด๋ ์ค๊ฐ์ค๊ฐ 100ms๋ง๋ค ์ด๋ฒคํธ ๋ฐ์์์ผ์ ์ผ์ ๋ฒ์์ ๋๋ฌํ๋ฉด apiํธ์ถ!
โ ์ฌ์ฉ์๊ฐ Footer์ ๋ฟ๊ธฐ ์ ์ ํธ์ถํ ์ ์์
lodash ์ธ์คํจํ๋ฉด ํธํ๊ฒ ์ธ ์ ์๋ค...ใ ใ
๊ตฌํํด๋ณด์
debounce
const notionScroller = document.querySelector(
".notion-scroller.vertical.horizontal"
);
let interval = null;
notionScroller.addEventListener("scroll", (e) => {
if (interval) {
clearTimeout(interval);
}
interval = setTimeout(() => {
console.log(
notionScroller.scrollHeight -
notionScroller.clientHeight -
notionScroller.scrollTop
);
}, 100);
});
throttle
const notionScroller = document.querySelector(
".notion-scroller.vertical.horizontal"
);
let interval = null;
notionScroller.addEventListener("scroll", (e) => {
if (!interval) {
interval = setTimeout(() => {
interval = null;
console.log(
notionScroller.scrollHeight -
notionScroller.clientHeight -
notionScroller.scrollTop
);
}, 100);
}
});
์ฐธ๊ณ ๋ฌธํ
https://joshua1988.github.io/web-development/javascript/event-propagation-delegation/
https://ko.javascript.info/bubbling-and-capturing