Chrome拡張機能:サムネイルをクリックしたらページ遷移せずに元画像を表示する


はじめに

やりたいこと

サムネイル画像をクリックしたら、ページ遷移せずに元画像を上からかぶせるようにプレビュー表示する。

制限事項

  • 元画像が別ドメインにあると、content_scriptsから取得できない。
  • content_scriptsからページ内のイベントハンドラを上書きできない。

content_scriptsからページ内にscript要素を埋め込んで、ページ上でスクリプトファイルを実行する。

手順1:ページ内で読み込ませるスクリプトファイルを登録する

ページ内で読み込ませるスクリプトファイルを、manifest.jsonのweb_accessible_resourcesに追加する。


"web_accessible_resources": [
    "bower_components/jquery/dist/jquery.min.js",
    "scripts/preview.js"
  ],

手順2:ページ内にscript要素を埋め込む

chrome.runtime.getManifest().web_accessible_resourcesにて、スクリプトファイルの一覧が取得できる。これをchrome.extension.getURL()でURLに変換し、script要素を生成する。

content_scripts.js
const scripts = chrome.runtime.getManifest().web_accessible_resources;

const next = () => {
  let script = scripts.pop();

  if (!script) {return;}

  $('<script></script>', {
    type: 'text/javascript',
    src: chrome.extension.getURL(script),
    on: {ready: next()}
  }).appendTo('body');
};

next();

手順3:プレビューの実装

preview.js
//リンクをたどって元画像のURLを取得する
const getImageUrl = async (url) => {
  ...
  return Promise.resolve(imageUrl);
};

//メインの処理
const initialize = () => {
  //元画像を表示するコンテナを生成する
  const container = $('<div></div>', {
    id: 'PreviewContainer',
    click: function(){
      //クリックされたら非表示にする
      $(this).hide();
      //履歴を一つ戻る
      window.history.back();
    }
  }).appendTo('body');
  container.hide();

  //サムネイルのリンクを無効にする
  $('#ThumbnailContainer').on('click', 'a', false);

  //サムネイル画像をクリックしたら元画像を表示する
  $('#ThumbnailContainer').on('click', 'img', async function(){
    //元画像のURLを取得
    let imageUrl = await getImageUrl($(this).parent('a').attr('href'));

    //元画像をコンテナの背景画像として表示する
    let container = $('#PreviewContainer');
    container.css('background-image', 'url(' + imageUrl + ')');
    container.show();

    //履歴を一件追加
    window.history.pushState(null, null, null);
  });

  //戻るボタンが押されたら元画像を非表示にする
  window.addEventListener('popstate', function (e) {
    if (container.css('display') === 'block') {
      $('#PreviewContainer').hide();
    }
  }, false);
};

//待機
const setTimeoutAsync = delay => {
  return new Promise(resolve => {
    setTimeout(resolve, delay);
  });
};

//jQueryの読み込み待ち
const limit = 5;
const checkJquery = async (count) => {
  if (!$ && count < limit) {
    await setTimeoutAsync(100);
    checkJquery(count++);
  } else {
    initialize();
  }
};
checkJquery(0);

元画像をクリックするとプレビュー表示が消えるようにしたが、癖で戻るボタンのショートカットを実行してしまうことが多かったので、戻るボタンでもプレビュー表示が消えるようにした。