执行AJAX返回HTML片段中的JavaScript脚本

如果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脚本

时间: 2024-10-10 04:51:50

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

在&lt;a&gt;&lt;/a&gt;标签中调用javascript脚本

有时候,我们点击了<a></a>标签(除了跳转到指定链接外)想要它调用某个方法,及调用javascript脚本,该如何做: 方法1:<a href="javascript:void(0);" onclick="functionname()" >aaaa</a> 方法2:<a href="javascript:functionname('dd')" >ddddd</a> 方法3

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

JSON:JavaScript 对象表示法(JavaScript Object Notation) 和xml相似,都是文本形式(保存在文本文件中或字符串等形式),比如: jsstr = {"系别":"历史系","班级":"一班", "学员":[ {"姓名":"张三","年龄":25,"性别":"男"}, {&

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

Response.Write 与   Page.ClientScript.RegisterStartupScript 与 Page.ClientScript.RegisterClientScriptBlock 之间的区别 方法1,使用Response.Write,这种方法会把JS代码写在页面的最顶部(<html>的前面):System.Web.HttpContext.Current.Response.Write("<script language=javascript>a

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

1   概述 VS2017可以调试JS,本篇文章简要概述VS2017关于启用和关闭VS调试功能. 2   具体内容 当开启VS2017JS调试功能时,我们用VS2017打开解决方案时,会出现如下界面: 关闭VS2017 js调试功能: 工具=>选项 调试=>去掉"启用ASP.NET的JavaScript调试(Chrome和IE)"勾号 用VS2017启动解决方案,就不会弹出如下界面,且关闭Chrome时,VS解决方案不会关闭 3   参考文献 [01]https://blo

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

一.使用<script>元素 1.<script>元素定义了6个属性: async:可选.表示应该立即下载脚本,但不应该妨碍页面中的其他操作,比如下载其他资源或等待加载其他脚本.只对外部脚本文件有效. charset:可选.表示通过src属性指定的代码的字符集.很少人用. defer:可选.表示脚本可以延迟到文档完全被解析和显示之后再执行.只对外部文件有效. language:已废弃. src:可选.表示包含要执行代码的外部文件. type:可选.表示编写代码使用的脚本语言的内容类

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

JavaScript脚本通常放置在三个位置: 1.head部分JavaScript脚本. 2.body部分JavaScript脚本. 3.单独以.js结尾的文件中的JavaScript脚本. 客户端会顺序读取并解析文档内容,body部分的JavaScript脚本会优先执行,其他部分的JavaScript脚本会通过调用执行. head部分JavaScript脚本 <html> <head> <script type="text/javascript">

1-1 在html中插入JavaScript

1.在html文件当中,使用<script>标签在HTML网页中插入JavaScript代码.<script>标签要成对出现,并把JavaScript代码写在<script></script>之间 1 <!DOCTYPE html> 2 <html lang="en"> 3 4 <head> 5 <meta charset="UTF-8"> 6 <title>

Visual studio 2017 中的Javascript智能提示

1.智能提示 对于JS文件中的API,你若需要让那个JS文件中的方法能够在你写的那个JS文件中能够智能显示的话,直接把它拉进你的JS文件中就好了. 举个例子:你想 在你正在写的a.js文件中引用b.js中的功能与函数.很简单,你只需要把b.js文件拉到你的a.js文件中就好了.这时在你的文件 顶端会显示这样一行内容:/// <reference path="../angular.js" />. 就是这样,智能显示的很全面,就像你自己写类文件一样,里面的注释什么的都全部显示出

(五)CodeMirror - 关于htmlmixed中包含script脚本

最近发现个问题,场景如下: 当创建的mode类型为htmlmixed,且内容中包含javascript脚本,且是闭包立即执行: 如果内容是使用JQuery函数.html()插入到DOM中后再创建codeMirror对象: 那么,这时,如果JS执行报错,那么随后创建codeMirror对象也中断了: 解决方法: 可以使用elem.text()方法插入内容,那么有错的JS也不会被执行到,可顺利进行后面的创建 1 var elem = $('#code'); 2 var str = ''<div cl