[Javascript] 2つ以上のリストボックスを相互に同期スクロールする方法

2021年8月21日

Javascript テクノロジー

eyecatch 同期スクロールに、悶え苦しんでしまった、ユゲタです。 同期スクロールって何かというと、 2つの関連したリストを表示するような場合に、それぞれリストがスクロールするほど長い状態の時、 その2つのリストの片方をスクロールしたら、もう片方がそれを追従するようにリストをスクロールさせたい場合に、 同期スクロールという方法をとるようにする事なんですが、 javascriptで、リストのスクロールイベントをキャッチして、もう片方のリストのスクロール値を変更してあげればいいだけなんですが、 思いもよらず、うまく動かなかったので、その苦労の状態を備忘録しておきたいと思います。

相互の動機スクロールでの問題点

とりあえず、まずは最初にコードを書いてみます。 <link rel="stylesheet" href="common.css"> <div class="flex"> <ul id="a"> <li>1</li> <li>2</li> <li>3</li> <li>4</li> <li>5</li> <li>6</li> <li>7</li> <li>8</li> <li>9</li> <li>10</li> <li>11</li> <li>12</li> <li>13</li> <li>14</li> <li>15</li> <li>16</li> <li>17</li> <li>18</li> <li>19</li> <li>20</li> </ul> <ul id="b"> <li>a</li> <li>b</li> <li>c</li> <li>d</li> <li>e</li> <li>f</li> <li>g</li> <li>h</li> <li>i</li> <li>j</li> <li>k</li> <li>l</li> <li>m</li> <li>n</li> <li>o</li> <li>p</li> <li>q</li> <li>r</li> <li>s</li> <li>t</li> </ul> </div> <script src="scroll_sync.js"></script> ul,li{ list-style:none; margin:0; padding:0; } ul{ margin:10px; overflow:scroll; width:300px; height:300px; border:1px solid black; } li{ padding:5px; border-bottom:1px solid #ccc; } .flex{ display:flex; } document.getElementById("a").addEventListener("scroll" , scroll_sync); function scroll_sync(e){ let target = null; switch(e.target.id){ case "a": target = document.getElementById("b"); break; } target.scrollTop = e.target.scrollTop; } これを実行すると、左側のスクロールをした場合に、右側が一緒にスクロールします。 そして、今度は、右側のスクロールをした時に、左側のスクロールをさせたい場合に、 効率を考えて、同じscroll_sym関数を利用するために、次のコードをjsに付け足してみました。 document.getElementById("b").addEventListener("scroll" , scroll_sync); サンプルの画面では、ぎこちなくスクロール同期されていますが、少しemenentが増えてくると 片方がうまくスクロールされなくなります。 これは、対象のスクロールイベントが発火してしまって、相互にスクロール同期をやり合う、コンフリクトに近い状態になているようです。

回避方法は力技

とにかくぎこちなさをなくすために、今回とった策として、次のようなjsコードにしてみました。 window.a = document.getElementById("a"); window.b = document.getElementById("b"); a.addEventListener("scroll" , scroll_sync); b.addEventListener("scroll" , scroll_sync); function scroll_sync(e){ let target = null; if(window.scroll_sync_target && window.scroll_sync_target !== e.target){return;} switch(e.target.id){ case "a": target = window.b; break; case "b": target = window.a; break; } window.scroll_sync_target = e.target; target.scrollTop = e.target.scrollTop; setTimeout((function(){if(window.scroll_sync_target){delete window.scroll_sync_target}}) , 300); } 最初に発火したタイミングで、window.scroll_sync_targetに、発火したエレメントを保持しておき、 0.3秒後に、そのフラグを削除しています。(もう少し短い秒数でもいいですが) そして、対象エレメントのイベント発火の際に、window.scroll_sync_targetと同じエレメントでなければ、 returnして、処理をしないようにしています。 あと、毎回getElementをしている非効率を、グローバル変数で、最初一回のみの処理にすることで、 ほんのちょっぴり効率を良くしています。 でも、そもそもの、scrollイベントのタイムラグがあるため、多少のぎこちなさは残りますし、 めちゃくちゃ素早くスクロールすると、同期されない場合もあります。 この辺は、他のイベントと組み合わせて行うしかないでしょうね。 とりあえず、今回は2つのスクロール同期をやってみましたが、Googleのスプレッドシートのフレームのように、縦横でのスクロール同期などを やる場合もあるので、この辺の精度の良さ追求しておきたいですね。 Googleもちょびっとぎこちないんで、仕方ないかもしれませんが・・・

デモ

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • a
  • b
  • c
  • d
  • e
  • f
  • g
  • h
  • i
  • j
  • k
  • l
  • m
  • n
  • o
  • p
  • q
  • r
  • s
  • t

人気の投稿

このブログを検索

ごあいさつ

このWebサイトは、独自思考で我が道を行くユゲタの少し尖った思考のTechブログです。 毎日興味がどんどん切り替わるので、テーマはマルチになっています。 もしかしたらアイデアに困っている人の助けになるかもしれません。

ブログ アーカイブ