Ruby's Louvre

每天学习一点点算法

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

IE下script标签的readyState属性

在做加载器时遇到一个常见问题,如何判定一个脚本已经执行完毕。

  • “uninitialized” – 原始状态
  • “loading” – 下载数据中
  • “loaded” – 下载完成
  • “interactive” – 还未执行完毕
  • “complete” – 脚本执行完毕.

网上流行的答案是这个,我怎么觉得其实这是抄自XMLHttpRequest的readyState呢?!恰逢这两个都有这属性。

我们亲自做一个实验:

<!DOCTYPE html>
<html>
    <head>
        <title>node.readyState</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width">
        <script>
            var node = document.createElement("script")
            node.onreadystatechange = function() {
                var state = node.readyState
                setTimeout(function() {
                    var div = document.createElement("div")
                    document.body.appendChild(div)
                    div.innerHTML = state
                }, 300)
 
            }
            var head = document.getElementsByTagName("head")[0]
            head.appendChild(node)
            node.src = "avalon.js"
 
        </script>
    </head>
    <body>
        <div>node.readyState</div>
    </body>
</html>
完整的控件教程
IE11 空白,说明完全与标准一致了
IE10 loading loaded
IE9 loading loaded
IE8 complete loaded
IE7 complete loaded 但有一定机率,只出现complete或loaded
IE6 complete loaded 但有一定机率,只出现complete或loaded

换言之,IE67是个非常悲催的问题。另外,opera9-10也支持readyState,根据老外的描述,它竟然两次都是loaded!

因此我们需要根据浏览器的情况采用不同的策略。

首先是使用何种回调,如果是支持onload事件,那么就直接用onload 就没有这么多麻烦事。最简单的策略是这样判定:

var node = DOC.createElement("script")
var supportLoad = "onload" in node
var onEvent = supportLoad ? "onload" : "onreadystatechange"
node[onEvent] = callback

判定完成时机, 我们不使用网上的/complete|loaded|undefined/.test(node.readyState),这会同时掉进opera与IE67的坑中。对于使用onload事件进行监听的,不再判定node.readyState,IE(其实也就是IE6-8),需要使用一个定时器。当第一次进行onreadystatechange回调时,timeID为空, 并且readyState为complete或loaded时,我们设置它在300ms后再执行自身。然后如果浏览器还执行此回调时, 它就进入第二个分支,清掉定时器,执行用户代码。万一,浏览器只执行一次onreadystatechange回调,那也没关系,让定时器100~300ms后执行用户代码

最后贴出全部代码:

//通过script节点加载目标模块
var node = DOC.createElement("script")
var timeID
var supportLoad = "onload" in node
var onEvent = supportLoad ? "onload" : "onreadystatechange"
node[onEvent] = function onLoad() {
    if (!supportLoad && !timeID && /complete|loaded/.test(node.readyState)) {
        timeID = setTimeout(onLoad)
        return
    }
    if (supportLoad || timeID) {
        clearTimeout(timeID)
        //你的代码
    }
}
 
head.insertBefore(node, head.firstChild) //chrome下第二个参数不能为null
node.src = url //插入到head的第一个节点前,防止IE6下head标签没闭合前使用appendChild抛错

大家也可以到这里看一下它的实际应用,如果大家都是使用AMD规范定义JS文件,那么我在旧式IE下连onerror也模拟出来了。

如果您觉得此文有帮助,可以打赏点钱给我支付宝1669866773@qq.com ,或扫描二维码

posted on   司徒正美  阅读(6121)  评论(4编辑  收藏  举报

编辑推荐:
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2012-02-06 javascript 瀑布流
2010-02-06 とある要素以下にある textNode で一致する textNode を XPath で高速に取り出す
2010-02-06 一些JSON相关的函数
点击右上角即可分享
微信分享提示