背景
在當前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,};
}