著作一覧 |
同時に良く似たページを比較して眺めたいので、方法をいろいろ考えた。 それぞれをスクロール可能なDIVに組み込めば、同じWindow内の要素同士なので、ほぼ何も考えなくても、片側のDIVのスクロール量をもう一方に与えれば良いので簡単だ。 が、それぞれが独自にJavaScriptを読み込んだりするので、htmlタグ全体を読み込みたい。
となると、iFrameを2つ並べてそれを使うしか、ちょっと方法を考えつかなかった。 で、MDNを読むとwheelイベントというWeb標準があるので、それを利用して、片方のiFrameで受信したwheelイベントのdeltaYを親Windowに与えて、親Windowはもう片方のiFrameのwindowをスクロールすれば良い。簡単じゃん。
と、Firefoxで実装したわけだ。おお、ちゃんと同期する。し、(比較したいわけなので)片方を余分にスクロールしたければスクロールサムをドラッグすればそのiFrameだけ動くので具合も良い。
が、残念。
EdgeでもChromeでもwheelイベントのdeltaYは正しいスクロール量ではない。
なんじゃこれ? と、MDNを良く読むと、ブラウザーの実装ではwheelイベントのdelta*を反映する必要はないと書いてある。だから、scrollイベントを使え。
とはいえ、単純にscrollイベントを使うと、移動量を変えたい場合に処理ができない。ということは、スクロールバーを使ったスクロール時は無視する必要がある。
結局、ホイール操作からスクロールバー操作に人間の動作が移る最短時間を500ミリ秒と適当に判断して、以下のような実装となった(Coffeeで記述している)。
<iframe data-opposite-id="B" src=... ></iframe> <!-- こちらのHTMLは自身をAと認識 --> <iframe data-opposite-id="A" src=... ></iframe> <!-- こちらのHTMLは自身をBと認識 -->
# 親Window側 window.addEventListener('message', (e) -> try msg = JSON.parse(e.data) catch e then return if msg.command == 'scroll' document.querySelector('iframe[data-opposite-id="' + msg.sender + '"]').contentWindow.scrollBy(0, Math.ceil(msg.deltaY)) # Math.ceilは不要だとは思うし、おそらく余分にスクロールする )
# フレーム側 wheelTimer = null currentTop = null window.addEventListener('wheel', (e) -> if wheelTimer clearTimeout(wheelTimer) if currentTop == null currentTop = window.scrollY wheelTimer = setTimeout(() -> wheelTimer = null currentTop = null , 500) ) ) window.addEventListener('scroll', (e) -> if wheelTimer id = document.querySelector('body').dataset['myId'] # A or B data = {command: 'scroll', sender: id, deltaY: window.scrollY - currentTop} origin = /^[^:]+:\/\/[^/]+/.exec(document.location.href) window.parent.postMessage(JSON.stringify(data), origin[0]) currentTop = window.scrollY ) )
もちろん、もっとスマートな方法があればそれを知りたいところ。
ジェズイットを見習え |