如何通过Chrome扩展移除嵌套iframe中video.js的按键事件监听
解决Chrome扩展中video.js数字热键劫持输入框的问题
我开发了一款Chrome扩展,做了个视频覆盖层,支持通过快捷键激活、输入时间戳跳转视频。但遇到卡点:video.js播放器的数字热键会劫持输入框的数字输入,直接触发视频跳转到对应进度,导致没法正常输入时间戳。
已知信息
- 视频播放器嵌套在id为
player的iframe里 - 劫持输入的
keydown事件监听来自video.js库 - 手动在控制台移除该事件监听后,扩展功能完全正常
已尝试但无效的方法
- 给元素添加带
event.stopPropagation()和event.preventDefault()的keydown监听,结果直接禁用了所有视频相关按键,连覆盖层都打不开 - 尝试用第二次快捷键移除添加的监听,但视频毫无反应,还是没法输入数字,仿佛第一次加的监听根本没生效
- 想在content.js里通过
contentDocument和contentWindow访问iframe内元素,但扩展没权限,涉及CORS问题,兼容性极差 - 尝试把焦点从视频切到覆盖层,不管是快捷键触发还是扩展加载时执行都没用
核心代码片段(原尝试禁用监听的部分)
function handleKeydown(event) { const isCtrlKey = OSName === "Mac/iOS" ? event.metaKey : event.ctrlKey; const isAltKey = OSName === "Mac/iOS" ? event.ctrlKey : event.altKey; if (isCtrlKey && isAltKey) { if ( window.location.href.includes("watch") || window.location.href.includes("open") || window.location.href.includes("redbarradio") ) { // document.activeElement.blur() // console.log(document.activeElement) overlayVisibleBool.value = !overlayVisibleBool.value; const overlayDiv = document.getElementById("rr_overlay"); if (!overlayDiv) { appendOverlay(); appendListeners(); appendTippy(); } if (!spotifyOverlayBackground) { spotifyOverlayBackground = true; } toggleOverlay(); document.getElementById("timeInput").value = ""; let cancelIt = (event) => { event.preventDefault() event.stopPropagation() } let player = document.getElementById("player") console.log(player) console.log("overlayBool: ", overlayVisibleBool.value) // if (overlayVisibleBool.value) { // player.addEventListener("keydown", cancelIt, true) // console.log("Keydown cancelled.") // } // else { // player.removeEventListener("keydown", cancelIt, true) // console.log("Keydown enabled.") // } } } } document.addEventListener("keydown", handleKeydown);
补充定位
已确认问题根源是video.js的hotkeys库:当视频元素被选中时,该库会捕获数字输入,事件监听绑定在视频所在iframe的document元素上。
解决方案:针对性拦截iframe内的数字热键
因为事件绑定在iframe内部且跨域限制无法直接操作,我们可以通过Chrome扩展的content script注入到iframe内部来处理:
1. 修改manifest.json,允许注入到iframe
修改扩展的manifest.json,添加all_frames: true让content script可以注入到目标域名的所有iframe中:
"content_scripts": [ { "matches": ["*://redbarradio.net/*", "其他目标域名"], "all_frames": true, // 关键配置:允许注入到iframe "js": ["iframe-content.js"] } ]
2. 创建iframe-content.js,在iframe内拦截事件
在iframe内部监听消息同步覆盖层状态,仅在覆盖层激活时拦截数字键:
let overlayActive = false; // 接收主页面的覆盖层状态消息 window.addEventListener('message', (event) => { if (event.data?.type === 'TOGGLE_OVERLAY') { overlayActive = event.data.active; } }); // 在捕获阶段优先拦截数字键事件 document.addEventListener('keydown', (event) => { // 仅当覆盖层激活且按下的是数字键时,阻止事件传递 if (overlayActive && /^[0-9]$/.test(event.key)) { event.stopPropagation(); event.preventDefault(); } }, true);
3. 主页面content.js同步状态到iframe
修改handleKeydown函数,在切换覆盖层时给iframe发送状态消息:
toggleOverlay(); document.getElementById("timeInput").value = ""; // 向iframe发送覆盖层状态 const playerIframe = document.getElementById('player'); if (playerIframe) { playerIframe.contentWindow.postMessage({ type: 'TOGGLE_OVERLAY', active: overlayVisibleBool.value }, '*'); // 若知道iframe域名,替换为具体域名更安全 }
4. 强制输入框获取焦点
在toggleOverlay函数中,添加输入框自动聚焦逻辑,进一步降低视频捕获按键的概率:
function toggleOverlay() { const overlay = document.getElementById('rr_overlay'); const timeInput = document.getElementById('timeInput'); if (overlayVisibleBool.value) { overlay.style.display = 'block'; // 延迟确保元素渲染完成后再聚焦 setTimeout(() => timeInput.focus(), 100); } else { overlay.style.display = 'none'; } }
内容的提问来源于stack exchange,提问作者Chris
相关产品推荐
相关产品推荐

