| 著作一覧 |
同時に良く似たページを比較して眺めたいので、方法をいろいろ考えた。 それぞれをスクロール可能な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
)
)
もちろん、もっとスマートな方法があればそれを知りたいところ。
ジェズイットを見習え |