如果AJAX加载的数据是一个HTML片段,而且这个HTML片段还包含脚本<script>块,那么在你把这数据xmlHttp.responseText用innerHTML方法插入到当前文档一个元素中,你会发现AJAX加载回来的脚本根本没有执行。这是AJAX开发中很常见的问题,如果你不是一直在用JavaScript框架做开发,相信你早就发现这个问题了。本文分析了两个解决办法,其中一个是讲解jQuery框架的实现。

一、 问题描述

下面举个简单的例子,演示问题所在。在下面的例子中,假设变量responseText就是AJAX加载的HTML片段数据,其中包含脚本弹出一条消息,用innerHTML方法插入ID为ajaxData的DIV中,你可能期望看到弹出那个消息框,结果你发现没有,问题就是这样。

<div id="ajaxData"></div>

    <script type="text/javascript">var=';

        document.getElementById(ajaxData).innerHTML  responseText;

        </script>

二、两种解决办法

1、 利用JavaScript的eval方法执行脚本。

本方法的具体实现思路是把xmlHttp.responseText中的脚本都抽取出来,不管AJAX加载的HTML包含多少个脚本块,我们对找出来的脚本块都调用eval方法执行它即可。下面提供一个封装好的函数:


function executeScript(html)

    {

        var reg = /<script[^>]*>([^\x00]+)$/i;

          //对整段HTML片段按<\/script>拆分

            var htmlBlock = html.split("<\/script>");

              for (var i in htmlBlock) 

                {

                    var blocks;//匹配正则表达式的内容数组,blocks[1]就是真正的一段脚本内容,因为前面reg定义我们用了括号进行了捕获分组

                      if (blocks = htmlBlock[i].match(reg)) 

                        {

                            //清除可能存在的注释标记,对于注释结尾-->可以忽略处理,eval一样能正常工作

                              var code = blocks[1].replace(/<!--/, '');

                                try 

                                  {

                                      eval(code) //执行脚本

                                    } 

                                      catch (e) 

                                        {

                                        }

                                    }

                                }

                            }

本方法的使用如下,对HTML用innerHTML方法添加到DOM,紧跟着调用executeScript方法执行脚本块:

document.getElementById("div1").innerHTML = xmlHttp.responseText;

    executeScript(xmlHttp.responseText);

显然这个方法还是存在缺陷的,如果xmlHttp.responseText包含像这样的外部脚本调用:
<script type="text/javascript" src="/js/common.js"></script>,executeScript方法不能再深入执行这个外部加载的脚本。

2、 学习并使用jQuery框架的实现

jQuery对于AJAX加载HTML,是最终在执行html(value)方法时把整个xmlHttp.responseText数据转换成DOM,然后利用DOM相关操作方法来找出里面的脚本,最后再把这些脚本插入到head中。具体原理也不好说,先举个最简单的例子,然后再分析一下大致思路。先看例子:

$.get('ajax.aspx', function(data)

    {

        $('#div1').html(data);

      });

现在假设上面ajax.aspx页面返回的是HTML片段,而且包含一个或多个脚本块,甚至外部脚本引用。div1是AJAX请求发起页的一个DIV标签的ID,整句代码实现的结果是加载ajax.aspx中的HTML填充到一个ID为div1的DIV标签中。

在匿名回调函数中通过typeof(data)可以发现data还是原始的字符串,即等同于xmlHttp.responseText,通过代码执行跟踪发现,对AJAX加载脚本片段的执行处理不在jQuery的AJAX模块代码中,而是在html(value)方法,即把一段包含脚本块的HTML字符串插入DOM时,由它负责抽出脚本进行调用处理。而html(value)方法其实又是调用了append(value)方法……,整个过程大概调用了以下方法,箭头代表调用这些方法的先后顺序:

html -> append -> domManip -> clean -> evalScript -> globalEval

其中clean方法特别关键,这个方法也是jQuery比较重要的方法,其中也涉及修复HTML错误(标签没有结束,表格结构调整等方法)处理脚本。而脚本的抽出也是在这里进行的。看看相关源代码(jQuery1.3.2):


if (fragment) 

    {

        for (var i = 0; ret[i]; i++) 

          {

              if (jQuery.nodeName(ret[i], "script") && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript")) 

                {

                    scripts.push(ret[i].parentNode ? ret[i].parentNode.removeChild(ret[i]) : ret[i]);

                  }

                  else 

                    {

                        if (ret[i].nodeType === 1) 

                              ret.splice.apply(ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))));

                            fragment.appendChild(ret[i]);

                        }

                    }

                    return scripts;

                  }

另外,在evalScript方法中我们还发现如下代码,这里是“同”步加载像<script
type="text/javascript"
src="/js/common.js"></script>这样的外部脚本,解决executeScript方法存在的一个缺陷:


if (elem.src) 

        jQuery.ajax(

        {

            url: elem.src,

            async: false,

              dataType: "script"

            });

同时也发现如下代码,这段代码是把xmlHttp.responseText中的脚本删除,因为在这个方法中,jQuery是准备把抽取的脚本放入head区,所以删除可以避免最终的HTML出现重复的脚本块:

if (elem.parentNode)

        elem.parentNode.removeChild(elem);

最后,在globalEval方法中,发现head.removeChild( script
);方法,就是把脚本插入head后马上又移除脚本标签,这也是避免因为重复执行html(value)方法在head区生成重复的脚本块。这个移除是不影响脚本执行的,同是也是不会清除脚本块中的相关变量值。显然,如果你想看看html(data)最终的执行结果,比如抽取后插入到head的脚本块是什么,你可以先临时注释这一行代码。

执行AJAX返回HTML片段中的JavaScript脚本的更多相关文章

  1. 在<a></a>标签中调用javascript脚本

    有时候,我们点击了<a></a>标签(除了跳转到指定链接外)想要它调用某个方法,及调用javascript脚本,该如何做: 方法1:<a href="javas ...

  2. OpenJDK源码研究笔记(十六):在Java中使用JavaScript脚本语言

    友情提示 本文主要参考了51CTO上的一篇文章,代码经过自己的模仿和整理,还算凑合. 本文中的代码注释比较多,不再过多解释. 更多用法,还是得看JDK的API或者看原文http://developer ...

  3. VBA中使用JavaScript脚本语言解析JSON数据

    JSON:JavaScript 对象表示法(JavaScript Object Notation) 和xml相似,都是文本形式(保存在文本文件中或字符串等形式),比如: jsstr = {" ...

  4. ASP.NET CS文件中输出JavaScript脚本的3种方法以及区别

    Response.Write 与   Page.ClientScript.RegisterStartupScript 与 Page.ClientScript.RegisterClientScriptB ...

  5. ASP.NET CS文件中输出JavaScript脚本

    ClientScript.RegisterStartupScript:http://msdn.microsoft.com/zh-cn/library/system.web.ui.clientscrip ...

  6. 【VS2017新特性】在VS中调试javascript脚本

    1   概述 VS2017可以调试JS,本篇文章简要概述VS2017关于启用和关闭VS调试功能. 2   具体内容 当开启VS2017JS调试功能时,我们用VS2017打开解决方案时,会出现如下界面: ...

  7. 在VS中调试javascript脚本

    https://blog.csdn.net/u010228798/article/details/78207375

  8. JavaScript高级程序设计(二):在HTML中使用JavaScript

    一.使用<script>元素 1.<script>元素定义了6个属性: async:可选.表示应该立即下载脚本,但不应该妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本 ...

  9. JavaScript脚本在页面中放置的位置

    JavaScript脚本通常放置在三个位置: 1.head部分JavaScript脚本. 2.body部分JavaScript脚本. 3.单独以.js结尾的文件中的JavaScript脚本. 客户端会 ...

随机推荐

  1. Linux内核的特征

    Linux内核的特征 Linux是个人计算机和工作站上的Unix类操作系统.但是,它绝不是简化的Unix.相反,Linux是强有力和具有创新意义的Unix类操作系统.它不仅继承了Unix的特征,而且在 ...

  2. Hadoop 使用Combiner提高Map/Reduce程序效率

    众所周知,Hadoop框架使用Mapper将数据处理成一个<key,value>键值对,再网络节点间对其进行整理(shuffle),然后使用Reducer处理数据并进行最终输出. 在上述过 ...

  3. sed 之 & 符号

    sed 之 & 符号 摘自:https://blog.csdn.net/jasonliujintao/article/details/53509620 & 这个符号,其实很有用,在对相 ...

  4. .html与.text的异同

    .html与.text的方法操作是一样,只是在具体针对处理对象不同 .html处理的是元素内容,.text处理的是文本内容 .html只能使用在HTML文档中,.text 在XML 和 HTML 文档 ...

  5. (转)基于MVC4+EasyUI的Web开发框架形成之旅--界面控件的使用

    原文地址:http://www.cnblogs.com/wuhuacong/p/3317223.html 在前面介绍了两篇关于我的基于MVC4+EasyUI技术的Web开发框架的随笔,本篇继续介绍其中 ...

  6. 微信运动数据抓取(Python)

    "微信运动"能够向朋友分享一个包含有运动数据的网页,网页中就有我们需要的数据.url类似于:http://hw.weixin.qq.com/steprank/step/person ...

  7. easyui datagrid deleteRow(删除行)的BUG

    有时候想临时保存一些数据,等确定好后在批量一次提交,但EasyUI  datagrid 用的时候添加可以正常,如果从中间删除那行号就全乱了.导致删除的时候有可能删除上一行数据. function ad ...

  8. C#设计模式系列:适配器模式(Adapter Pattern)

    一.引言 在软件系统中,由于应用环境的变化,常常需要将“一些现存的对象”放在新的环境中应用.但是新环境要求的接口是这些现存对象所不满足的.如何应对这种“迁移的变化”?如何既能利用现有对象的良好实现,同 ...

  9. SQL多行并一行统计例子之STUFF()函数+FOR XML PATH()函数应用

    SELECT * FROM tbiz_ProjectRelation 目标统计每个项目有几条申请记录 Step1 SELECT ProjectID , RelationIDs , , '') FROM ...

  10. 多态的作用-游戏编程展示------新标准c++程序设计

    游戏软件的开发最能体现面向对象设计方法的优势.游戏中的人物.道具.建筑物.场景等都是很直观的对象,游戏运行的过程就是这些对象相互作用的过程.每个对象都有自己的属性和方法,不同对象也可能有共同的属性和方 ...