要約
Gmailの「スヌーズ期間…」メニューのアイテムをユーザースクリプトで自動クリックする時、element.click()
では反応せず、mousedown
イベントとmouseup
イベントを連続で発火させたら反応しました。
// 動かない例
targetItem.click();
// 動く例
targetItem.dispatchEvent(
new MouseEvent("mousedown", {
bubbles: true,
cancelable: true
})
);
targetItem.dispatchEvent(
new MouseEvent("mouseup", {
bubbles: true,
cancelable: true
})
);
今回は「スヌーズ期間…」メニューしか調査していませんが、それ以外のポップアップメニューでも同じかもしれないのでメモとして残しておきます。また、今回はTampermonkeyでユーザースクリプトを記述していますが、拡張機能でもおそらく同じことだと思います。
※正直「何故こうなのか」は分かっておらず偶然上手く動いているだけかもしれません。将来的に動かなくなる可能性は十分にありますので参考にする際は自己責任でお願いします。
検証環境
OS: Windows 11 Home 23H2
ブラウザ: Chrome 132.0.6834.84(Official Build) (64 ビット) 日本語環境
Tampermonkey: 5.3.3
Gmail: 2025-0116時点版
事の発端
Gmailで「スヌーズ」を選択すると「スヌーズ期間…」メニューがポップアップ表示されます。
私はプリセットの時間を使わず毎回「日付と時間を選択」をクリックしていたため面倒臭くなり、これをスキップしたくなりました。つまり、「スヌーズ期間…」メニューが出現したら自動で即座に「日付と時間を選択」をクリック(したことに)させたかったのです。
「スヌーズ期間…」メニューのDOM構造
調べてみると「スヌーズ期間…」メニューの正体は次のようなDOM構造で、body
配下にappendされることが分かりました(かなり簡略化しています。本当はもっと色々な属性があり、div
タグの構造も深いです)。
<div role="menu" aria-label="スヌーズ メニュー">
<div>スヌーズ期間…</div>
<div role="menuitem">今日中 木 18:00</div>
<div role="menuitem">明日 金 8:00</div>
<div role="separator"></div>
<div role="menuitem">日付と時間を選択</div>
</div>
DOMの変化を監視してariaLabel === "スヌーズ メニュー"
のエレメントが現れたらその配下にある目的のmenuitem
をクリックすればやりたいことが実現できる気がしました。
click
版ユーザスクリプト(失敗)
早速、次のようなユーザスクリプトを書いてTampermonkeyで動かしてみました。
// ==UserScript==
// @name SkipGmailSnoozeMenu
// @description Gmailの「スヌーズ期間…」メニューをスキップする(注意:これは動きません)
// @match https://mail.google.com/*
// ==/UserScript==
(function () {
"use strict";
// body配下の変更を監視する。
new MutationObserver(observeSnoozeMenu).observe(document.body, {
childList: true
});
// 「スヌーズ期間…」メニューが出現したら即座に「日付と時間を選択」を選択する。
function observeSnoozeMenu(mutations) {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
// 「スヌーズ期間…」メニューにだけ反応する
const isSnoozeMenu =
node instanceof Element && node.ariaLabel === "スヌーズ メニュー";
if (!isSnoozeMenu) {
continue;
}
// 「日付と時間を選択」アイテムのDOMを取得
const targetItem = Array.from(
node.querySelectorAll("[role=menuitem]")
).find((e) => e.textContent === "日付と時間を選択");
if (!targetItem) {
throw new Error("GmailのDOM構造が変わった可能性があります");
}
// 「日付と時間を選択」アイテムをクリックする(★注意:動かない)
targetItem.click();
}
}
}
})();
結果、動きませんでした。深追いはしませんが、どうやらGmailは単純にclickイベントハンドリングしているだけはないようです。
mousedown
,mouseup
イベント版ユーザスクリプト(成功)
試行錯誤の結果、menuitem
のDOMに対してmousedown
イベントとmouseup
イベントを連続で発火させるとクリックしたことになる事が判明しました。先程のコードのtargetItem.click()
の部分を次のように書き換えたら狙い通り動くようになりました。
// 「日付と時間を選択」アイテムをクリックする
// `targetItem.click()`では動かないため`mousedown`イベントと`mouseup`イベントを連続で発火させる。
targetItem.dispatchEvent(
new MouseEvent("mousedown", {
bubbles: true,
cancelable: true
})
);
targetItem.dispatchEvent(
new MouseEvent("mouseup", {
bubbles: true,
cancelable: true
})
);
成功版コードの全量
// ==UserScript==
// @name SkipGmailSnoozeMenu
// @description Gmailの「スヌーズ期間…」メニューをスキップする
// @match https://mail.google.com/*
// ==/UserScript==
(function () {
"use strict";
// body配下の変更を監視する。
new MutationObserver(observeSnoozeMenu).observe(document.body, {
childList: true
});
// 「スヌーズ期間…」メニューが出現したら即座に「日付と時間を選択」を選択する。
function observeSnoozeMenu(mutations) {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
// 「スヌーズ期間…」メニューにだけ反応する
const isSnoozeMenu =
node instanceof Element && node.ariaLabel === "スヌーズ メニュー";
if (!isSnoozeMenu) {
continue;
}
// 「日付と時間を選択」アイテムのDOMを取得
const targetItem = Array.from(
node.querySelectorAll("[role=menuitem]")
).find((e) => e.textContent === "日付と時間を選択");
if (!targetItem) {
throw new Error("GmailのDOM構造が変わった可能性があります");
}
// 「日付と時間を選択」アイテムをクリックする
// `targetItem.click()`では動かないため`mousedown`イベントと`mouseup`イベントを連続で発火させる。
targetItem.dispatchEvent(
new MouseEvent("mousedown", {
bubbles: true,
cancelable: true
})
);
targetItem.dispatchEvent(
new MouseEvent("mouseup", {
bubbles: true,
cancelable: true
})
);
}
}
}
})();
このユーザスクリプトは2025-0116現在のGmailの実装に強く依存しており、Gmailの実装変更に極めて弱いことに注意してください。特にariaLabelやtextContentは変更されやすい属性であり、長期的に見ると動かなくなる可能性が高いです。必要に応じてDOM構造の確認やスクリプトの更新を行ってください。