iframe内滚动至元素无法触发hover/mouseenter事件的问题咨询
iframe内滚动至元素无法触发hover/mouseenter事件的问题咨询
最近有个朋友遇到了这么个奇怪的问题,我整理了下细节,跟大家聊聊怎么解决:
他做了一个嵌套iframe的页面,iframe里嵌入的内容和父页面完全一致,代码如下:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>悬浮效果示例</title> <style> #A { border: 1px solid #000; padding: 10px; } #B { height: 600px; } #B, #C { margin-bottom: 10px; padding: 10px; border: 1px solid #ccc; } .child { margin-bottom: 5px; padding: 10px; border: 1px solid #eee; transition: background-color 0.3s ease; } #B1:hover, #B2:hover, #B3:hover { background-color: #007BFF; color: #fff; } </style> </head> <body> <div id="A"> <div id="B"> <div class="child" id="B1">B1</div> <div class="child" id="B2">B2</div> <div class="child" id="B3">B3</div> </div> <div id="C">C</div> </div> <iframe src="http://localhost:54365" width="100%" height="300px" scrolling="yes"></iframe> </body> </html>
问题是:当滚动iframe里的内容,让里面的B1/B2/B3滚到鼠标正下方时,hover效果完全没反应;但直接把鼠标移到这些元素上,hover又能正常触发。
为啥会出现这个问题?
其实这是浏览器事件触发的机制导致的:hover本质依赖的是mouseenter/mouseleave这类鼠标事件,而这类事件的触发前提是鼠标的位置发生了变化,并且浏览器检测到鼠标和元素的重叠关系改变。
当你滚动iframe时,鼠标的物理位置根本没动,只是iframe内部的元素位置变了,这时候浏览器不会主动去重新检测“鼠标现在压在哪个元素上”,自然就不会触发hover相关的事件。
怎么解决?
既然浏览器不会主动检测,那我们就手动来做这件事,思路很简单:
- 监听iframe的滚动事件
- 滚动时,找到鼠标在iframe内对应的元素
- 手动给这个元素加上hover样式(或者触发对应的事件)
具体实现步骤如下:
第一步:给iframe加个id方便操作
修改iframe标签:
<iframe id="targetIframe" src="http://localhost:54365" width="100%" height="300px" scrolling="yes"></iframe>
第二步:添加JavaScript逻辑
在父页面(因为是同域,可直接操作iframe内部元素)加入这段代码:
const iframe = document.getElementById('targetIframe'); let currentHoverEl = null; let mousePos = { x: 0, y: 0 }; // 先记录鼠标在iframe内的坐标 iframe.contentWindow.addEventListener('mousemove', (e) => { mousePos.x = e.clientX; mousePos.y = e.clientY; }, { passive: true }); // 监听iframe滚动事件 iframe.addEventListener('scroll', () => { const iframeDoc = iframe.contentDocument || iframe.contentWindow.document; // 找到鼠标位置下的元素 const el = iframeDoc.elementFromPoint(mousePos.x, mousePos.y); // 判断是不是我们要的child元素 if (el?.classList.contains('child')) { // 如果和之前的元素不一样,先移除之前的样式 if (currentHoverEl && currentHoverEl !== el) { currentHoverEl.classList.remove('manual-hover'); } // 给当前元素加样式 el.classList.add('manual-hover'); currentHoverEl = el; } else { // 鼠标下不是目标元素,清空之前的样式 if (currentHoverEl) { currentHoverEl.classList.remove('manual-hover'); currentHoverEl = null; } } });
第三步:修改CSS样式
把原来的hover样式和我们手动加的类样式合并:
.child.manual-hover, #B1:hover, #B2:hover, #B3:hover { background-color: #007BFF; color: #fff; }
这样一来,当iframe滚动时,我们就会主动检测鼠标下方的元素,手动给它加上hover的样式,完美解决问题~
另外要提一句,因为这里iframe是同域的(都是localhost),所以可以直接访问内部的文档对象,不会有跨域限制。如果是跨域场景的话,这个方法就不适用了,不过你的情况刚好没问题。
备注:内容来源于stack exchange,提问作者贺琪恒
相关产品推荐
相关产品推荐

