你的浏览器不支持canvas

Enjoy life!

javasript -跨域 - 动态创建script标签

Date: Author: JM

本文章采用 知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议 进行许可。

一、动态创建script标签实现跨域

  • 原理:script标签不受同源策略的限制
    • 注意: 如果不讲动态创建的script元素添加到页面,浏览器是不会开始下载资源的。
function loadScript(url, func) { 
  var script = document.createElement('script');
  script.src = url;

  //script加载完毕才执行
  script.onload = script.onreadystatechange = function(){
    if(!this.readyState || this.readyState=='loaded' || this.readyState=='complete'){
      func && func();
      script.onload = script.onreadystatechange = null;
    }
  };

  document.body.appendChild(script);
}

二、 readystatechange事件

2.1 简介

  • readystatechange事件:提供与文档或元素的加载状态有关的信息。
  • 支持 readystatechange 事件的每个对象都有一个 readyState 属性,可能包含下列 5 个值中的一个。即:并非所有对象都会经历 readyState 的这几个阶段。【这意味着 readystatechange 事件经常会少于 4 次,而 readyState 属性的值也不总是连续的。】
    • uninitialized(未初始化):对象存在但尚未初始化。
    • loading(正在加载):对象正在加载数据。
    • loaded(加载完毕):对象加载数据完成。
    • interactive(交互):可以操作对象了,但还没有完全加载。
    • complete(完成):对象已经加载完毕。
  • 支持 readystatechange 事件的浏览器有 IE、Firfox 4+和 Opera。

2.2 document

  • 对于 document 而言,值为”interactive“的 readyState 会在与 DOMContentLoaded 大致相同的时刻触发 readystatechange 事件。
  • 此时,DOM 树已经加载完毕,可以安全地操作它了,因此就会进入交互(interactive)阶段。
  • 但与此同时,图像及其他外部文件不一定可用。下面来看一段处理readystatechange 事件的代码。
EventUtil.addHandler(document, "readystatechange", function(event){
     if (document.readyState == "interactive"){
        alert("Content loaded");
     }
});

这个事件的 event 对象不会提供任何信息,也没有目标对象。

2.3 与load事件一起使用

  • 在与 load 事件一起使用时,无法预测两个事件触发的先后顺序。在包含较多或较大的外部资源的 页面中,会在 load 事件触发之前先进入交互阶段;而在包含较少或较小的外部资源的页面中,则很难 说 readystatechange 事件会发生在 load 事件前面。

  • 交互阶段可能会早于也可能会晚于完成阶段出现,无法确保顺序。因此,为了尽可能抢到先机,有必要同时检测交互和完成阶段。

EventUtil.addHandler(document, "readystatechange", function(event){
     if (document.readyState == "interactive" || document.readyState == "complete"){
         EventUtil.removeHandler(document, "readystatechange", arguments.callee);
         alert("Content loaded");
     }
}); 

对于上面的代码来说,当 readystatechange 事件触发时,会检测 document.readyState 的值, 看当前是否已经进入交互阶段或完成阶段。如果是,则移除相应的事件处理程序以免在其他阶段再执行。 注意,由于事件处理程序使用的是匿名函数,因此这里使用了 arguments.callee 来引用该函数。然 后,会显示一个警告框,说明内容已经加载完毕。这样编写代码可以达到与使用 DOMContentLoaded 十分相近的效果。

2.4 script标签

  • readyState 属性无论等于 “loaded” 还 是”complete“都可以表示资源已经可用。
    • 有时候,readyState 会停在”loaded“阶段而永远不会“完成”;
    • 有时候,又会跳过”loaded“阶段而直接“完成”。于是,还需要像对待 document 一样采取相同的编码方式。
EventUtil.addHandler(window, "load", function(){
     var script = document.createElement("script");
     EventUtil.addHandler(script, "readystatechange", function(event){
         event = EventUtil.getEvent(event);
         var target = EventUtil.getTarget(event);
         if (target.readyState == "loaded" || target.readyState == "complete"){
             EventUtil.removeHandler(target, "readystatechange", arguments. callee);
             alert("Script Loaded");
         }
     });
     script.src = "example.js";
     document.body.appendChild(script);
}); 

这个例子为新创建的<script>节点指定了一个事件处理程序。事件的目标是该节点本身,因此当 触 发 readystatechange 事件时,要检测目标的 readyState 属性是不是等于”loaded“或 “complete“。如果进入了其中任何一个阶段,则移除事件处理程序(以防止被执行两次),并显示一个 警告框。与此同时,就可以执行已经加载完毕的外部文件中的函数了。

  • 同样的编码方式也适用于通过元素加载 CSS 文件的情况,如下面的例子所示。
EventUtil.addHandler(window, "load", function(){
     var link = document.createElement("link");
     link.type = "text/css";
     link.rel= "stylesheet";
     EventUtil.addHandler(script, "readystatechange", function(event){
         event = EventUtil.getEvent(event);
         var target = EventUtil.getTarget(event);
         if (target.readyState == "loaded" || target.readyState == "complete"){
             EventUtil.removeHandler(target, "readystatechange", arguments. callee);
             alert("CSS Loaded");
         }
     });
     link.href = "example.css";
     document.getElementsByTagName("head")[0].appendChild(link);
}); 

对于本文内容有问题或建议的小伙伴,欢迎在文章底部留言交流讨论。