スクロールを連動させる
Qiitaなんかである入力エリアとプレビューエリアのスクロールが連動するやつ。
要件は違ったんですが、スクロール同期という点では同じだったのでやってみたので書いておきます。
単純に連動する
まずは単純に同じサイズの要素を連動させてみます。
単純な連動デモ
const areaA = document.getElementById('area-a') const areaB = document.getElementById('area-b') areaA.addEventListener('scroll', e => { const scrollTop = e.target.scrollTop areaB.scrollTo(0, scrollTop) })
同じサイズのA→Bと連動させるなら、AのスクロールイベントでscrollTop
をとって、BのscrollTo()
に放り込んであげるだけです。
サイズが違う要素を連動する
次にQiitaみたいな要素の大きさが異なる場合の連動を見ていきます。
サイズ違いの連動デモ
const areaA = document.getElementById('area-a') const areaB = document.getElementById('area-b') areaA.addEventListener('scroll', e => { const scrollMaxA = e.target.scrollHeight - e.target.clientHeight const scrollMaxB = areaB.scrollHeight - areaB.clientHeight const percent = e.target.scrollTop / scrollMaxA areaB.scrollTo(0, scrollMaxB * percent) })
さっきと異なるのがサイズが異なるので、何%スクロールしたのかを求めることが必要です。
まずはscrollHeight - clientHeight
で、どれだけスクロールできるのかをそれぞれ求めます。
次にAのscrollTop / scrollMaxA
で%を計算。
最後にBのscrollTo
にscrollMaxB * percent)
でスクロール量を放り込んであげて完成です!
相互連動させる
最後に相互に連動させる必要がある場合です。
相互連動デモ
const areaA = document.getElementById('area-a') const areaB = document.getElementById('area-b') function scrollA(e) { const scrollTop = e.target.scrollTop areaB.scrollTo(0, scrollTop) } function scrollB(e) { const scrollTop = e.target.scrollTop areaA.scrollTo(0, scrollTop) } areaA.addEventListener('scroll', scrollA) areaB.addEventListener('scroll', scrollB) areaA.addEventListener('mouseenter', e => { areaB.removeEventListener('scroll', scrollB) }) areaB.addEventListener('mouseenter', e => { areaA.removeEventListener('scroll', scrollA) }) areaA.addEventListener('mouseleave', e => { areaB.addEventListener('scroll', scrollB) }) areaB.addEventListener('mouseleave', e => { areaA.addEventListener('scroll', scrollA) })
ちょっと面倒な書き方してるかもしれないですが。。。
相互連動するとA→Bをしたときに、Bのスクロールイベントがトリガーされます。
デモぐらい単純な中身なら気にはなりませんが、たまーにズレたりします。
そこで、AをスクロールさせたときはBのイベントをトリガーしない。
BをスクロールしたときはAのイベントをトリガーしない。みたいなことが必要になります。
方法は
- どこからスクロールされているのかを明確にする
- そもそもイベント自体を消してしまう
どっちかだと思いますが、今回は後者です。
各要素にmouseenter
したら別要素のイベントをremoveEventListener
で削除。
mouseleave
したら別要素にイベントをaddEventListener
で付与しています。