window.onload的替代方案——DOM状态检测

本文详细介绍了如何在不同浏览器环境下确保DOM加载完成后执行特定JavaScript函数的方法。包括window.onload、DOMContentLoaded及针对IE、Firefox、Opera和Safari等浏览器的兼容性解决方案。

window.onload是非常常用的初始化事件,而该事件所对应的响应函数只有在浏览器将目标页面中所有资源内容全部装载完毕之后才会执行,这里的“所有资源内容”特指图片、音效、iframe所包含的页面等。

下面的例子是使用window.onload方式执行JavaScript脚本:

window.onload=function(){  walkmydog()}

Firefox & Opera下的替换方案:

if (document.addEventListener)
  document.addEventListener("DOMContentLoaded", walkmydog, false)

IE下的替换方案,:

if (document.all && !window.opera){ //Crude test for IE
//Define a "blank" external JavaScript tag
  document.write('<script type="text/javascript" id="contentloadtag" defer="defer" src="javascript:void(0)"><\/script>')
  var contentloadtag=document.getElementById("contentloadtag")
  contentloadtag.onreadystatechange=function(){
    if (this.readyState=="complete")
      walkmydog()
  }
}

Firefox & Opera & IE综合方案:

var alreadyrunflag=0 //flag to indicate whether target function has already been run

if (document.addEventListener)
  document.addEventListener("DOMContentLoaded", function(){alreadyrunflag=1; walkmydog()}, false)
else if (document.all && !window.opera){
  document.write('<script type="text/javascript" id="contentloadtag" defer="defer" src="javascript:void(0)"><\/script>')
  var contentloadtag=document.getElementById("contentloadtag")
  contentloadtag.onreadystatechange=function(){
    if (this.readyState=="complete"){
      alreadyrunflag=1;
      walkmydog()
    }
  }
}

window.onload=function(){
  setTimeout("if (!alreadyrunflag) walkmydog()", 0);
}

之所以要用window.onload是为了应对那些既不支持DOMContentLoaded也不支持onreadystatechange的浏览器。setTimeout函数的作用是为了保证变量alreadyrunflag在window.onload事件被激活之前已经赋值。因为有时在IE浏览器中,由于要装载资源几乎没有,导致window.onload和onreadystatechange事件同时激活,这样的话,在onload事件做判断时变量alreadyrunflag可能还没有被赋值,这就无法保证程序执行的正确性。所以用setTimeout函数来让onload事件中的判断条件稍微滞后一些,以保证alreadyrunflag可用。

下面是原文中用在Safari中的方案:

if(/Safari/i.test(navigator.userAgent)){ //Test for Safari
  var _timer=setInterval(function(){
  if(/loaded|complete/.test(document.readyState)){
    clearInterval(_timer)
    walkmydog() // call target function
  }}, 10)
}
这种方案是就是单纯的检测document的状态。至于为什么不使用DOMContentLoaded,可能是和文章所写时的Safari版本有关吧。从 http://en.wikipedia.org/wiki/DOM_events可以看出,Safari3.1+就开始支持DOMContentLoaded了,可喜的是IE9也支持了( http://p2b.jp/1269996654,同时支持的还有addEventListener)。不过这种方案也许还会有用,最差也可以帮我们开阔下眼界。
以上内容大部分来自: http://www.javascriptkit.com/dhtmltutors/domready.shtml。其中添加了点自己理解。

<template> <view class="container"> <!-- 全屏透明点击层 --> <view class="overlay" @click="handleOverlayClick"> <text class="instruction">轻触屏幕以选择文件</text> <text class="instruction">maxCount~~~{{maxCount}}</text> <text class="instruction">maxSize~~~{{maxSize}}</text> <text class="instruction">acceptTypes~~~{{acceptTypes}}</text> <text class="instruction">isSingle~~~{{isSingle}}</text> <text class="instruction">acceptParam~~~{{acceptParam}}</text> </view> <!-- 隐藏的 file input --> <input ref="fileInputRef" type="file" :accept="acceptTypes" :multiple="!isSingle" @change="handleFiles" style="display: none;" /> </view> </template> <script setup> import { ref, onMounted, onBeforeUnmount } from 'vue' onMounted(() => { console.log('✅ 页面已挂载') // 监听页面隐藏事件(关键:用户点击返回、切换 Tab 都会触发) const visibilityHandler = () => { if (document.visibilityState === 'hidden') { console.log('👀 页面即将隐藏 - 尝试发送待上传文件...') sendFilesToMiniProgram(pendingFiles.value) } } const pageHideHandler = () => { console.log('📱 pagehide 触发 - 最后机会发送数据') sendFilesToMiniProgram(pendingFiles.value) } document.addEventListener('visibilitychange', visibilityHandler) window.addEventListener('pagehide', pageHideHandler) // 👇 清理监听器(Vue 卸载时自动移除) onBeforeUnmount(() => { console.log('🧹 页面即将销毁,清理事件监听器') document.removeEventListener('visibilitychange', visibilityHandler) window.removeEventListener('pagehide', pageHideHandler) // 🔥 在最后时刻再次尝试发送(防止前面没发成功) sendFilesToMiniProgram(pendingFiles.value) }) }) // 获取 URL 参数 const getUrlParameter = (name) => { const regex = new RegExp(`[?&]${name}=([^&#]*)`) const results = regex.exec(window.location.search) return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' ')) } const maxCount = ref(parseInt(getUrlParameter('maxCount')) || 10) const maxSize = ref(parseInt(getUrlParameter('maxSize')) * 1024 * 1024 || Infinity) const acceptParam = getUrlParameter('accept') || '' const acceptTypes = ref( acceptParam || '.pdf,.doc,.docx,.xls,.xlsx,.txt,.ppt,.pptx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,text/plain' ) const isSingle = ref(getUrlParameter('isSingle') === 'true') const files = ref([]) const fileInputRef = ref(null) // 工具函数 const formatSize = (bytes) => { try { if (!bytes) return '0 B' const k = 1024 const sizes = ['B', 'KB', 'MB', 'GB'] const i = Math.floor(Math.log(bytes) / Math.log(k)) return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i] } catch (error) { console.log("🚀 ~ formatSize ~ error:", error) } } // 处理遮罩点击(关键:用户手势) const handleOverlayClick = () => { try { console.log('用户点击了遮罩层,准备打开文件选择器...') // 创建 input 并插入 DOM const input = document.createElement('input') input.type = 'file' input.accept = acceptTypes.value input.multiple = !isSingle.value input.style.display = 'none' document.body.appendChild(input) // 绑定 change 事件 input.onchange = async (event) => { await handleFiles(event) document.body.removeChild(input) // 清理 } // 触发点击(现在允许!因为发生在用户 click 回调中) input.click() } catch (error) { console.log("🚀 ~ handleOverlayClick ~ error:", error) } } // 响应式变量:存储已读取但尚未确认的文件(用于页面销毁前发送) const pendingFiles = ref([]) // 处理文件选择 const handleFiles = async (event) => { try { const selectedFiles = event.target.files if (!selectedFiles || selectedFiles.length === 0) { sendFilesToMiniProgram([]) return } const remainingSlots = maxCount.value - files.value.length const filesToProcess = Array.from(selectedFiles).slice(0, remainingSlots) const filePromises = filesToProcess.map(file => { return new Promise((resolve) => { if (file.size > maxSize.value) { console.warn(`文件 "${file.name}" 超出大小限制`) resolve(null) return } const reader = new FileReader() reader.onload = () => { const fileData = { name: file.name, size: file.size, type: file.type, base64: reader.result } // 👉 立即加入待发送队列 pendingFiles.value.push(fileData) resolve(fileData) } reader.onerror = () => { console.error(`读取失败: ${file.name}`) resolve(null) } reader.readAsDataURL(file) }) }) const results = await Promise.all(filePromises) const validFiles = results.filter(Boolean) if (validFiles.length > 0) { // 🔥 立即发送给小程序(可选:也可等用户确认) sendFilesToMiniProgram(pendingFiles.value) } else { sendFilesToMiniProgram([]) } } catch (error) { console.error("🚀 ~ handleFiles ~ error:", error) } } const sendFilesToMiniProgram = (fileList) => { try { console.log('[H5 → 小程序] 发送文件:', fileList) window.parent.postMessage?.({ type: 'selectedFiles', data: fileList }, '*') window.postMessage?.({ type: 'selectedFiles', data: fileList }, '*') } catch (error) { console.log("🚀 ~ sendFilesToMiniProgram ~ error:", error) } } </script> <style scoped> .container { position: relative; width: 100vw; height: 100vh; margin: 0; padding: 0; overflow: hidden; } .overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(255, 255, 255, 0); /* 完全透明 */ display: flex; justify-content: center; align-items: center; z-index: 9999; cursor: pointer; flex-direction: column; /* 垂直排列子元素 */ } .instruction { margin-bottom: 20px; padding: 10px; font-size: 16px; color: #999; text-align: left; /* 水平居中 */ pointer-events: none; /* 点击穿透,不影响底层事件 */ user-select: none; /* 禁止选中文本 */ /* 新增:确保文本可换行 */ width: 100%; /* 最大宽度占满 */ box-sizing: border-box; /* 包含 padding 计算 */ word-wrap: break-word; /* 长单词/URL 自动断行 */ word-break: break-word; /* 兼容性更好(尤其中文) */ white-space: normal; /* 允许换行(默认行为) */ max-width: 100vw; /* 防止超出视口 */ line-height: 1.5; /* 提高可读性 */ } </style> 修改代码让sendFilesToMiniProgram只在页面销毁的时候调用,传入的是已经选中的文件
最新发布
09-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值