4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Android スマホで Chrome Extensions と UserJS を動かす

Last updated at Posted at 2025-11-30

はじめに

ACCESS Advent Calendar 2025 の初日です。

今年一番、子供にドヤれたことは「マリオカートワールドのフリーランの『マリオカートスタッフもビックリ! 1〜6』を全部クリアして『パパスゴーイ!』と言わせたこと」の渡邉直彦です。

Android スマホで Chrome Extensions (Chrome 拡張機能)と UserJS を動かす方法をまとめます!

構成

  • Android スマホ
    • Microsoft Edge Canary
      • 個別の Chrome Extensions
      • Tampermonkey (PC 側と同期)
        • 個別の UserJS

Android 版の Microsoft Edge Canary に Tampermonkey という UserJS 管理の拡張機能をインストールし、 PC と同期させて UserJS を実行できるようにしています。

利点

便利!

  • Chrome Extensions
    • Chrome ウェブストアの Chrome Extensions が Android から使える(一部だけども)
  • UserJS
    • PC と同じ UserJS 環境を Android でも動作可能
    • PC で開発して同期機能で Android へ送り動作可能(スマホで開発は不要)

動作確認環境

項目 詳細
OS Android
ブラウザ Microsoft Edge Canary
Chrome Extensions 実行環境 Tampermonkey

手順

共通

Android で Chrome Extensions を動かす

  • Edge Canary から好きな個別の Chrome Extension をインストール

    • Android 版 Edge の拡張機能サポートはまだ発展途上です。全ての Chrome 拡張機能が動くわけではないです
    • 2025/11 執筆時点で僕が数えて約40個。
      • 個別に動作確認している?

Android で UserJS を動かす

  • PC Chrome に Tampermonkey をインストール

  • Android Edge に Tampermonkey をインストール

    • URL 同上
  • Tampermonkey の同期設定

    • PC 版と Android 版の Tampermonkey の設定画面を開く
      • 「設定のモード」を「上級者」へ(以下の「UserScript 同期」項目が出現する)
      • 「UserScript 同期」項目の「UserScript 同期を有効にする」チェックを ON
  • PC で個別の UserJS 開発&動作確認

  • 個別の UserJS を PC から Android へ渡す

    • PC 版と Android 版の Tampermonkey の設定画面を開く
      • 「UserScript 同期」項目の「今すぐ実行」ボタンを押下
  • Android 側で個別の UserJS が動作することを確認する

スクショ

Android Edge Canary

  • 起動画面
    • image.png
  • メニュー表示
    • image.png
  • 「拡張機能」押下時
    • image.png
  • 「拡張機能の管理」押下時
    • 「購入」となってるけど無料。多分「入手」あたりの誤訳?
    • image.png

UserJS

  • PC
    • Tampermonkey 「全般」
      • image.png
    • Tampermonkey 「UserScript 同期」
      • image.png

僕の使用例:自作ツールと英語学習

Chrome Extension

YouTube 動画で日英字幕併記の翻訳学習

最近英語に慣れる必要がある状況なので積極的に触れるべく、PC Chrome だと language-reactor で「字幕日英併記」状態にして Youtube 動画見てます。
残念ながら Android Edge では language-reactor は使えないので、 「没入型翻訳 - Immersive Translate」でほぼ同じ環境を作ってます。

スクショ

  • 公式
    • language-reactor
      • image.png
    • 「没入型翻訳 - Immersive Translate」
      • image.png
  • 手元の Android Edge Canary
    • 「没入型翻訳 - Immersive Translate」
      • image.png
    • Youtube 動画
      • https://www.youtube.com/watch?v=e-MiGGlThRk
      • 最近「ゼルダの伝説 ブレス オブ ザ ワイルド」に(Switch1 持ってなく Switch2 からデビューしたので今更)ハマってるので、趣味と実益を兼ね?、英語の攻略動画を見てます。
      • お陰様で「剣の試練 極位」クリア出来た!!
      • image.png

UserJS

個別の UserJS は数十個作って動かしてます。
その中でイメージしやすいものとして、1番世間的にユーザー数多いであろう Money Forward の「合計行追加」 UserJS の例です。

  • スクショ
    • Android Edge Canary の Tampemonkey
      • image.png
      • image.png
    • Money Forward の各項目に「合計」行追加
      • 見せやすいものがこれ(企業型DC、毎月2千円積立)しかなく、元々一行しかないので合計行は不要だが例として。。
      • 複数行あっても勿論ちゃんと合計してくれる
      • image.png

コード

// ==UserScript==
// @name         [moneyforward] 合計行追加
// @namespace    http://tampermonkey.net/
// @version      1.8
// @description  [moneyforward] 個別口座、ポートフォリオ、その他指定テーブルに合計行を追加し、ソート後も末尾に固定
// @match        https://*.moneyforward.com/accounts/show/*
// @match        https://*.moneyforward.com/bs/portfolio*
// @grant        none
// ==/UserScript==

(function() {
  'use strict';
  console.debug('[MF合計] START');

  const SUM_KEYWORDS = ['評価額', '前日比', '評価損益', '取得価額', '現在価値'];
  const RATE_KEYWORD = '損益率';

  function addTotalRow(table) {
    const thead = table.querySelector('thead');
    const tbody = table.querySelector('tbody');
    if (!thead || !tbody) return;
    const headers = Array.from(thead.querySelectorAll('th'));

    // 合計対象列と損益率列を特定
    const sumCols = [];
    let rateCol = -1;
    headers.forEach((th, i) => {
      const text = th.innerText.trim();
      if (text.includes(RATE_KEYWORD)) rateCol = i;
      if (SUM_KEYWORDS.some(k => text.includes(k)) && !text.includes(RATE_KEYWORD)) sumCols.push(i);
    });
    if (sumCols.length === 0) return;

    // 各列の合計を計算
    const sums = {};
    sumCols.forEach(i => sums[i] = 0);
    tbody.querySelectorAll('tr').forEach(tr => {
      const cells = tr.querySelectorAll('td');
      sumCols.forEach(i => {
        const raw = cells[i]?.innerText.replace(/[,円%%−‐]/g, '').trim() || '';
        const num = parseFloat(raw);
        if (!isNaN(num)) sums[i] += num;
      });
    });

    // コスト基準と利益を識別
    const costIdx = sumCols.find(i => {
      const label = headers[i].innerText;
      return label.includes('評価額') || label.includes('取得価額');
    });
    const profitIdx = sumCols.find(i => headers[i].innerText.includes('評価損益'));
    let rateValue = null;
    if (costIdx != null && profitIdx != null && sums[costIdx] !== 0) {
      rateValue = sums[profitIdx] / sums[costIdx] * 100;
    }

    // 合計行生成
    const totalTr = document.createElement('tr');
    totalTr.dataset.isTotal = 'true';
    headers.forEach((_, j) => {
      const td = document.createElement('td');
      const headerText = headers[j].innerText;
      const isDiffCol = headerText.includes('前日比');
      const isProfitCol = headerText.includes('評価損益');
      const isRateCol = headerText.includes('損益率');
      // numberクラスを合計値セルに付与
      if (j === 0) {
        td.innerText = '合計';
      } else if (sumCols.includes(j)) {
        td.classList.add('number');
        const span = document.createElement('span');
        let value = sums[j].toLocaleString() + '';
        span.innerText = value;
        if (isDiffCol || isProfitCol) {
          if (sums[j] > 0) span.className = 'plus-color';
          else if (sums[j] < 0) span.className = 'minus-color';
        }
        td.appendChild(span);
      } else if (j === rateCol && rateValue != null) {
        td.classList.add('number');
        const span = document.createElement('span');
        span.innerText = rateValue.toFixed(2) + '%';
        if (rateValue > 0) span.className = 'plus-color';
        else if (rateValue < 0) span.className = 'minus-color';
        td.appendChild(span);
      }
      totalTr.appendChild(td);
    });
    tbody.appendChild(totalTr);

    // ソート後に合計行を末尾に移動
    const reposition = () => {
      const total = tbody.querySelector('tr[data-is-total]');
      if (total) tbody.appendChild(total);
    };
    headers.forEach(th => th.addEventListener('click', () => setTimeout(reposition, 50)));

    console.debug('[MF合計] 合計行追加完了');
  }

  window.addEventListener('load', () => {
    document.querySelectorAll('table.table.table-bordered').forEach(table => {
      const ths = Array.from(table.querySelectorAll('thead th'));
      if (ths.some(th => ['銘柄コード', '銘柄名', '名称'].some(key => th.innerText.includes(key)))) {
        addTotalRow(table);
      }
    });
  });
})();

まとめ

Android スマホでも UserJS や一部の Chrome Extensions が動くのは本当に便利!

おわりに

ACCESS Advent Calendar 2025 、明日の担当は @aym です。お楽しみに!🎉

4
0
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
4
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?