Notification.requestPermission 在 Mac 版本的 Safari 中引发错误

Posted

技术标签:

【中文标题】Notification.requestPermission 在 Mac 版本的 Safari 中引发错误【英文标题】:Notification.requestPermission throws an error in Mac versions of Safari 【发布时间】:2016-11-02 00:39:24 【问题描述】:

我正在尝试在页面加载时使用 jQuery 在本地显示通知。通知在 Firefox、Firefox Developer 和 Chrome 中正确显示。尽管通知首选项设置允许,但通知未显示在 Safari 中。

MDN 站点 https://developer.mozilla.org/en/docs/Web/API/notification 上的类似代码正在运行。

片段如下。

// Display a sample notification
  if (window.Notification) 
    return $(".au-notifications-page").show(function() 
      var notification;
      notification = new Notification(
        'Success Text', 
        //tag: $("[name=tag]").val(),
        body: 'Success Message',
        iconUrl: 'img/avatar-male.png',
        icon: 'img/avatar-male.png'
      );
      return notification.onclick = function() 
        notification.close();
        window.open().close();
        return window.focus();
      ;
    );
  ;

完整代码如下。

$(document).ready(function () 

  // Request permission on site load
  Notification.requestPermission().then(function(result) 
    if (result === 'denied') 
      //alert('denied');
      $(".au-notif-disabled-header").removeClass('hide');
      $(".au-notif-disabled-header .btn").addClass('hide');
      return;
    
    if (result === 'default') 
      //alert('ignored');
      $(".au-notif-disabled-header").removeClass('hide');
      return;
    
    //alert('granted');
    $(".au-notif-disabled-header").addClass('hide');
  );

  // Request permission with button
  $('.au-notif-disabled-header .btn').click(function () 
    Notification.requestPermission().then(function(result) 
      if (result === 'denied') 
        $(".au-notif-disabled-header").removeClass('hide');
        $(".au-notif-disabled-header .btn").addClass('hide');
        return;
      
      if (result === 'default') 
        $(".au-notif-disabled-header").removeClass('hide');
        return;
      
      $(".au-notif-disabled-header").addClass('hide');
    );
  );

  $( ".au-notification-icon" ).hover(
    function() 
      $(".au-notifications-menu .au-notif-msg-realtime").slideDown();
      $('.au-notification-icon .badge').html("2");
    , function() 
      $(".au-notifications-menu .au-notif-msg-realtime").slideUp();
      $('.au-notification-icon .badge').html("1");
    
  );

  //To show notification received while on notifications page
  $(".au-notif-msg-realtime").hide();
  //$(".au-notifications-page .au-notif-msg-realtime").slideDown();

  $(".au-notifications-page .au-notif-msg-realtime").slideDown(
    complete: function()
      $('.au-notification-icon .badge').html("2");
      $('head title').html("(2) Notifications");
    
  );


  // Display a sample notification
  if (window.Notification) 
    return $(".au-notifications-page").show(function() 
      var notification;
      notification = new Notification(
        'Success Heading', 
          body: 'Success Text',
          iconUrl: 'img/avatar-male.png',
          icon: 'img/avatar-male.png'
      );
      return notification.onclick = function() 
        notification.close();
        window.open().close();
        return window.focus();
      ;
    );
  ;
);

编辑 1:Safari 抛出此异常

undefined 不是对象(评估 'Notification.requestPermission().then')

【问题讨论】:

【参考方案1】:

您必须为 Safari 使用回调函数,因为它不会返回 Promise。

根据MDN:

这使用了该方法的 promise-version,正如最近支持的那样 实现(例如 Firefox 47。)如果你想支持 旧版本,您可能必须使用旧回调版本, 看起来像这样:

这是他们提供的示例代码:

Notification.requestPermission(function (permission) 
    // If the user accepts, let's create a notification
    if (permission === "granted") 
        var notification = new Notification("Hi there!");
    
);

为了支持 Safari 通知,我最终得到了这样的结果:

    try 
        Notification.requestPermission()
            .then(() => doSomething())                                                                                                                                               
     catch (error) 
        // Safari doesn't return a promise for requestPermissions and it                                                                                                                                       
        // throws a TypeError. It takes a callback as the first argument                                                                                                                                       
        // instead.
        if (error instanceof TypeError) 
            Notification.requestPermission(() =>                                                                                                                                                              
                doSomething();
            );
         else 
            throw error;                                                                                                                                                                                       
                                                                                                                                                                                                              
          

【讨论】:

由于某些浏览器(即 ios 11.4 上的 Safari)不支持 Notification,因此在执行 try/catch 块之前检查 typeof Notification !== 'undefined' 可能是一个好习惯。【参考方案2】:

更好的解决方案是将结果包装在Promise 中,然后然后(没有双关语)运行您的代码。此代码适用于所有浏览器(包括 Safari),并且没有复杂的 if 块(概念​​在 this question 中有详细讨论)

Promise.resolve(Notification.requestPermission()).then(function(permission) 
    // Do something
);

这是因为Promise.resolvePromise 没有任何作用,但会将Safari 的requestPermission() 转换为Promise

请注意,iOS Safari 仍然不支持 Notification API,因此您需要 check if it is available first

【讨论】:

在 safari 上,权限在 Safari 上是未定义的,对吧? permission 只是我选择的参数名称。 promise 会将通知权限对象传递给回调函数 这不起作用,因为Notification.requestPermission()在Safari上立即返回undefinedthen函数会立即被调用,无需等待权限请求对话框被解析,并且不管用户选择什么。【参考方案3】:

要返回一个在用户授予或拒绝显示通知的权限之前不会解析的承诺:

        if (!permissionPromise && Notification.permission === 'granted' ) 
            permissionPromise = Promise.resolve(Notification.permission);
        
        if (!permissionPromise) 
            permissionPromise = new Promise(function (resolve, reject) 
                // Safari uses callback, everything else uses a promise
                var maybePromise = $window.Notification.requestPermission(resolve, reject);
                if (maybePromise && maybePromise.then) 
                    resolve(maybePromise);
                
            );
        
        return permissionPromise;

【讨论】:

以上是关于Notification.requestPermission 在 Mac 版本的 Safari 中引发错误的主要内容,如果未能解决你的问题,请参考以下文章