10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

複数のウィンドウを同時にスクロールさせるChrome拡張作った話

Last updated at Posted at 2022-12-05

本記事はSLP KBIT AdventCalendar 2022の6日目の記事になります.
大突貫工事ですが,緩くお楽しみください.

DEMO

とりあえず開発物のDEMO動画です.
タイトルのとおり,複数のウィンドウを同時に自動スクロールさせることができます(1つでもOK).
画面右上のポップアップから対象としたいページを選択して,中央のスタートボタンを押すとスクロールが開始します.
右のボタンでスピードアップ,左のボタンでスピードダウンです.
これらのボタンを以降ではスクロール制御ボタンと呼びます.
ezgif-2-af32aa2a12.gif

開発に使ったもの

開発のお話

今回この記事では,システムの中心的な機能についての話に話題を絞ります.
Chrome拡張の開発のいろはについては,このあたりの記事を参照してみてください.

システム概要

このシステムは大まかにはbrowser action(ポップアップ)とContent Scriptsの2つのプログラムから構成され,必要な処理は次のとおりです.

  1. ポップアップメニュー起動時に,アクティブなタブを取得
  2. スクロール制御ボタン押下時に,選択されたタブに対してスクロール制御命令を送信
  3. スクロールの実行

アクティブなタブの取得

chrome.tabs APIのchrome.tabs.queryで一撃です.
波括弧の中で取得したいタブの条件を指定し,コールバックに対して任意の処理を追加します.
今回はアクティブ(各ウィンドウで表示されている状態)なタブを取得したいため,active: trueを条件としています.
また,取得したタブはユーザから選択できるようにするために,チェックリストとして表示するようにしています.
チェックリストにはタブのIdをvalueとして入れています.後で使います.
※このAPIを利用するためには,manifestのpermissionにtabsを追加しておく必要があります.
※このコードはポップアップ(browser action)内に書いています.

chrome.tabs.query({'active': true}, tabs => {
    // 任意の処理
    for (tab of tabs) {
        content += `<div><input type='checkbox' id='${tab.windowId}' name='activeWindow' value='${tab.id}' /><label for='${tab.windowId}'>${tab.title}</label></div>`
    }
    div.innerHTML = content;
});

スクロール制御命令の送信

実際に画面のスクロールを担当するのは,Content ScriptsなのでポップアップからContent Scriptsに向けて命令を送信することになります.
そして,この命令の送信もchrome.tabs APIでできます.今回の開発ほぼtabsしか触ってないですね()
命令を送信したい対象のtabIdとJSON形式のデータをセットにしてメッセージを送信します.
データ取得の命令など,返却値が欲しい場合は先述のようにコールバックに対して処理を実装することもできます.
今回はユーザが選択したすべてのタブに対してスクロール制御命令を送ることで,複数ウィンドウの制御をしています.
スクロールの制御自体は後述しますが,Content Scriptsに任せるので,コールバックはなしです.

document.getElementById("start").onclick = function() {
    // 選択されたチェックボックスを取得
    let selectOption = document.querySelectorAll("input[name=activeWindow]:checked");
    for (option of selectOption) {
        // ここで命令送信
        chrome.tabs.sendMessage(Number(option.value), {method: "startScroll"})
    }
}

スクロールの実行

今回の記事のラストです.
ポップアップから送信されたメッセージの受信と,スクロール制御の2つからなります.
まずメッセージの受信です.
表記のとおり,chrome.runtime.onMessage.addListenerによって受信待ちをしているイメージです.
この中で,送信されたメッセージに対して実現したい任意の処理を書いてやります.
今回はmethodの種類で場合分けをしたのでswitch文での制御になっていますが,やり方は色々あると思います.

chrome.runtime.onMessage.addListener(function(request) {
    // 任意の処理
    switch (request.method) {
        case 'startScroll':
            if (state == 'scroll') {
                state = 'stop';
            } else {
                y = y >= document.documentElement.scrollHeight ? 0 : y;
                pace = 1;
                state = 'scroll';
            }
            scrollPage(5);
            break;
        ・・・
        case default:
            console.log('no method');
    }
})

最後にスクロール制御です.
ざっくりとは,window.scrollTo()で移動先を指定してスクロールを実行して,setTimeoutによってそれを一定間隔で繰り返し実行するだけです.
ただ,それだけではいつまで経ってもスクロールし続けてしまうため,以下の場合に停止するように条件を追加しています.

  • スクロール可能領域を超過(yの条件)
  • ユーザによる停止命令の発火(stateの条件)

現状ではあくまでもウィンドウのスクロールができるだけなので,ウィンドウ中の任意のスクロール領域を動かすことはできません.

function scrollPage(speed) {
    y += pace;
    window.scrollTo(0, y);
    if (y < document.documentElement.scrollHeight && state == 'scroll') {
        setTimeout(() => {
            scrollPage(speed);
        }, speed);
    } else {
        state = 'stop';
    }
}

おわりに

今回開発したものはGitHubにてmultiScrollerという名前で公開しています.
実はmanifestをv2で書いていたり,スクロールの終了がアイコンでわからなかったりと杜撰なとこがかなりありますが興味があれば見てみてください(それ以上にこの記事が…という説はある).
かなり用途が不明なので,何かしら使い道を見出してもらえたら喜びます.
複数人から別ファイルでもらったレビュー箇所の照らし合わせくらいはできますかね.

それでは,明日以降の記事もお楽しみください.

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?