背景
在当前window窗口,对于一些浮窗组件,一般需要点击当前window下的其他位置才能够隐藏浮窗。但如果当前窗口中存在iframe区域,那么由于一些特殊的性质,无法通过常规的click点击事件监听iframe元素的点击,而通过contentDocument的形式也很难在跨域存在的情况下实现。
解决方案
window对象本身可以监听到blur方法,在点击到iframe时,会触发父级窗口的blur事件,此时可以间接监听到相当于点击到了iframe的事件。
缺陷
当然,缺陷很明显,其他方式触发了窗口的blur时,也会触发该事件,而不是点击到了iframe。但对于以上场景,是完全够用的。
代码
import { ref, onUnmounted, Ref } from 'vue';export function useIframeClick(iframeRef: Ref<HTMLIFrameElement | undefined>,handleIframeClick: () => void
) {// 监听iframe点击的状态const iframeClickDetection = ref({isSetup: false,blurTimer: null as NodeJS.Timeout | null,focusTimer: null as NodeJS.Timeout | null,});// 覆盖层相关状态const showClickOverlay = ref(false);const overlayTimer = ref<NodeJS.Timeout | null>(null);// 处理覆盖层点击const handleOverlayClick = () => {console.log('覆盖层检测到点击');handleIframeClick();// 点击后短暂隐藏覆盖层,让用户能正常与iframe交互showClickOverlay.value = false;setTimeout(() => {showClickOverlay.value = true;}, 100);};// 鼠标进入覆盖层时暂时隐藏,允许正常交互const handleOverlayMouseEnter = () => {if (overlayTimer.value) {clearTimeout(overlayTimer.value);}overlayTimer.value = setTimeout(() => {showClickOverlay.value = false;}, 500); // 500ms后隐藏};// 鼠标离开后重新显示覆盖层const handleOverlayMouseLeave = () => {if (overlayTimer.value) {clearTimeout(overlayTimer.value);}overlayTimer.value = setTimeout(() => {showClickOverlay.value = true;}, 1000); // 1s后重新显示};// 设置iframe点击检测const setupIframeClickDetection = () => {if (iframeClickDetection.value.isSetup) return;const detection = iframeClickDetection.value;// 监听window的焦点事件const handleWindowBlur = () => {// 延迟检测,确保是iframe获得了焦点detection.blurTimer = setTimeout(() => {// 检查当前活动元素是否是iframeif (document.activeElement === iframeRef.value) {handleIframeClick();}}, 0);};const handleWindowFocus = () => {// 清除blur定时器if (detection.blurTimer) {clearTimeout(detection.blurTimer);detection.blurTimer = null;}};// 监听鼠标移动事件,确保是真实的点击而不是键盘导航let mouseMovedOverIframe = false;const handleMouseMove = (e: MouseEvent) => {const iframeRect = iframeRef.value?.getBoundingClientRect();if (iframeRect) {const isOverIframe =e.clientX >= iframeRect.left &&e.clientX <= iframeRect.right &&e.clientY >= iframeRect.top &&e.clientY <= iframeRect.bottom;mouseMovedOverIframe = isOverIframe;}};// 改进的blur处理const handleWindowBlurImproved = () => {detection.blurTimer = setTimeout(() => {if (document.activeElement === iframeRef.value && mouseMovedOverIframe) {handleIframeClick();}}, 0);};window.addEventListener('blur', handleWindowBlurImproved);window.addEventListener('focus', handleWindowFocus);document.addEventListener('mousemove', handleMouseMove);detection.isSetup = true;// 清理函数onUnmounted(() => {window.removeEventListener('blur', handleWindowBlurImproved);window.removeEventListener('focus', handleWindowFocus);document.removeEventListener('mousemove', handleMouseMove);if (detection.blurTimer) {clearTimeout(detection.blurTimer);}});};return {showClickOverlay,setupIframeClickDetection,handleOverlayClick,handleOverlayMouseEnter,handleOverlayMouseLeave,};
}