用AJAX返回HTML片段中的JavaScript脚本

这是AJAX开发中很常见的问题,如果你不是一直在用JavaScript框架做开发,相信你早就发现这个问题了。本文分析了两个解决办法,其中一个是讲解jQuery框架的实现。

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


代码如下:

<div id="ajaxData"></div>
<script type="text/javascript">
var responseText = '<p>这是一个段落</p><script>alert("这是AJAX加载回来的脚本片段")</script>';
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)

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

(0)

相关推荐

  • 分享8个最佳的代码片段在线测试网站

    有时候,我们需要测试一些代码片段,而电脑中没有安装针对该语言的运行环境,没关系,你可以在线测试它们. 本文为你带来 8 款非常好用的代码片段在线工具,帮助你快速.方便地测试.编辑代码片段. 1.   ideone ideone 是一个在线代码编辑.调试工具,允许开发者在线编译.运行代码,支持超过 40 种编程语言. 2.   Codepad Codepad 是一个在线编译/解释器,还是一个简单的协作工具.在文本框中粘贴代码片段,Codepad 将会运行它,并会生成一个短 URL,你可以共享给其他

  • 你一定会收藏的Nodejs代码片段

    以下就是为大家分享的四段Nodejs值得收集的代码片段,供大家参考,具体内容如下 1.关于Nodejs的静态资源压缩与缓存参见:Nodejs打造静态资源服务器与文件上传刚学Express那会,动态文件的gzip压缩,好长一段时间都没找到合适的方法,才发现就是这么简单-- app.js里添加compression模块: 先安装:var compress=require('compression');app.use(compress()); OK了动态文件也能gzip压缩了: 2.抓取妹子的图片:

  • 15个非常实用的JavaScript代码片段

    本文实例为大家分享了非常实用的js片段,供大家参考,具体内容如下 1.如何区分IE及非IE浏览器: if(!+[1,]){ //IE 11 不支持 alert("这是 IE 浏览器"): }else{ alert("这不是 IE 浏览器"): } 2.将日期直接转换为数值: +new Date(); 3.非IE浏览器下将类数组对象 "arguments"转为数组: Array.prototype.slice.call(arguments); 4.

  • 一些有用的JavaScript和jQuery的片段分享

    为指定元素添加 CSS 类: 通过添加 CSS 类改变元素的外观和感觉,非常干净的方法,而不是添加内联样式.使用 jQuery,这很容易做到: $('#myelement').addClass('myclass'); 从指定元素移除 CSS 类: 你可能觉得添加 CSS 类的操作已经灰常牛逼,但我们还需要知道如何移除不需要的 CSS 类.下面的代码能做到这一点: $('#myelement').removeClass('myclass'); 检测指定元素是否具有某个 CSS 类: 如果应用程序或

  • JS跨域代码片段

    下面的代码块是js调用一般处理程序的代理来实现js跨域的.如果js需要多次跨域,推荐下面的方法. 复制代码 代码如下: public string GetInfo(HttpContext context) { string post = "a=XX&b=XX"; return CreateHttpRequest("https://www.XXXX.com", post, "POST"); } #region 构造请求 /// <su

  • JavaScript的漂亮的代码片段

    动态构建正则表达式 复制代码 代码如下: new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ) 来自sizzle,动态构建正则时,这样做避免了字符转义. 更灵活和巧妙的数字补零 复制代码 代码如下: function prefixInteger(num, length) {    return (num / Math.pow(10, length)).toFixed(length).substr

  • AngularJS中取消对HTML片段转义的方法例子

    今天尝试用 Rails 做后端提供 JSON 格式的数据, AngularJS 做前端处理 JSON 数据,其中碰到 AngularJS 获取的是一段 HTML 文本,如果直接使用 data-ng-bind 的话是被转义过的,使用 data-ng-bind-html 则可以取消转义. 但是直接使用 data-ng-bind-html 的话会提示错误 复制代码 代码如下: Error: [$sce:unsafe] Attempting to use an unsafe value in a saf

  • JavaScript和JQuery实用代码片段(一)

    (一)怎样用JQuery刷新一幅图片? 说明:我们都知道,当我们在请求一个资源(比如网页,图片等)的时候,如果该资源被缓存到浏览器了,那么请求返回的就是缓存的副本,不是我们希望获取的资源(该资源内容已经被更新了),此时最普遍的一个办法就是在请求的页面后面或者图片的src后面加上一个查询字符串"ran=" + Math.random(),这样就会请求到最新版本的资源啦! 代码: 复制代码 代码如下: $(imageObj).attr('src',$(imageObj).attr('src

  • 在多个页面使用同一个HTML片段的代码

    问题描述 有一个比较复杂的HTML片段(A),如果把这个HTML片段嵌入到其他页面中(B,C,D....). 问题的关键是在HTML片段中有大量的JavaScript逻辑需要处理,比如说分页,点击事件响应等. 对于这个问题,我们用一个简单的例子来说明: "页面上有一个按钮,点击此按钮引入一个HTML片段,此HTML片段中有分页按钮." 1. 使用IFrame 主页面,点击一个按钮向页面引入一个IFrame: 复制代码 代码如下: <script type="text/j

  • 总结AJAX相关JS代码片段和浏览器模型

    在.net开发中,充分利用免费控件是好事情,但是如果不能修改控件达到自己的需求,就要动用JS大法了,前提是研究好浏览器模型 的各种对象的方法属性.尤其是熟悉CSS+HTML就会做的很酷.就JS语言本身来说要求不高. 1.动态删除Table 里面内容技巧,不需要写太多代码,一行: tb.removeNode(true) 2.动态增加行,除了CreateElement方法,还可以这样比较短小: <table id=tb1></table> <SCRIPT> function

随机推荐