マウスジェスチャーとかコンテキストメニューを dispatchEvent で表示させようとした調査とか

かざぐるマウスという Windows 向けのマウスジェスチャーアプリがありました。

現在は公開が中止されていて、ドメインも手放されたようです(その後同一ドメインが怪しいサービスに取られてしまっているので、アクセスしないようにしてください)。

Chromeマウスジェスチャーはかざぐるマウスに頼っていたのですが、Chrome がバージョンアップしてかざぐるマウスが機能しなくなってしまったので、Chrome 拡張を探してみました。

見つけたのはこれです。

この拡張はシンプルで、デフォルトの設定で十分機能するのですが、MacLinux には対応していないため、Windows 以外の OS ではこの拡張がむしろ邪魔になってしまいます。Windows 以外に対応できない理由は こちら に記載されていて、要するにバグだということです。

段階を追って説明します。

まず、Windows ではコンテキストメニューが表示されるタイミング、つまり contextmenu イベントは mouseup イベントの後に発生します。なので、mousedown → mousemove → mouseup → contextmenu という順番であるため、contextmenu イベントで、マウスジェスチャーが成立しているかどうかを判定して、コンテキストメニューを出す出さないを処理できます。

一方 Mac では mousedown → contextmenu → mousemove という順番でイベントが発生します。なんと、mouseup イベントはリスナーに通知されません。これではジェスチャーの終わりを判定できません。

contextmenu イベントで、コンテキストメニューを出さないように event.preventDefault() を行えば mouseup イベントが通知されます。じゃあ、contextmenu インベント発生時はとりあえずコンテキストメニューを表示しないように制御し、mouseup のタイミングでジェスチャーが成立していなければイベントを生成して dispatchEvent() でそのタイミングでコンテキストメニューを表示させる、という案はどうか。

これを試してみましたが、期待通りには行きません。具体的には contextmenu イベントを発生させること自体はできるのですが、コードで発生させたイベントではコンテキストメニューは現れない、ということがわかりました。

それはこういうことだと理解しました。

ボタン1をクリックするとボタン2にクリックイベントを発生させる処理があったとして、ボタン2のイベント発生時にボタンを実際にクリックしたようなへこむエフェクトが表示されるか。これは以下のコードで確認できますが、されません。

<script>
function button2Click(element) {
    var evt = element.ownerDocument.createEvent('MouseEvents');
    evt.initMouseEvent('click', true, true,
         element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
         false, false, false, 1, null);

    if (document.createEventObject){ 
        // dispatch for IE
       return element.fireEvent('onclick', evt)
     } else {
       // dispatch for Chrome, Firefox or others
      return !element.dispatchEvent(evt);
    }
}

function button1Click() {
    button2Click(document.getElementById("button2"));
}

</script>
<body>
<button id="button1" onclick="button1Click()">button 1</button>
<button id="button2" onclick="alert('hello')">button 2</button>
</body>

イベント自体をコードで発生させることはできるものの、GUI には影響を与えないのではないか、という結論です。

stackoverflow にいくつか質問があるのですが、この中で一番わかりやすい回答は

You can't. That's totally impossible.

javascript - Generate an event context menu - Stack Overflow

ですね。コンテキストメニューはコードから表示させられないんです。


閑話休題

Chrome 用のマウスジェスチャーはどうしたかというと、以下を使うようにしました。

これは、Gestures for Google Chrome から派生した拡張で、コンテキストメニュー表示時の mouseup イベントが通知されない問題にどのように対応しているかというと、右クリックでコンテキストメニュー表示を、右ダブルクリックでコンテキストメニュー表示に変更して対応しています。なるほど、それなら確かに干渉しないので、うまい方法だと思います。僕はこの右ダブルクリック機能を Mac であえて無効にして、Mac ではマウスジェスチャーを使用しない、という方向で落ち着きそうです。


なお、Windows は mouseup の後に contextmenu が通知されるのに対し、Mac は mouseup より前に contextmenu が通知されるのは、表示されたコンテキストメニュー上にマウスを移動し、そこでマウスを離すことで項目が選択できるという OS のポリシーです。Wiondows はドラッグした先のターゲットでコンテキストメニューの内容を変更できるので、どちらの考え方も正しくて、たまたま右クリックでマウスジェスチャーという新たな概念が Mac UI にはマッチしなかった、ということだと思います。