解决移动设备上通过AJAX播放音频的NotAllowedError

解决移动设备上通过AJAX播放音频的NotAllowedError

本文旨在解决移动设备上通过ajax动态加载音频时遇到的`NotAllowedError`,特别是当`onerror`事件未能触发的问题。核心在于理解移动浏览器对用户手势的严格要求,并指出传统的`click`事件在触摸设备上可能无法满足这些要求,推荐使用更符合触摸交互的`touchend`事件来确保音频播放的成功。

理解移动设备上的音频播放限制与NotAllowedError

在开发Web应用时,尤其是在移动设备上,尝试通过javaScript动态播放音频常常会遇到NotAllowedError。这种错误通常表现为控制台输出Uncaught (in promise) NotAllowedError,并且令人困惑的是,标准的audio.onerror事件可能并不会被触发。

这个问题的根源在于现代浏览器,特别是移动浏览器,为了改善用户体验和防止滥用,对媒体(包括音频和视频)的自动播放实施了严格的策略。这些策略通常要求媒体播放必须由明确的用户手势(User Gesture)触发。当代码尝试在没有足够用户手势的情况下播放音频时,浏览器会阻止播放并抛出NotAllowedError。

原始场景中,用户点击按钮触发AJAX请求,获取音频路径后赋值给<audio>标签并尝试播放。尽管用户执行了“点击”操作,但在某些移动设备或ipad上,浏览器可能认为这个“点击”事件与后续的audio.play()操作之间存在脱节,或者其事件类型不满足播放策略的要求。

解决策略:利用触摸事件确保用户手势有效性

针对移动设备上的这种问题,关键在于确保触发audio.play()的代码确实是在一个被浏览器认为是“有效用户手势”的上下文中执行的。对于触摸屏设备,传统的click事件在某些情况下可能表现不一致或不被视为足够强烈的用户手势。此时,使用更原生的触摸事件,如touchend,可以更可靠地满足浏览器的要求。

touchend事件在用户手指从屏幕上抬起时触发,它更直接地反映了触摸交互,并且在移动设备上通常被视为一个可靠的用户手势。

解决移动设备上通过AJAX播放音频的NotAllowedError

千面视频动捕

千面视频动捕是一个ai视频动捕解决方案,专注于将视频中的人体关节二维信息转化为三维模型动作。

解决移动设备上通过AJAX播放音频的NotAllowedError 27

查看详情 解决移动设备上通过AJAX播放音频的NotAllowedError

示例代码优化

让我们基于原始问题,优化代码以处理移动设备上的NotAllowedError。我们将重点放在错误重试机制上,确保重试按钮的点击(或触摸)能够成功触发音频播放。

html 结构保持不变:

<audio id="audio"></audio> <button id="play-audio">Play Audio</button> <button id="retry" style="display:none">Retry To Play Audio</button>

javascript 优化:

document.addEventListener('domContentLoaded', function() {     const audioPlayer = document.getElementById('audio');     const playButton = document.getElementById('play-audio');     const retryButton = document.getElementById('retry');      // 辅助函数:尝试播放音频并处理Promise     function tryPlayAudio() {         // audioPlayer.load(); // 可以在播放前调用load(),但通常不是解决NotAllowedError的关键         const playPromise = audioPlayer.play();          if (playPromise !== undefined) {             playPromise.then(() => {                 console.log('Audio is playing successfully.');                 retryButton.style.display = "none"; // 播放成功则隐藏重试按钮             }).catch(error => {                 // NotAllowedError 会在这里被捕获                 console.error('Audio play failed:', error.name, error.message);                 if (error.name === 'NotAllowedError' || error.name === 'AbortError') {                     // 即使onerror不触发,Promise的catch也能捕获到                     console.log('Displaying retry button due to play error.');                     retryButton.style.display = "block";                 }             });         }     }      // 初始播放按钮的点击事件     playButton.addEventListener('click', function() {         $.ajax({             url: "../audio/audio.json", // 假设这是一个返回 { "sound": "path/to/audio.mp3" } 的json             type: 'GET',             dataType: "json",             success: function(response) {                 audioPlayer.src = response.sound;                 // 尝试播放音频                 tryPlayAudio();             },             error: function(xhr, status, error) {                 console.error('Failed to fetch audio path:', status, error);                 // 可以在这里显示错误信息或重试按钮                 retryButton.style.display = "block";             }         });     });      // 关键优化:为重试按钮添加touchend事件监听     // 对于移动设备,touchend通常比click更可靠地触发用户手势策略     retryButton.addEventListener('touchend', function(event) {         event.preventDefault(); // 阻止默认的触摸行为,如滚动         console.log('Retry button touched, attempting to play audio...');         tryPlayAudio(); // 再次尝试播放     });      // 也可以保留click事件作为桌面设备的备用,但touchend优先级更高     retryButton.addEventListener('click', function() {         // 如果touchend已经处理,这里可能不会执行或不重要         console.log('Retry button clicked (fallback), attempting to play audio...');         tryPlayAudio();     });      // 监听audio元素的错误事件(作为补充,即使它可能不总是触发NotAllowedError)     audioPlayer.onerror = function(e) {         console.error('Audio element onerror event fired:', e);         // 如果这里捕获到错误,也显示重试按钮         retryButton.style.display = "block";     };      audioPlayer.onplaying = function() {         console.log('Audio is actually playing.');     }; });

代码解释与注意事项:

  1. audio.play() 返回 Promise: 现代浏览器中,audio.play() 方法会返回一个 Promise。这是处理播放错误(包括 NotAllowedError)的推荐方式。即使 onerror 事件不触发,Promise.catch() 也能捕获到播放失败的原因。
  2. touchend 事件: 对于重试按钮,我们专门添加了 touchend 事件监听器。在移动设备上,当用户通过触摸与页面交互时,touchend 事件是浏览器最认可的用户手势之一。它能更有效地绕过自动播放限制。
  3. event.preventDefault(): 在触摸事件处理器中调用 event.preventDefault() 是一个好习惯,可以避免某些默认的触摸行为(如滚动或缩放)干扰按钮点击。
  4. DOMContentLoaded: 将所有JavaScript代码包裹在 DOMContentLoaded 事件监听器中,确保在DOM完全加载后再执行脚本,避免元素未找到的错误。
  5. 桌面兼容性: 可以保留 click 事件作为桌面设备的备用,但 touchend 在移动设备上会更早且更可靠地触发。
  6. 错误处理粒度: NotAllowedError 通常表示浏览器策略限制,而不是文件不存在或损坏。对于后者,onerror 事件可能更容易触发。通过 Promise.catch() 可以统一处理这两种情况。

总结

在移动设备上处理通过AJAX动态加载音频时的NotAllowedError,核心在于尊重并满足浏览器的用户手势策略。虽然click事件在桌面端通常足够,但在触摸设备上,使用更原生的触摸事件(如touchend)来触发音频播放,可以显著提高成功率。同时,利用audio.play()返回的Promise进行错误处理,是现代Web开发中处理媒体播放失败的健壮方法,它能捕获到onerror事件可能遗漏的NotAllowedError。通过结合这些策略,可以构建出在各种设备上都更加可靠的音频播放功能。

上一篇
下一篇
text=ZqhQzanResources