js实现动态加载脚本的方法实例汇总


Posted in Javascript onNovember 02, 2015

本文实例讲述了js实现动态加载脚本的方法。分享给大家供大家参考,具体如下:

最近公司的前端地图产品需要做一下模块划分,希望用户用到哪一块的功能再加载哪一块的模块,这样可以提高用户体验。

所以到处查资料研究js动态脚本的加载,不过真让人伤心啊!,网上几乎都是同一篇文章,4种方法,讨厌其中拷贝别人成果的人,也不加个原文的链接。哎!关键是最后一种方法还有点错误。经过两天的研究查阅资料,在这里和大家分享一下。

首先我们需要一个被加载的js文件,我在一个固定文件夹下创建了一个package.js,打开后在里面写一个方法functionOne,很简单,代码如下:

function functionOne(){
  alert("成功加载");
}

后面的html文件都创建在同一个目录下。

方法一:直接document.write

在同一个文件夹下面创建一个function1.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      //加载js脚本
      document.write("<script src='package.js'><\/script>");
      //加载一个按钮
      document.write("<input type=\"button\" value=\"测试运行效果\" onclick=\"operation()\"\/>");
      //如果马上使用会找不到,因为还没有加载进来,此处会报错
      functionOne();
    }
    function operation()
    {
      //可以运行,显示“成功加载”
      functionOne();
    }
  </script>
</head>
<body>
  <input type="button" value="初始化加载" onclick="init()"/>
</body>
</html>

通过document.write的方式可以往页面写入脚本,如代码所示,点击按钮“初始化加载”后可以加载package.js文件,但是立即运行里面的方法functionOne会找不到此方法,报告错误,而点击第二个按钮(通过document.write动态创建的“测试运行效果”)发现可以执行,此时脚本已经加载完毕了。由于这种方式是异步加载(一边继续执行后面的代码,一边额外开一个线程执行需要加载的脚本),并且document.write会重写界面,明显不实用。

方法二:动态改变已有script的src属性

在同一个文件夹下面创建一个function2.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript" id="yy" src=""></script>
  <script type="text/javascript">
    function init()
    {
      yy.src = "package.js";
      //如果马上使用会找不到,因为还没有加载进来,此处会报错
      functionOne();
    }
    function operation()
    {
      //可以运行,显示“成功加载”
      functionOne();
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
  <input type="button" value="测试运行效果" onclick="operation()"/>
</body>
</html>

此种方法的好处在于不会改变界面元素,不至于重写界面元素,但是同样是异步加载,会有同样的问题。

方法三:动态创建script元素(异步)

在同一个文件夹下面创建一个function3.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      var myScript= document.createElement("script");
      myScript.type = "text/javascript";
      myScript.src="package.js";
      document.body.appendChild(myScript);
      //如果马上使用会找不到,因为还没有加载进来
      functionOne();
    }
    function operation()
    {
      //可以运行,显示“成功加载”
      functionOne();
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
  <input type="button" value="测试运行效果" onclick="operation()"/>
</body>
</html>

此办法的优势相对于第二种而言就是不需要最开始就在界面写一个script标签,缺点还是异步加载,存在同样的问题。

这三种方法都是异步执行的,所以在加载这些脚本的同时,主页面的脚本继续运行,如果用以上的方法,那下面的代码将得不到预期的效果。

不过可以在functionOne前面加一个alert就可以堵塞一下主页面脚本的运行,然后你发现functionOne就可以运行了,或者你的后期代码需要在另一个按钮下执行,一步一步的来,要不就定义一个计时器,在固定时间后再执行后面的代码,不过在项目里面肯定不可能使用这些方法。

其实第三种方法改一点就变成同步加载了。

方法四:动态创建script元素(同步)

在同一个文件夹下面创建一个function4.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      var myScript= document.createElement("script");
      myScript.type = "text/javascript";
      myScript.appendChild(document.createTextNode("function functionOne(){alert(\"成功运行\"); }"));
      document.body.appendChild(myScript);
      //此处发现可以运行
      functionOne();
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
</body>
</html>

此方法并没有加载外部的js文件,而是给myScript添加了子项。在Firefox、Safari、Chrome、Opera和IE9中,这些代码可以正常运行。但是在IE8以及以下的版本中会导致错误。IE将<script>视为一个特殊的元素,不允许DOM访问其子节点。不过可以用<script>元素的text属性来制定js代码,想下面的例子这样:

var myScript= document.createElement("script");
myScript.type = "text/javascript";
myScript.text = "function functionOne(){alert(\"成功运行\"); }";
document.body.appendChild(myScript);
//此处可以运行
functionOne();

经过这样修改之后的代码可以在IE、Firefox、Opera和Safari3及之后版本中运行。Safari3.0之前的版本虽然不能正确地支持text属性,但却允许使用文本节点技术来指定代码。如果需要兼容早期版本的Safari,可以使用下面代码:

var myScript= document.createElement("script");
myScript.type = "text/javascript";
var code = "function functionOne(){alert(\"成功运行\"); }";
try{
    myScript.appendChild(document.createTextNode(code));
}
catch (ex){
    myScript.text = code;
}
document.body.appendChild(myScript);
//此处发现可以运行
functionOne();

这里,首先尝试标准的DOM文本节点方法,因为除了IE8以及以下,所有浏览器都支持这种方式。如果这行代码抛出了错误,那么说明是IE8以及以下,于是就必须使用text属性了。整个过程可以用以下函数来表示:

function loadScriptString(code)
{
  var myScript= document.createElement("script");
  myScript.type = "text/javascript";
  try{
    myScript.appendChild(document.createTextNode(code));
  }
  catch (ex){
    myScript.text = code;
  }
  document.body.appendChild(myScript);
}

然后你可以在其他地方使用此方法来加载需要使用的代码。实际上,这样执行代码与在全局作用于中把相同字符串传递给eval()是一样的。但是我们这里只能使用字符串形式的代码,也有局限性,用户一般希望提供的方法形如loadScriptAddress("package.js")的方式,所以我们还需要继续讨论。

方法五:XMLHttpRequest/ActiveXObject异步加载

在同一个文件夹下面创建一个function5.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      //加载package.js文件,设置script的id为yy
      ajaxPage("yy","package.js");
      //此方法为package.js里面的方法,此处执行方法成功
      functionOne();
    }
    function ajaxPage(sId,url)
    {
      var oXmlHttp = getHttpRequest();
      oXmlHttp.onreadystatechange = function()
      {
        //4代表数据发送完毕
        if ( oXmlHttp.readyState == 4 )
        {
          //0为访问的本地,200代表访问服务器成功,304代表没做修改访问的是缓存
          if(oXmlHttp.status == 200 || oXmlHttp.status == 0 || oXmlHttp.status == 304)
          {
            includeJS(sId,oXmlHttp.responseText);
          }
          else
          {
          }
        }
      }
      oXmlHttp.open("GET",url,true);
      oXmlHttp.send(null);
    }
    function getHttpRequest()
    {
      if(window.ActiveXObject)//IE
      {
        return new ActiveXObject("MsXml2.XmlHttp");
      }
      else if(window.XMLHttpRequest)//其他
      {
        return new XMLHttpRequest();
      }
    }
    function includeJS(sId,source)
    {
      if((source != null)&&(!document.getElementById(sId)))
      {
        var myHead = document.getElementsByTagName("HEAD").item(0);
        var myScript = document.createElement( "script" );
        myScript.language = "javascript";
        myScript.type = "text/javascript";
        myScript.id = sId;
        try{
          myScript.appendChild(document.createTextNode(source));
        }
        catch (ex){
          myScript.text = source;
        }
        myHead.appendChild( myScript );
      }
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
</body>
</html>

ActiveXObject只有IE里面才有,其他浏览器大部分支持XMLHttpRequest,通过此办法我们可以实现动态加载脚本了,不过是异步加载,也没法运行functionOne,第二次就可以运行了,但是可惜的是在IE、Firefox、Safari下可以运行,在Opera、Chrome下会出错,Chrome下的错误如下:

js实现动态加载脚本的方法实例汇总

不过只要发布之后在Chrome和Opera下就不会出现错误了。

其实这里把open里面设置为false就是同步加载了,同步加载不需要设置onreadystatechange事件。

方法六:XMLHttpRequest/ActiveXObject同步加载 

在这里我把一些情况考虑在内,写成了一个方法,封装为loadJS.js,方便以后直接调用,代码如下:

/**
 * 同步加载js脚本
 * @param id  需要设置的<script>标签的id
 * @param url  js文件的相对路径或绝对路径
 * @return {Boolean}  返回是否加载成功,true代表成功,false代表失败
 */
function loadJS(id,url){
  var xmlHttp = null;
  if(window.ActiveXObject)//IE
  {
    try {
      //IE6以及以后版本中可以使用
      xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
    }
    catch (e) {
      //IE5.5以及以后版本可以使用
      xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
    }
  }
  else if(window.XMLHttpRequest)//Firefox,Opera 8.0+,Safari,Chrome
  {
    xmlHttp = new XMLHttpRequest();
  }
  //采用同步加载
  xmlHttp.open("GET",url,false);
  //发送同步请求,如果浏览器为Chrome或Opera,必须发布后才能运行,不然会报错
  xmlHttp.send(null);
  //4代表数据发送完毕
  if ( xmlHttp.readyState == 4 )
  {
    //0为访问的本地,200到300代表访问服务器成功,304代表没做修改访问的是缓存
    if((xmlHttp.status >= 200 && xmlHttp.status <300) || xmlHttp.status == 0 || xmlHttp.status == 304)
    {
      var myHead = document.getElementsByTagName("HEAD").item(0);
      var myScript = document.createElement( "script" );
      myScript.language = "javascript";
      myScript.type = "text/javascript";
      myScript.id = id;
      try{
        //IE8以及以下不支持这种方式,需要通过text属性来设置
        myScript.appendChild(document.createTextNode(xmlHttp.responseText));
      }
      catch (ex){
        myScript.text = xmlHttp.responseText;
      }
      myHead.appendChild( myScript );
      return true;
    }
    else
    {
      return false;
    }
  }
  else
  {
    return false;
  }
}

此处考虑到了浏览器的兼容性以及当为Chrome、Opera时必须是发布,注释还是写的比较清楚的,以后需要加载某个js文件时,只需要一句话就行了,如loadJS("myJS","package.js")。方便实用。

如果想要实现不发布还非要兼容所有浏览器,至少我还没找出这样的同步加载的办法,我们只能通过异步加载开出回调函数来实现。

方法七:回调函数方式

在同一个文件夹下面创建一个function7.html,代码如下:

<html>
<head>
  <title></title>
  <script type="text/javascript">
    function init()
    {
      //加载package.js文件,设置script的id为yy
      loadJs("yy","package.js",callbackFunction);
    }
    function callbackFunction()
    {
      functionOne();
    }
    function loadJs(sid,jsurl,callback){
      var nodeHead = document.getElementsByTagName('head')[0];
      var nodeScript = null;
      if(document.getElementById(sid) == null){
        nodeScript = document.createElement('script');
        nodeScript.setAttribute('type', 'text/javascript');
        nodeScript.setAttribute('src', jsurl);
        nodeScript.setAttribute('id',sid);
        if (callback != null) {
          nodeScript.onload = nodeScript.onreadystatechange = function(){
            if (nodeScript.ready) {
              return false;
            }
            if (!nodeScript.readyState || nodeScript.readyState == "loaded" || nodeScript.readyState == 'complete') {
              nodeScript.ready = true;
              callback();
            }
          };
        }
        nodeHead.appendChild(nodeScript);
      } else {
        if(callback != null){
          callback();
        }
      }
    }
  </script>
</head>
<body>
  <input type="button" value="测试按钮" onclick="init()"/>
</body>
</html>

这种方式所有浏览器都支持,但是后面的代码必须放在回调函数里面,也就是异步加载了。看需求使用把!我还是比较喜欢第六种方法的。如果是异步加载的话,方法还有好几种,不过我的出发点是希望实现同步加载,这里就不对异步加载做总结了。

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
获取div编辑框,textarea,input text的光标位置 兼容IE,FF和Chrome的方法介绍
Nov 08 Javascript
Three.js源码阅读笔记(Object3D类)
Dec 27 Javascript
jquery获取div宽度的实现思路与代码
Jan 13 Javascript
基于javascript实现浏览器滚动条快到底部时自动加载数据
Nov 30 Javascript
基于jQuery实现仿百度首页选项卡切换效果
May 29 Javascript
jQuery禁用快捷键例如禁用F5刷新 禁用右键菜单等的简单实现
Aug 31 Javascript
vue双向绑定的简单实现
Dec 22 Javascript
完美的js图片轮换效果
Feb 05 Javascript
BootstrapTable refresh 方法使用实例简单介绍
Feb 20 Javascript
angular中实现控制器之间传递参数的方式
Apr 24 Javascript
JS查找数组中重复元素的方法详解
Jun 14 Javascript
js实现微信/QQ直接跳转到支付宝APP打开口令领红包功能
Jan 09 Javascript
Jquery时间轴特效(三种不同类型)
Nov 02 #Javascript
Jquery全屏相册插件zoomvisualizer具有调节放大与缩小功能
Nov 02 #Javascript
关注jquery技巧提高jquery技能(前端开发必学)
Nov 02 #Javascript
18个非常棒的jQuery代码片段
Nov 02 #Javascript
js实现文件上传表单域美化特效
Nov 02 #Javascript
非常实用的12个jquery代码片段
Nov 02 #Javascript
jQuery+CSS3折叠卡片式下拉列表框实现效果
Nov 02 #Javascript
You might like
php 上传文件类型判断函数(避免上传漏洞 )
2010/06/08 PHP
yii的CURD操作实例详解
2014/12/04 PHP
yii2使用ajax返回json的实现方法
2016/05/14 PHP
php中mkdir()函数的权限问题分析
2016/09/24 PHP
laravel 解决多库下的DB::transaction()事务失效问题
2019/10/21 PHP
数据结构之利用PHP实现二分搜索树
2020/10/25 PHP
超级酷和最实用的jQuery实例收集(20个)
2010/04/21 Javascript
javascript中用星号表示预录入内容的实现代码
2011/01/08 Javascript
JavaScript lastIndexOf方法入门实例(计算指定字符在字符串中最后一次出现的位置)
2014/10/17 Javascript
JsRender实用入门教程
2014/10/31 Javascript
JavaScript分秒倒计时器实现方法
2015/02/02 Javascript
JavaScript实现的圆形浮动标签云效果实例
2015/08/06 Javascript
js实现完美兼容各大浏览器的人民币大小写相互转换
2015/10/29 Javascript
使用JavaScript判断用户输入的是否为正整数(两种方法)
2017/02/05 Javascript
angular6.0使用教程之父组件通过url传递id给子组件的方法
2018/06/30 Javascript
vue.js单文件组件中非父子组件的传值实例
2018/09/13 Javascript
NodeJS使用Range请求实现下载功能的方法示例
2018/10/12 NodeJs
跨域请求两种方法 jsonp和cors的实现
2018/11/11 Javascript
js Array.slice的8种不同用法示例
2019/07/10 Javascript
JS访问对象两种方式区别解析
2020/08/29 Javascript
js 数据类型判断的方法
2020/12/03 Javascript
在Python中使用dict和set方法的教程
2015/04/27 Python
总结python爬虫抓站的实用技巧
2016/08/09 Python
selenium+python实现1688网站验证码图片的截取功能
2018/08/14 Python
python 计算数据偏差和峰度的方法
2019/06/29 Python
python 函数的缺省参数使用注意事项分析
2019/09/17 Python
python 插入日期数据到Oracle实例
2020/03/02 Python
Python开发之身份证验证库id_validator验证身份证号合法性及根据身份证号返回住址年龄等信息
2020/03/20 Python
英国最大的经认证的有机超市:Planet Organic
2018/02/02 全球购物
瑞士最大的图书贸易公司:Orell Füssli
2019/12/28 全球购物
《歌唱二小放牛郎》教学反思
2014/04/19 职场文书
教师自我剖析材料(四风问题)
2014/09/30 职场文书
对外汉语教师推荐信
2015/03/27 职场文书
红色经典电影观后感
2015/06/18 职场文书
Python基于Tkinter开发一个爬取B站直播弹幕的工具
2021/05/06 Python
教你使用一行Python代码玩遍童年的小游戏
2021/08/23 Python