WindowsのCtrlキーとMacのCommandキーを識別する

はじめに

WindowsとMacでは修飾キー (CtrlキーやCommandキー(⌘)) の役割りが異なります。このためプログラムの操作性を統一するには、プログラムの中でシステムと修飾キーの組み合わせを識別して、操作性を統一する必要があります。本稿ではその方法について解説しています。

修飾キーの違い

WindowsとMacでは、マウス操作やキーボード操作と組み合わせる修飾キーに違いがあります。例えばよく使われるコピー&ペーストの場合は、次のようになります。

  • Windows: Ctrl+c & Ctrl+v
  • Mac: Command+c & Command+v

このような修飾キーの使い方について、WindowsとMacの対応を表にしてみました。カッコ内はマウスイベントやキーボードイベントで通知される修飾キーの値です。

WindowsMac備考
Ctrlキー (ctrlKey)Commandキー (metaKey)選択項目の追加・除外・コピーなど
Shiftキー (shiftKey)Shiftキー (shiftKey)選択範囲の追加・除外など
Altキー (altKey)Optionキー (altKey)
Ctrlキー (ctrlKey)コンテキストメニューの表示
Winキー (metaKey)Windowsメニューの表示

Ctrlキー、Commandキーについては、WindowsとMacで異なる意味を持つことが分かります。これらの修飾キーを利用するプログラムでは、WindowsとMac環境でキーを識別して処理を分ける必要があります。(Linux系のシステムも修飾キーの使い方はWindowsと同様であるため、Macとそれ以外の環境と考えても差し支えありません)

CtrlキーとCommandキーを見分けるコードの例

いきなりですが、例としてWindowsではCtrlキーの押下を、MacではCommandキーの押下を判別して表示するサンプルコードを次に示します。

<!DOCTYPE html>
<html>

<head charset="utf-8">
</head>

<body>
    <div id="ClickMe">
        Click Me !!
    </div>
</body>

<script>
    const target = document.getElementById('ClickMe');

    target.addEventListener('click', (event) => {
        if (isWinCtrlOrMacCommandKey(event)) {
            alert('Ctrl or Command Key Pressed');
        }
    });

    function isWinCtrlOrMacCommandKey(event) {
        let isMac;

        if (navigator.userAgentData) {
            isMac = navigator.userAgentData.platform.toUpperCase().indexOf('MAC') >= 0;
        }
        else {
            isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
        }

        if (isMac) {
            return event.metaKey;
        }
        else {
            return event.ctrlKey;
        }
    }
</script>

</html>

実行すると”Click Me”と表示されるのでマウスでクリックします。Ctrlキー (Windows) または Commandキー (Mac) を押しながらクリックすると、アラート (‘Ctrl or Command Key Pressed’) が表示されます。

このコードの内容について説明していきます。

WindowsとMacの識別

WindowsとMacで異なる修飾キーに対応するには、ブラウザがどのシステムで動作しているかを識別する必要があります。この識別は次の何れかのプロパティを確認することで行えます。

プロパティMacの場合に得られる値
navigator.platformChrome, Firefox, Edge, Safari: “MacIntel”
navigator.userAgentData.platformChrome, Edge: “macOS”
Firefox, Safari: undefined

「得られる値」は筆者の環境で試したものです。

navigator.platformは執筆時点 (2024/10) で主要なブラウザでサポートされています。ただしMDNによれば仕様としてはDeprecated (廃止) となっていて「将来はサポートされなくなる可能性あり」とされています。一方のnavigator.userAgentData.platformは、navigator.userAgentDataの仕様がExperimentalとなっていて、FirefoxやSafariではサポートされていません。

このようなサポート状況を踏まえて、まずnavigator.userAgentDataを調べて、定義されていない場合はnavigator.platformを調べることで、Macかどうかを判別しています。

    let isMac;

    if (navigator.userAgentData) {
        isMac = navigator.userAgentData.platform.toUpperCase().indexOf('MAC') >= 0;
    }
    else {
        isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
    }

各プロパティに設定される値の明確な定義は見つけられなかったのですが、MDNでもこの方法 (‘MAC’を含むかどうかで判定) が使われているので、とりあえず大丈夫と判断しました。

修飾キーの識別

修飾キーが押されているかどうかは、イベントハンドラに渡されるイベントオブジェクトに定義されています。サンプルコードの場合は、イベントオブジェクトはMouseEventオブジェクトで、次のようなプロパティが定義されています。それぞれのキーが押下されているとtrueになります。同じプロパティはKeyboardEventオブジェクトにも定義されています。

MouseEvent.ctrlKey
MouseEvent.shiftKey
MouseEvent.altKey
MouseEvent.metaKey

サンプルコードでは、Macの場合はmetaKeyの状態を、それ以外ではctrlKeyの状態を返しています。

    if (isMac) {
        return event.metaKey;
    }
    else {
        return event.ctrlKey;
    }

TypeScriptの場合

TypeScriptの場合は、関数定義を次のように変更します。マウスとキーボードの両方で使えます。また型定義への参照を追加します。

/// <reference types="user-agent-data-types" />

function isWinCtrlOrMacCommandKey(event: MouseEvent | KeyboardEvent) {
}

TypeScriptのコードでは、navigator.userAgentDataが定義されていない、とのエラーが発生します。これはuserAgentDataがExperimentalのため、標準の型定義には含まれていないためです。このuserAgentDataの型定義が別途提供されているのでインストールします。

npm i -D user-agent-data-types

またこの型定義を参照するために型定義への参照を宣言します。

/// <reference types="user-agent-data-types" />

対象となるファイルが多い場合は、tsconfig.jsonに追加する方法もあります。

{
  compilerOptions: {
    /* 他の定義 */
    types: [
      "node_modules/user-agent-data-types"
    ]
  }
}

ただしtypesを明示的に定義すると、暗黙にインクルードされていた@typeディレクトリ下の定義が読み込まれなくなるので、必要に応じて明示的に定義に加える必要があります。

終わりに

利用環境をWindowsからMacに移したことで、WindowsとMacではCtrlキーの働きが異なることを初めて知りました。それまではMacのキーボードにもCtrlキーはあるので、てっきりWindowsと同じと思っていたのです。そこでWindowsとMacでCtrlキーとCommandキーを使い分ける方法を調べた結果をまとめてみました。

編集履歴

日付内容
2024/10/20関数名を変更 isCtrlOrMetaKey -> isWinCtrlOrMacCommandKey
2024/10/17初版リリース