두 요소 스크롤 동기화

const box1 = document.getElementById('box1')
const box2 = document.getElementById('box2')

let ignore = null

function sync(source, target) {
  if (ignore === source) { ignore = null; return } // 내가 일으킨 scroll 이벤트는 한 번 건너뛴다
  const ratio = source.scrollLeft / (source.scrollWidth - source.clientWidth)
  const next = Math.round(ratio * (target.scrollWidth - target.clientWidth))
  if (target.scrollLeft === next) return // 값이 안 바뀌면 이벤트도 안 온다 — ignore 를 세우면 다음 사용자 스크롤을 삼킨다
  ignore = target
  target.scrollLeft = next
}

box1.addEventListener('scroll', () => sync(box1, box2))
box2.addEventListener('scroll', () => sync(box2, box1))

scroll 이벤트는 scrollLeft 를 넣는 순간이 아니라 다음 렌더링 때 온다1 — 동기 플래그(isSyncing = true … false)는 이벤트가 오기 전에 이미 풀려 있어서 막지 못한다. 그래서 “다음에 올 이벤트 하나”를 표시해 둔다.


Footnotes

  1. CSSOM View — Scrolling events: “Whenever an element gets scrolled (whether in response to user interaction or by an API), the user agent must run these steps: … Append (element, “scroll”) to doc’s pending scroll events.“ — 실제 발사는 이벤트 루프의 렌더링 단계(“run the scroll steps”)에서 한다. ↩

`#532`