MediaDevices: devicechange 事件

可用性有限

此特性不是基线特性,因为它在一些最广泛使用的浏览器中不起作用。

安全上下文: 此功能仅在安全上下文(HTTPS)中可用,且支持此功能的浏览器数量有限。

当系统连接或移除诸如摄像头、麦克风或扬声器之类的媒体设备时,会向 MediaDevices 实例发送 devicechange 事件。

此事件不可取消,也不会冒泡。

语法

在诸如 addEventListener() 之类的方法中使用事件名称,或设置事件处理程序属性。

js
addEventListener("devicechange", (event) => { })

ondevicechange = (event) => { }

事件类型

一个通用的 Event

示例

在此示例中,我们创建了一个名为 updateDeviceList() 的函数。该函数在 MediaDevices.getUserMedia() 成功获取到流时调用一次,之后每当设备列表发生变化时都会调用。它会在浏览器窗口中显示两个列表:一个音频设备列表,一个视频设备列表,其中包含设备标签(名称)以及它是输入设备还是输出设备。由于示例为 devicechange 事件提供了处理程序,因此每次向运行示例的设备添加或移除媒体设备时,列表都会刷新。

我们设置了全局变量,这些变量包含用于列出音频和视频设备的 <ul> 元素的引用。

js
const audioList = document.getElementById("audioList");
const videoList = document.getElementById("videoList");

获取并绘制设备列表

现在让我们看看 updateDeviceList() 本身。每当我们要获取当前媒体设备列表并使用该信息更新显示的音频和视频设备列表时,都会调用此方法。

js
function updateDeviceList() {
  navigator.mediaDevices.enumerateDevices().then((devices) => {
    audioList.textContent = "";
    videoList.textContent = "";

    devices.forEach((device) => {
      const elem = document.createElement("li");
      const [kind, type, direction] = device.kind.match(/(\w+)(input|output)/i);

      elem.innerHTML = `<strong>${device.label}</strong> (${direction})`;
      if (type === "audio") {
        audioList.appendChild(elem);
      } else if (type === "video") {
        videoList.appendChild(elem);
      }
    });
  });
}

updateDeviceList() 完全由对 navigator.mediaDevices 属性中引用的 MediaDevices 对象调用 enumerateDevices() 函数以及在 promise 被满足时运行的代码组成。当设备列表准备就绪时,会调用满足处理程序。列表以 MediaDeviceInfo 对象数组的形式传递到满足处理程序中,每个对象描述一个媒体输入或输出设备。

使用 forEach() 循环扫描所有设备。对于每个设备,我们创建一个新的 <li> 对象,用于将其显示给用户。

let [kind, type, direction] = device.kind.match(/(\w+)(input|output)/i); 这行代码值得特别注意。它使用 解构赋值String.match() 返回的数组中的前三个元素的值赋给变量 kindtypedirection。我们这样做是因为 MediaDeviceInfo.kind 的值是一个单一字符串,其中同时包含媒体类型和媒体流动的方向,例如“audioinput”或“videooutput”。因此,这一行会提取出类型(“audio”或“video”)和方向(“input”或“output”),以便它们可用于构建列表中显示的字符串。

一旦字符串被组装起来,其中包含设备名称(粗体显示)和方向(括号内显示),它就会通过根据设备类型调用 audioListvideoList 上的 appendChild() 来附加到相应的列表中。

处理设备列表更改

我们在两个地方调用 updateDeviceList()。第一个是在 getUserMedia() promise 的满足处理程序中,以便在流打开时最初填充列表。第二个是在此 devicechange 事件的事件处理程序中。

js
navigator.mediaDevices.ondevicechange = (event) => {
  updateDeviceList();
};

有了这段代码,每次用户插入摄像头、麦克风或其他媒体设备,或者打开或关闭它们时,我们都会调用 updateDeviceList() 来重绘已连接设备的列表。

结果

规范

规范
媒体捕获和流
# event-mediadevices-devicechange
媒体捕获和流
# dom-mediadevices-ondevicechange

浏览器兼容性