---
# System prepended metadata

title: Notifications API瀏覽器通知
tags: [Javascript]

---

---
tags: Javascript
---

# Notifications API瀏覽器通知

## 通知 API

[Notifications API](https://developer.mozilla.org/en-US/docs/Web/API/Notifications_API)允許我們向用戶顯示通知。 它非常強大且易於使用。在可能的情況下，它使用與原生應用程序相同的機制，提供完全原生的外觀和感覺。

我們可以將 `Notifications API` 分成兩個核心區域（這些是非技術性的，也不屬於規範的一部分）。調用API (**Invocation API**) 控制如何使您的通知出現，包括樣式和振動。我們從頁面（或從服務器，在推送通知的情況下）創建（或調用）通知。交互 API 控制用戶與通知互動時發生的情況。 用戶交互在 service worker 中處理。

### 請求許可

在創建通知之前，我們需要獲得用戶的許可。放在主JS文件中

```javascript=
Notification.requestPermission(function(status) {
    console.log('Notification permission status:', status);
});
```

### 顯示通知

可以使用`showNotification`方法（“調用 API”）顯示來自應用程序主腳本的通知 。這是一個例子：

```javascript=
function displayNotification() {
  if (Notification.permission == 'granted') {
    navigator.serviceWorker.getRegistration().then(function(reg) {
      reg.showNotification('Hello world!');
    });
  }
}
```

#### 通知選項

`showNotification`方法有一個可選的第二個參數來配置通知。

```javascript=
function displayNotification() {
  if (Notification.permission == 'granted') {
    navigator.serviceWorker.getRegistration().then(function(reg) {
      var options = {
        body: 'Here is a notification body!',
        icon: 'images/example.png',
        vibrate: [100, 50, 100],
        data: {
          dateOfArrival: Date.now(),
          primaryKey: 1
        }
      };
      reg.showNotification('Hello world!', options);
    });
  }
}
```

1. 該`body`選項將主要描述添加到通知中。為用戶提供足夠的信息來決定如何對其採取行動。

1. 該`icon`選項附加圖像以使通知更具視覺吸引力，但也與用戶更相關。例如，如果是來自他們朋友的消息，您可能會包含發件人頭像的圖像。

1. 該`vibrate`選項指定接收通知的電話的振動模式。在我們的示例中，手機會振動 100 毫秒，暫停 50 毫秒，然後再次振動 100 毫秒。

1. 該`data`選項將自定義數據附加到通知，以便服務工作者可以在用戶與通知交互時檢索它。例如，向數據添加唯一的`“id”`或`“key”`選項可以讓我們確定當服務工作者處理點擊事件時點擊了哪個通知。


[測試通知選項網站](https://tests.peter.sh/notification-generator/)

#### 向通知添加操作

向用戶發送簡單通知，並在用戶點擊通知時，處理通知的基本互動，可以在通知中添加上下文，就可以讓使用者在不前往網站的情況下與網站互動。

```javascript=
function displayNotification() {
  if (Notification.permission == 'granted') {
    navigator.serviceWorker.getRegistration().then(function(reg) {
      var options = {
        body: 'Here is a notification body!',
        icon: 'images/example.png',
        vibrate: [100, 50, 100],
        data: {
          dateOfArrival: Date.now(),
          primaryKey: 1
        },
        actions: [
          {action: 'explore', title: 'Explore this new world',
            icon: 'images/checkmark.png'},
          {action: 'close', title: 'Close notification',
            icon: 'images/xmark.png'},
        ]
      };
      reg.showNotification('Hello world!', options);
    });
  }
}
```

要使用一組自定義操作的按鈕，我們在通知選項物件中添加一個操作數組。這些物件定義要向用戶顯示的操作按鈕。

動作可以有一個標識符字符串，一個包含要向用戶顯示的文本的標題，以及一個包含要在動作旁邊顯示的圖像的位置的圖標。


### 監聽事件

現在我們需要在 `service worker` 中處理與通知互動（使用`“Interaction API”`）。用戶看到通知後，他們可以將其關閉或對其採取行動。

#### 通知點擊事件

當用戶點擊通知時處理。點擊後會在`service worker`內部觸發一個`notificationclick`事件。

讓我們看一下在 `service worker` 中處理 `click` 事件的代碼。

```javascript=
self.addEventListener('notificationclick', function(e) {
  var notification = e.notification;
  var primaryKey = notification.data.primaryKey;
  var action = e.action;

  if (action === 'close') {
    notification.close();
  } else {
    clients.openWindow('http://www.example.com');
    notification.close();
  }
});
```

可以看到當通知的操作為`close`則關閉通知，否則開啟網頁

### 檢查權限

始終檢查使用通知 API 的權限。請務必檢查是否已授予權限，因為狀態可能會更改：

```javascript=
if (Notification.permission === "granted") {
  /* do our magic */
} else if (Notification.permission === "blocked") {
 /* the user has previously denied push. Can't reprompt. */
} else {
  /* show a prompt to the user */
}
```

## 實作

利用vscode [LiveServe](https://marketplace.visualstudio.com/items?itemName=ritwickdey.LiveServer)套件，建立即時更新的本機伺服器，不需再存檔後重整頁面來確認。

### 建立`main.html`

```htmlembedded=
<!DOCTYPE html>
<html lang="en">
  <head>
    <script>
      Notification.requestPermission(function (status) {
        console.log("Notification permission status:", status);
        registerServiceWorker();
      });

      function registerServiceWorker() {
        if (!("serviceWorker" in navigator)) {
          console.log("Service workers aren't supported in this browser.");
          return;
        }

        navigator.serviceWorker
          .register("/sw.js") // 註冊 service worker
          .then(() => initialiseServiceWorker());
      }

      function initialiseServiceWorker() {
        if (Notification.permission === "granted") {
          /* do our magic */
          displayNotification();
        } else if (Notification.permission === "blocked") {
          /* the user has previously denied push. Can't reprompt. */
          console.log("用戶之前拒絕推送。 無法重新提示");
          var info = document.querySelector(".info");
          info.innerHTML = "您之前拒絕推送。 無法重新提示";
        } else {
          /* show a prompt to the user */
          var info = document.querySelector(".info");
          info.innerHTML = "請允許通知";
        }
      }

      function displayNotification() {
        if (Notification.permission == "granted") {
          navigator.serviceWorker.getRegistration().then(function (reg) {
            // reg記得在初始化之後，不然會是undefined
            var options = {
              body: "Here is a notification body!",
              icon: "/notification.png",
              vibrate: [100, 50, 100],
              data: {
                dateOfArrival: Date.now(),
                primaryKey: 1,
              },
              actions: [
                {
                  action: "explore",
                  title: "點擊前往新世界",
                  icon: "/share.png",
                },
                {
                  action: "close",
                  title: "關閉通知",
                  icon: "/close.png",
                },
              ],
            };
            reg.showNotification("Hello world!", options);
          });
        }
      }
    </script>
  </head>
  <body>
    <h1>Hello world!</h1>
    <p>
      <span class="info"></span>
      <button onclick="displayNotification()">發送通知</button>
    </p>
  </body>
</html>
```

### 建立`sw.js`

```javascript=
self.addEventListener("install", (event) => {
  console.log("Installed");
});

self.addEventListener("activate", (event) => {
  console.log("Activated");
});

self.addEventListener("fetch", (event) => {
  console.log("Fetch request");
});

self.addEventListener("notificationclick", function (e) {
  var notification = e.notification;
  var primaryKey = notification.data.primaryKey;
  var action = e.action;
  // 如果action是close關閉通知
  if (action === "close") {
    notification.close();
  } else {
    // 否的話開啟github，關閉通知
    clients.openWindow("https://github.com/");
    notification.close();
  }
});
```
### 效果如下

![](https://i.imgur.com/O8T13jJ.gif)


[introduction-to-push-notifications](https://developers.google.com/web/ilt/pwa/introduction-to-push-notifications)