通知 API の使用

註: この機能は Web Workers 内で利用可能です。

安全なコンテキスト用
この機能は一部またはすべての対応しているブラウザーにおいて、安全なコンテキスト (HTTPS) でのみ利用できます。

通知 API は、ウェブページやアプリからシステムレベルでページ外部に表示される通知を送ることを可能にします。これにより、アプリケーションがアイドルやバックグラウンドの状態であっても、アプリからユーザーに情報を送信することができます。この記事では、アプリで API を使用する方法の基本を見ていきます。

一般的にシステム通知とは、オペレーティングシステムの標準的な通知メカニズムを指します。一般的なデスクトップシステムやモバイル端末が、通知を行う方法の実例を思い出しましょう。

当然ながらシステム通知の仕組みはプラットフォームやブラウザーによって異なりますが問題はなく、通知 API はほとんどのシステム通知に対して十分な互換性を持つようになっています。

ウェブ通知の最も顕著な用途の一つが、ウェブベースのメールや IRC アプリケーションにおいて、新しいメッセージを受信したときに、ユーザーがほかのアプリケーションで何かをしていても通知をする必要がある場合です。これには数多くの事例が存在し、例えば Slack などがあります。

ウェブ通知がどのように利用できるのかの考えを深めるために、実世界の例を — To-do リストアプリ — を書いてみました。これは IndexedDB を使用してローカルにデータを格納し、タスクの期限が来たときにシステム通知を使用してユーザーに通知します。 To-do リストのコードをダウンロードするか、ライブで実行するアプリを見るかしてみましょう。

許可の要求

アプリが通知を送信できるようになる前に、ユーザーはアプリがそれを行う権限を認めなければなりません。これは API がウェブページの外部にあるものと対話しようとする際の一般的な要件です。ユーザは少なくとも 1 回はアプリケーションが通知を送ることを認めなければならず、これによりどのアプリやサイトが通知を表示してよいかをユーザーが制御することができます。

過去にプッシュ通知が悪用されることがあったため、ウェブブラウザーや開発者はこの問題を緩和するための対策を実装し始めています。通知を表示することの許可を求めるのは、ユーザーの操作 (ボタンをクリックするなど) の反応として行うべきです。ユーザーが同意していない通知でユーザーに迷惑をかけるべきではないので、これはベストプラクティスであるだけでなく、今後ブラウザーはユーザーの操作によって起動されたものではない通知の許可の要求を明示的に拒否するようになるでしょう。例えば、 Firefox はバージョン72からすでにこれを行っており、 Safari もしばらく前からこれを行っています。

加えて、 Chrome と Firefox では、サイトが安全なコンテキスト (すなわち HTTPS) ではない限り通知を要求することができず、また別オリジンの <iframe> から要求された通知を許可することができなくなりました。

現在の許可状態を確認する

読取専用の Notification.permission プロパティの値を調べると、すでに許可を得ているかを確認できます。このプロパティは、3 種類のいずれかの値を取ります。

default
ユーザーはまだ許可を求められたことがなく、したがって通知は表示されない。
granted
ユーザーは以前に通知表示の許可を求められており、許可した。
denied
ユーザーは、通知を表示することを明示的に拒否した。

許可を得る

通知を表示する許可をまだ得ていない場合は、アプリケーションは Notification.requestPermission() メソッドを使用してユーザーに要求する必要があります。もっとも簡単な形では、次のようなものがあります。

Notification.requestPermission().then(function(result) {
  console.log(result);
});

これはメソッドのプロミスベースの版を使用しています。古いバージョンに対応したい場合は、次のように古いコールバック版を使用する必要があります。

Notification.requestPermission();

コールバック版はオプションで、ユーザーが表示を許可する要求に答えた時に呼び出されるコールバック関数を受け入れます。

To-do リストのデモでは、 "Enable notifications" ボタンを配置し、押されたときにアプリの通知を要求します。

<button id="enable">Enable notifications</button>

これをクリックすると、 askNotificationPermission() 関数が呼び出されます。

function askNotificationPermission() {
  // function to actually ask the permissions
  function handlePermission(permission) {
    // Whatever the user answers, we make sure Chrome stores the information
    if(!('permission' in Notification)) {
      Notification.permission = permission;
    }

    // set the button to shown or hidden, depending on what the user answers
    if(Notification.permission === 'denied' || Notification.permission === 'default') {
      notificationBtn.style.display = 'block';
    } else {
      notificationBtn.style.display = 'none';
    }
  }

  // Let's check if the browser supports notifications
  if (!('Notification' in window)) {
    console.log("This browser does not support notifications.");
  } else {
    if(checkNotificationPromise()) {
      Notification.requestPermission()
      .then((permission) => {
        handlePermission(permission);
      })
    } else {
      Notification.requestPermission(function(permission) {
        handlePermission(permission);
      });
    }
  }
}

最初に 2 番目のメインブロックを見てみると、まず最初に通知に対応しているかどうかチェックしているのが分かります。もし対応していれば、次に Notification.requestPermission() のプロミスベース版に対応しているかどうかチェックします。対応している場合は、プロミスベース版 (Safari 以外で対応) を実行し、対応していない場合は古いコールバックベース版 (Safari で対応) を実行します。

コードの重複を避けるために、このスニペットの最初のメインブロックである handlePermission() 関数の中に、多少のハウスキーピングコードを格納しています。この内部では、 Notification.permission の値を明示的に設定し (古いバージョンの Chrome では自動的に設定できないものがありました)、ユーザーが許可ダイアログで選択した内容に応じてボタンを表示・非表示にしています。すでに許可されている場合は表示したくありませんが、ユーザーが許可を拒否することを選択した場合は、後で変更できるようにしたいと考えています。

注: バージョン 37 より前の Chrome では、 Notification.requestPermission()load イベントのハンドラー内で呼び出すことを許可していませんでした (issue 274284 をご覧ください)。

requestPermission() プロミスの機能検出

前に、ブラウザーがプロミス版の Notification.requestPermission() に対応しているかどうかをチェックする必要があると言いました。これは以下のようにして行います。

function checkNotificationPromise() {
    try {
      Notification.requestPermission().then();
    } catch(e) {
      return false;
    }

    return true;
  }

基本的には、 .then() メソッドが requestPermission() で利用できるかどうかを確認します。成功した場合は true を返します。失敗した場合は、 falsecatch() {} ブロック内で返します。

通知の作成

通知の作成は簡単です。 Notification コンストラクターを使用するだけです。このコンストラクターは通知内に表示するタイトルと、通知を拡張するためのアイコン (icon) やテキスト本文 (body) などのオプションを受け取ります。

例えば To-do リストの例では必要に応じて以下のスニペットを使用して通知を作成します (createNotification() 内にあります)。

var img = '/to-do-notifications/img/icon-128.png';
var text = 'HEY! Your task "' + title + '" is now overdue.';
var notification = new Notification('To do list', { body: text, icon: img });

通知を閉じる

close() を使用して、ユーザーに関係がなくなった通知を除去します (例えば、メッセージアプリなどで、ユーザーが既にウェブページ上の通知を読んだ場合や、音楽アプリですでに次の曲が始まっているため、曲の変更を行うための通知を閉じるなど)。最近のブラウザーの多くは、数秒 (約4秒) 経過すると通知を自動的に解除しますが、これはユーザーやユーザーエージェントの判断に委ねられているため、一般的には気にする必要はありません。通知の削除はオペレーティングシステムレベルでも発生する可能性があり、ユーザーが制御できるようにしておく必要があります。古いバージョンの Chrome は通知を自動的に削除しないので、他のブラウザーの通知トレイから通知を削除しないように、古いバージョンのブラウザーでのみ setTimeout() の後に削除してください。

var n = new Notification('My Great Song');
document.addEventListener('visibilitychange', function() {
  if (document.visibilityState === 'visible') {
    // The tab has become visible so clear the now-stale Notification.
    n.close();
  }
});

注: この API は、 (現代のブラウザーで) 一定時間経過後に通知を画面から消去するだけのために使用すべきではありません。通知が最初に表示された後にユーザーがそれとやりとりすることを防ぐため、このメソッドは通知トレイからも通知を削除するためです。

: "close" イベントを受け取ったとき、それが通知を閉じたユーザーであるという保証はありません。これは仕様書に準拠しており、以下のように記載されています。「通知が基礎となる通知プラットフォームによって、またはユーザーによって閉じられるとき、その通知のための閉じるステップを実行しなければなりません。」

Notification イベント

Notifications API の仕様では、Notification のインスタンスで発生するイベントを 2 つ定義しています:

click
ユーザーが通知をクリックしたときに発生します。
close
通知が閉じられたときに一度発生します。
error
通知で問題が発生したときに発生します。通常、なんらかの理由で通知が表示されなかったためです。
show
通知がユーザーに表示されたときに発生します。

これらのイベントは onclick, onclose, onerror, onshow の各ハンドラーを使用して追跡することができます。 NotificationEventTarget も継承していますので addEventListener() メソッドも使用することができます。

既存の通知を置き換える

ユーザが短期間に多くの通知を受け取ることは、通常望ましくありません。例えばメッセンジャーアプリがメッセージを受け取るたびに通知を行って、それが大量になったらどうなるでしょうか。大量の通知によるスパム状態を避けるため、準備中の通知キューを変更して 1 つ以上の未表示通知を新た通知で置き換えることができます。

これを行うために、任意の新たな通知にタグ付けすることができます。すでに同じタグがついている通知がまだ表示されていない場合は、新しい通知が以前の通知を置き換えます。同じタグがついている通知がすでに表示されている場合は、前の通知が閉じられて新しい通知が表示されます。

タグの例

以下の基本的な HTML を想定してください。

<button>Notify me!</button>

以下の方法で、複数の通知を扱うことが可能です。

window.addEventListener('load', function () {
  // 始めに、通知の許可を得ているかを確認しましょう
  // 得ていなければ、尋ねましょう
  if (window.Notification && Notification.permission !== "granted") {
    Notification.requestPermission(function (status) {
      if (Notification.permission !== status) {
        Notification.permission = status;
      }
    });
  }

  var button = document.getElementsByTagName('button')[0];

  button.addEventListener('click', function () {
    // 通知されることにユーザが同意している場合
    // 10 個の通知を送信してみましょう
    if (window.Notification && Notification.permission === "granted") {
      var i = 0;
      // 一部のブラウザ (Firefox を含む) は一定の期間内に大量の通知を行うとブロックするため、interval を使用します。
      var interval = window.setInterval(function () {
        // タグのおかげで、 "Hi!9" の通知だけが見えます
        var n = new Notification("Hi! " + i, {tag: 'soManyNotification'});
        if (i++ == 9) {
          window.clearInterval(interval);
        }
      }, 200);
    }

    // 通知を受けたいか否かをユーザが告げていない場合
    // 注: Chrome のために permission プロパティが設定されているかの確信が
    // 持てないため、値 "default" を確認するのは安全ではありません。
    else if (window.Notification && Notification.permission !== "denied") {
      Notification.requestPermission(function (status) {
        // ユーザーが許可している場合
        if (status === "granted") {
          var i = 0;
          // 一部のブラウザー (Firefox を含む) は一定の期間内に大量の通知を行うとブロックするため、interval を使用します。
          var interval = window.setInterval(function () {
            // タグのおかげで、 "Hi!9" の通知だけが見えます
            var n = new Notification("Hi! " + i, {tag: 'soManyNotification'});
            if (i++ == 9) {
              window.clearInterval(interval);
            }
          }, 200);
        }

        // 許可していなければ、通常型の alert にフォールバックします
        else {
          alert("Hi!");
        }
      });
    }

    // ユーザが通知を拒否している場合
    else {
      // 通常型の alert にフォールバックできます
      alert("Hi!");
    }
  });
});

結果は以下のとおりです:

仕様書

仕様書 状態 備考
Notifications API 現行の標準 Living standard

ブラウザーの互換性

Update compatibility data on GitHub
デスクトップモバイル
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung Internet
NotificationChrome 完全対応 22
補足
完全対応 22
補足
補足 Before Chrome 22, the support for notification followed an old prefixed version of the specification and used the navigator.webkitNotifications object to instantiate a new notification. Before Chrome 32, Notification.permission was not supported. Before Chrome 42, service worker additions were not supported. Starting in Chrome 49, notifications do not work in incognito mode.
完全対応 5
接頭辞付き
接頭辞付き webkit のベンダー接頭辞が必要
Edge 完全対応 14Firefox 完全対応 22
完全対応 22
完全対応 4
接頭辞付き
接頭辞付き moz のベンダー接頭辞が必要
IE 未対応 なしOpera 完全対応 25Safari 完全対応 6WebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 22
完全対応 22
完全対応 4
接頭辞付き
接頭辞付き webkit のベンダー接頭辞が必要
Opera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
Notification() constructorChrome 完全対応 22
完全対応 22
完全対応 5
接頭辞付き
接頭辞付き webkit のベンダー接頭辞が必要
Edge 完全対応 ≤18Firefox 完全対応 22
完全対応 22
完全対応 4
接頭辞付き
接頭辞付き moz のベンダー接頭辞が必要
IE 未対応 なしOpera 完全対応 25Safari 完全対応 6WebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 22
完全対応 22
完全対応 4
接頭辞付き
接頭辞付き moz のベンダー接頭辞が必要
Opera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
actionsChrome 完全対応 53Edge 完全対応 18Firefox 未対応 なしIE 未対応 なしOpera 完全対応 39Safari ? WebView Android 未対応 なしChrome Android 完全対応 53Firefox Android 未対応 なしOpera Android 完全対応 41Safari iOS 未対応 なしSamsung Internet Android 完全対応 6.0
badgeChrome 完全対応 53Edge 完全対応 18Firefox 未対応 なしIE 未対応 なしOpera 完全対応 39Safari ? WebView Android 未対応 なしChrome Android 完全対応 53Firefox Android 未対応 なしOpera Android 完全対応 41Safari iOS 未対応 なしSamsung Internet Android 完全対応 6.0
bodyChrome 完全対応 ありEdge 完全対応 14Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
closeChrome 完全対応 ありEdge 完全対応 14Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
dataChrome 完全対応 ありEdge 完全対応 16Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari ? WebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
dirChrome 完全対応 ありEdge 完全対応 14Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
iconChrome 完全対応 22
完全対応 22
完全対応 5
接頭辞付き
接頭辞付き webkit のベンダー接頭辞が必要
Edge 完全対応 14Firefox 完全対応 22
完全対応 22
完全対応 4
接頭辞付き
接頭辞付き moz のベンダー接頭辞が必要
IE 未対応 なしOpera 完全対応 25Safari 未対応 なしWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 22
完全対応 22
完全対応 4
接頭辞付き
接頭辞付き moz のベンダー接頭辞が必要
Opera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
imageChrome 完全対応 53Edge 完全対応 18Firefox 未対応 なしIE 未対応 なしOpera 完全対応 40Safari ? WebView Android 未対応 なしChrome Android 完全対応 53Firefox Android 未対応 なしOpera Android 完全対応 41Safari iOS 未対応 なしSamsung Internet Android 完全対応 6.0
langChrome 完全対応 ありEdge 完全対応 14Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
maxActionsChrome 完全対応 ありEdge 完全対応 18Firefox 未対応 なしIE 未対応 なしOpera 完全対応 ありSafari ? WebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 未対応 なしOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
onclickChrome 完全対応 ありEdge 完全対応 14Firefox 完全対応 22IE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 未対応 なしOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
oncloseChrome 完全対応 ありEdge 完全対応 14Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
onerrorChrome 完全対応 ありEdge 完全対応 14Firefox 未対応 なしIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 未対応 なしOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
onshowChrome 完全対応 ありEdge 完全対応 14Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
permissionChrome 完全対応 ありEdge 完全対応 14Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
renotifyChrome 完全対応 50Edge 完全対応 79Firefox 未対応 なしIE 未対応 なしOpera 完全対応 37Safari 未対応 なしWebView Android 未対応 なしChrome Android 完全対応 50Firefox Android 未対応 なしOpera Android 完全対応 37Safari iOS 未対応 なしSamsung Internet Android 完全対応 5.0
requestPermissionChrome 完全対応 46Edge 完全対応 14Firefox 完全対応 47
補足
完全対応 47
補足
補足 From Firefox 70 onwards, cannot be called from a cross-origin IFrame.
補足 From Firefox 72 onwards, can only be called in response to a user gesture such as a click event.
IE 未対応 なしOpera 完全対応 40Safari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 46Firefox Android 完全対応 ありOpera Android 完全対応 41Safari iOS 未対応 なしSamsung Internet Android 完全対応 5.0
requireInteractionChrome 完全対応 ありEdge 完全対応 17Firefox 未対応 なしIE 未対応 なしOpera 完全対応 ありSafari ? WebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 未対応 なしOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
Secure context requiredChrome 完全対応 62Edge 完全対応 ≤79Firefox 完全対応 67IE 未対応 なしOpera 完全対応 49Safari ? WebView Android 未対応 なしChrome Android 完全対応 62Firefox Android 完全対応 67Opera Android 完全対応 46Safari iOS 未対応 なしSamsung Internet Android 完全対応 8.0
silentChrome 完全対応 43Edge 完全対応 17Firefox 未対応 なしIE 未対応 なしOpera 完全対応 30Safari 未対応 なしWebView Android 未対応 なしChrome Android 完全対応 43Firefox Android 未対応 なしOpera Android 完全対応 30Safari iOS 未対応 なしSamsung Internet Android 完全対応 4.0
tagChrome 完全対応 ありEdge 完全対応 14Firefox 完全対応 ありIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 完全対応 ありOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
timestampChrome 完全対応 ありEdge 完全対応 17Firefox 未対応 なしIE 未対応 なしOpera 完全対応 ありSafari ? WebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 未対応 なしOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
titleChrome 完全対応 ありEdge 完全対応 14Firefox 未対応 なしIE 未対応 なしOpera 完全対応 ありSafari 完全対応 ありWebView Android 未対応 なしChrome Android 完全対応 ありFirefox Android 未対応 なしOpera Android 完全対応 ありSafari iOS 未対応 なしSamsung Internet Android 完全対応 あり
vibrateChrome 未対応 なしEdge 未対応 なしFirefox 未対応 なしIE 未対応 なしOpera 未対応 なしSafari ? WebView Android 未対応 なしChrome Android 完全対応 53
補足
完全対応 53
補足
補足 Does not work on Android O or later regardless of Chrome version.
Firefox Android 未対応 なしOpera Android 完全対応 41
補足
完全対応 41
補足
補足 Does not work on Android O or later regardless of Chrome version.
Safari iOS 未対応 なしSamsung Internet Android 完全対応 6.0
補足
完全対応 6.0
補足
補足 Does not work on Android O or later regardless of Chrome version.
Available in workersChrome 完全対応 45Edge 完全対応 ≤18Firefox 完全対応 41IE 未対応 なしOpera 完全対応 32Safari ? WebView Android 未対応 なしChrome Android 完全対応 45Firefox Android 完全対応 41Opera Android 完全対応 32Safari iOS 未対応 なしSamsung Internet Android 完全対応 5.0

凡例

完全対応  
完全対応
未対応  
未対応
実装状況不明  
実装状況不明
実装ノートを参照してください。
実装ノートを参照してください。
使用するには、ベンダー接頭辞または異なる名前が必要です。
使用するには、ベンダー接頭辞または異なる名前が必要です。

関連情報