Unobtrusive JavaScript介绍

原文: http://blog.csdn.net/peterinor_/article/details/16367935

在asp.net MVC开发框架中,MS一直使用其开发的Unobtrusive JavaScript。

1.  Unobtrusive JavaScript介绍

说到Unobtrusive Ajax,就要谈谈UnobtrusiveJavaScript了,所谓Unobtrusive JavaScript即为非侵入式JavaScript,是目前在Web开发领域推行的一种思想。

相信大家一定都有直接在HTMLElements上面写上事件调用代码的情况,比如:

[html] view plaincopy
  1. <!--page.html-->
  2. <!doctype html>
  3. <html>
  4. <head>
  5. <title>Obtrusive JavaScript Page</title>
  6. <script type="text/ecmascript">
  7. function onload() {
  8. console.log("Page has be loaded");
  9. }
  10. </script>
  11. </head>
  12. <body onload="onload">
  13. <button onclick="alert('hello');">Hello</button>
  14. <div onclick="this.style.color='red'">
  15. Click to Change Color
  16. </div>
  17. </body>
  18. </html>

这种代码就叫做侵入式的JavaScript,因为这种情况下JavaScript代码和HTML代码混在在一起,不利于代码阅读以及然后维护,还会导致HTML代码混乱不堪。

而相对与上面的做法,非侵入式JavaScript则采用如下的做法来避免这种混乱:

[html] view plaincopy
  1. <!--page.html-->
  2. <!doctype html>
  3. <html>
  4. <head>
  5. <title>Unobtrusive JavaScript Page</title>
  6. <script src="page.js"></script>
  7. </head>
  8. <body>
  9. <button id="btnHello">Hello</button>
  10. <div id="colorChg">
  11. Click to Change Color
  12. </div>
  13. </body>
  14. </html>
[javascript] view plaincopy
  1. //page.js
  2. document.onload = function (e) {
  3. var body = document.getElementsByTagName('body')[0];
  4. body.onload = function(e) {
  5. console.log("Page has be loaded");
  6. }
  7. var btn = document.getElementById('btnHello');
  8. btn.onclick = function (e) {
  9. alert('hello');
  10. }
  11. var div = document.getElementById('colorChg');
  12. div.onclick = function (e) {
  13. this.style.color = 'red';
  14. }
  15. };

当然,为了让JavaScript看起来更加的优美,写起来更加的舒畅,更加的不那么费劲,可以使用现成的JavaScript类库完成上面的操作,比如jQuery:

[javascript] view plaincopy
  1. //page.js
  2. $(function (e) {
  3. $('body').click(function (e) {
  4. console.log("Page has be loaded");
  5. });
  6. $('#btnHello').click(function (e) {
  7. alert('hello');
  8. });
  9. $('#colorChg').click(function (e) {
  10. $(this).css('color', 'red');
  11. });
  12. });

2.  Unobtrusive JavaScript在ASP.NET MVC中的应用

使用VS新建一个ASP.NET MVC项目就会在~/Scripts/目录下面看到很多以unobtrusive结尾的javascript脚本文件,如:

今天的主角是ASP.NET MVC Unobtrusive Ajax,那么大家应该可以猜到主要还是jquery.unobtrusive-ajax.js和jquery.unobtrusive-ajax.min.js这两个文件。这就是ASP.NETMVC实现非侵入式Ajax的主要手段,若要在项目中使用Unobtrusive Ajax,那么一定要用到这两个文件中的一个,至于具体用哪个就不多废话了。下面来看看Unobtrusive Ajax在ASP.NET MVC中的使用。

ASP.NET MVC中使用Unobtrusive Ajax主要用到的是Ajax辅助方法,这些方法由AjaxHelper提供,定义在System.Web.Mvc.Ajax.AjaxExtensions类中。Ajax主要用到的方法有ActionLink,RouteLink,BeginForm,BeginRouteForm,当然这些方法都有各自重载的版本,具体参见MSDN文档,本文主要以具有代表性的BeginForm为例来进行说明,其他的用法大同小异。

ASP.NET MVC对BeginForm的使用提供了11个重载的版本,但是细细观察11个重载版本就会发现,这些重载中共同点是有一个AjaxOptions类型的参数,除此之外Ajax辅助方法的BeginForm和Html辅助方法的BeginForm相同参数版本之间并无差别,而这个AjaxOptions就是ASP.NET MVC实现Ajax方法依据。

为了便于观察测试效果,在新建的MVC项目的HomeController中增加如下Action:

[csharp] view plaincopy
  1. public ActionResult Index()
  2. {
  3. return View();
  4. }
  5. public string s(string q)
  6. {
  7. return "The Query String is : " + q;
  8. }

其中Action ‘Index’用于显示Ajax操作页面,Action ‘s’用来响应Ajax请求结果。Index页面的主要内容如下:

[html] view plaincopy
  1. @using (Ajax.BeginForm(
  2. new AjaxOptions
  3. {
  4. Url = "Home/s",
  5. HttpMethod = "GET",
  6. UpdateTargetId = "searchResult",
  7. InsertionMode = InsertionMode.Replace
  8. }))
  9. {
  10. <input type="text" name="q" />
  11. <input type="submit" value="查询" />
  12. }
  13. <div id="searchResult"></div>

其作用就是生成一个表单,提交这个表单的时候执行异步的Ajax请求,并将请求结果回显到id为searchResult的div元素内。运行如下:

此处只用到了AjaxOptions的四个属性,AjaxOptions的其他属性如下表所示:

各个参数的具体意思都已经有解释了,深入的用法将在本文第三部分予以说明。上表中对参数进行了分组和着色以示区分其在BeginForm中的作用:前两个Url和HttpMethod算是Ajax请求的基础了,指示了Ajax请求的Url路径及所采用的Http方法;UpdateTargetId和InsertionMode是对请求成功后回显的设置,正如上例所示的那样;OnBegin、OnComplete、OnFailure以及OnSuccess四个属性则是对Ajax请求过程JavaScript回调的设置,具体是什么大家可望文生义了,这些值可以是具体的JavaScript语句或者是一个JavaScript函数;而Confirm则是在发起Ajax请求前页面进行确认的消息,页面通过window.confirm显示确认信息;最后,LoadingElementDuration和LoadingElementId这两个则属于锦上添花的东西了,用于在请求过程中显示页面动态请求情况,比如一个‘Loading…’的文字或者一个显示进度的图片。

下面是一个完整的例子:

[html] view plaincopy
  1. @using (Ajax.BeginForm(
  2. new AjaxOptions
  3. {
  4. Url = "Home/s",
  5. HttpMethod = "GET",
  6. UpdateTargetId = "searchResult",
  7. InsertionMode = InsertionMode.Replace,
  8. OnBegin = "alert('开始请求');",
  9. OnComplete = "Ajax.GetData_Com",
  10. OnSuccess = "alert('请求成功');",
  11. OnFailure = "alert('请求失败');",
  12. Confirm = "确认发起搜索",
  13. LoadingElementDuration = 500,
  14. LoadingElementId = "waiting"
  15. }))
  16. {
  17. <input type="text" name="q" />
  18. <input type="submit" value="查询" />
  19. <span id="waiting" style="display: none">Loading...</span>
  20. }
  21. <div id="searchResult"></div>
  22. <script>
  23. Ajax = {};
  24. Ajax.GetData_Com = function (data, status, xhr) {
  25. console.log(data);
  26. };
  27. </script>

当然,真正的Unobtrusive自然是要将下面的脚本单独放到一个js文件中的,此处为了示例就免去这个麻烦了^_^。

3.  ASP.NET MVC Unobtrusive Ajax原理剖析

看看上面的Ajax辅助方法,的确给设计带来了不小的便利和帮助,省去了很多手工使用$.ajax或者$.get或者$.post的不便,那么这种便利是怎么实现的,鄙人有一毛病,遇到问题总要刨根问题搞个清楚。下面权当是自己分析的一些心得,与大家分享,不善之处,还望不吝赐教。

前面说到ASP.NET MVC实现Unobtrusive Ajax主要使用的是jquery.unobtrusive-ajax.js这个JavaScript脚本,这个当然是首先要看的喽。可是打开文件发现里面有不少类似data-ajax-*这样的Html属性,接触过HTML5的筒子们都应该知道这是HTML5预留给用户程序使用的属性,这自然是和Html相关,看来有必要看看Ajax.BeginForm辅助方法生成的Html代码哦。

原谅我还拿前面的例子,Razor引擎生成的Html脚本如下(原谅我为了便于阅读做了稍微的调整):

[html] view plaincopy
  1. <form action="/"
  2. data-ajax="true"
  3. data-ajax-url="Home/s"
  4. data-ajax-method="GET"
  5. data-ajax-begin="alert('开始请求');"
  6. data-ajax-complete="Ajax.GetData_Com"
  7. data-ajax-update="#searchResult"
  8. data-ajax-mode="replace"
  9. data-ajax-success="alert('请求成功');"
  10. data-ajax-failure="alert('请求失败');"
  11. data-ajax-confirm="确认发起搜索"
  12. data-ajax-loading="#waiting"
  13. data-ajax-loading-duration="500"
  14. method="post">
  15. <input type="text" name="q" />
  16. <input type="submit" value="查询" />
  17. <span id="waiting" style="display: none">Loading...</span>
  18. </form>
  19. <div id="searchResult"></div>
  20. <script>
  21. Ajax = {};
  22. Ajax.GetData_Com = function (data, status, xhr) {
  23. console.log(data);
  24. };
  25. </script>

可见,Ajax.BeginForm辅助方法的确为表单添加了很多以data-ajax-开头的属性,你一定想到了,这些属性的生成肯定和AjaxOptions有关,Bingo,对应关系如下表:

可以说,ASP.NET MVC实现一个Unobtrusive Ajax所需的全部东西都在这里了。下面就来分析一下jquery.unobtrusive-ajax.js中的实现机制。

回想一下,通过jQuery发起一个Ajax请求的过程,ASP.NET MVC实现Unobtrusive Ajax则是基于jQuery的,你一定猜到了,这个过程的核心就是一次$.ajax()调用!!而如你所知,$.ajax()的调用其实也就是准备一个ajaxOptions!!

ASP.NET MVC完成这一过程的核心有两步:

  • 截获<form>的submit事件或者<a>的click事件,并获取Ajax.BeginForm或者Ajax.BeginRouteForm或者Ajax.ActionLink或者Ajax.RouteLink中设置AjaxOptions属性(这些属性分别对应类似data-ajax-*的html5属性,见上面表格),并通过这些属性设定$.ajax()调用所需的ajaxOptions。
  • 通过前面的ajaxOptions发起$.ajax()调用,完成Ajax请求。

jquery.unobtrusive-ajax.js中与此过程涉及的主要有两段代码(其他的代码都是辅助性代码,都为这两个服务)。

第一段在jquery.unobtrusive-ajax.js文件的最后面,如下:

代码段一:

[javascript] view plaincopy
  1. $("form[data-ajax=true]").live("submit", function (evt) {
  2. var clickInfo = $(this).data(data_click) || [];
  3. evt.preventDefault();
  4. if (!validate(this)) {
  5. return;
  6. }
  7. asyncRequest(this, {
  8. url: this.action,
  9. type: this.method || "GET",
  10. data: clickInfo.concat($(this).serializeArray())
  11. });
  12. });

或(注意是或)

[javascript] view plaincopy
  1. $("a[data-ajax=true]").live("click", function (evt) {
  2. evt.preventDefault();
  3. asyncRequest(this, {
  4. url: this.href,
  5. type: "GET",
  6. data: []
  7. });
  8. });

作为jQuery的专家,你一定一眼就能看出此段代码的意图,对,监控页面中所有具有data-ajax属性的表单的提交操作,此处用的是$.live()方法(关于$.live就不废话了),这就是页面中不需要手工再去截获表单的submit事件或者链接的click事件的原因所在!

第二段就是第一段中出现的asyncRequest()函数,该函数中有两处代码,大家一看就会明白整个Ajax请求发起的过程了:

代码段二:

[javascript] view plaincopy
  1. function asyncRequest(element, options) {
  2. var confirm, loading, method, duration;
  3. confirm = element.getAttribute("data-ajax-confirm");
  4. if (confirm && !window.confirm(confirm)) {
  5. return;
  6. }
  7. loading = $(element.getAttribute("data-ajax-loading"));
  8. duration = element.getAttribute("data-ajax-loading-duration") || 0;
  9. $.extend(options, {
  10. type: element.getAttribute("data-ajax-method") || undefined,
  11. url: element.getAttribute("data-ajax-url") || undefined,
  12. beforeSend: function (xhr) {
  13. var result;
  14. asyncOnBeforeSend(xhr, method);
  15. result = getFunction(element.getAttribute("data-ajax-begin"), ["xhr"]).apply(this, arguments);
  16. if (result !== false) {
  17. loading.show(duration);
  18. }
  19. return result;
  20. },
  21. complete: function () {
  22. loading.hide(duration);
  23. getFunction(element.getAttribute("data-ajax-complete"), ["xhr", "status"]).apply(this, arguments);
  24. },
  25. success: function (data, status, xhr) {
  26. asyncOnSuccess(element, data, xhr.getResponseHeader("Content-Type") || "text/html");
  27. getFunction(element.getAttribute("data-ajax-success"), ["data", "status", "xhr"]).apply(this, arguments);
  28. },
  29. error: getFunction(element.getAttribute("data-ajax-failure"), ["xhr", "status", "error"])
  30. });
  31. options.data.push({ name: "X-Requested-With", value: "XMLHttpRequest" });
  32. method = options.type.toUpperCase();
  33. if (!isMethodProxySafe(method)) {
  34. options.type = "POST";
  35. options.data.push({ name: "X-HTTP-Method-Override", value: method });
  36. }
  37. $.ajax(options);
  38. }

没错,这两段就完成ASP.NET MVC 的Ajax请求。而从这两段代码中你也一定大致看出了前面的OnBegin、OnComplete、OnFailure以及OnSuccess四个属性对应的Ajax请求过程JavaScript回调对应的是Ajax请求的哪个部分了:

这里有一个关键的函数地方‘getFunction’,它定义在jquery.unobtrusive-ajax.js文件的最前面:

[javascript] view plaincopy
  1. function getFunction(code, argNames) {
  2. var fn = window, parts = (code || "").split(".");
  3. while (fn && parts.length) {
  4. fn = fn[parts.shift()];
  5. }
  6. if (typeof (fn) === "function") {
  7. return fn;
  8. }
  9. argNames.push(code);
  10. return Function.constructor.apply(null, argNames);
  11. }

这个函数的功能就是从data-ajax-*中和JavaScript回调相关的属性中获取JavaScript回调函数。拿OnComplete来说,通过Html Element的getAttribute("data-ajax-complete")获取data-ajax-complete属性(设为oncomplete),首先假设oncomplete为定义在外部JavaScript脚本文件中的函数,然后一级一级的查找该函数,回到前面例子里面对AjaxOptions的设定OnComplete = "Ajax.GetData_Com",那么,此处getFunction函数将返回的是window.Ajax.GetData_Com这个函数(注意window是JavaScript默认的全局名称空间),那么请求完成后,window.Ajax.GetData_Com将会被调用,即:

window.Ajax.GetData_Com.apply(this, arguments);

这里的arguments就是$.ajax请求中complete回调的参数。假如,这里没有找到符合条件的函数,那么,getFunction将会把oncomplete的字符串值作为脚本进行执行,即:getFunction的最后一句:

return Function.constructor.apply(null, argNames);

这就是为什么可以直接在OnSuccess中写JavaScript脚本的原因哦。

PS:

OK,主体框架分析完成了,来分析一些其他的东西,主要是Ajax辅助方法里面的AjaxOptions设置问题。

1) 上面的例子主要以Form来分析,对于Link来说,以上所有的分析都成立,只是<form>变成了<a>,这也是他们的唯一区别,所有的原理是一样的,也就是监听所有具有data-ajax的<a>标签的click事件,然后发起ajax请求,这部分的代码和前面的代码段一作用是一样的,只是后者用于<form>而已,相应的代码段一就变成了下面这个样子:

[javascript] view plaincopy
  1. $("a[data-ajax=true]").live("click", function (evt) {
  2. evt.preventDefault();
  3. asyncRequest(this, {
  4. url: this.href,
  5. type: "GET",
  6. data: []
  7. });
  8. });

2) AjaxOptions设置问题

》Url:默认为form的action,上面提到的jquery.unobtrusive-ajax.js两段代码中对其进行了设置,如果有设置data-ajax-url属性,则取data-ajax-url属性作为ajax请求的url,否则取<form>的action属性(如果是链接的话,则取的是<a>的href属性)。

》HttpMethod:默认值为GET,这部分的设置和AjaxOptions.Url是相似的,对于Form来说优先级依次为data-ajax-method属性,form.action,"GET",对于Link来说就是"GET"。

》UpdateTargetId:原则上应是需要更新的html元素的id(MVC的Aajx辅助方法会自动在生成的data-ajax-update属性前面插入字符'#',参看前面Ajax.BeginForm生成的Html代码),但是细细查看代码,你会发现此处可为多个html元素,只要第一个不加#,之后的依次加上即可,如UpdateTargetId ="div1,#div2, #div3",可实现多个div同时更新,甚至于后面的可以是任何jQuery支持的css selector。这部分代码定义在文件jquery.unobtrusive-ajax.js里的函数asyncOnSuccess中:

[javascript] view plaincopy
  1. mode = (element.getAttribute("data-ajax-mode") || "").toUpperCase();
  2. $(element.getAttribute("data-ajax-update")).each(function (i, update) {
  3. var top;
  4. switch (mode) {
  5. case "BEFORE":
  6. top = update.firstChild;
  7. $("<div />").html(data).contents().each(function () {
  8. update.insertBefore(this, top);
  9. });
  10. break;
  11. case "AFTER":
  12. $("<div />").html(data).contents().each(function () {
  13. update.appendChild(this);
  14. });
  15. break;
  16. default:
  17. $(update).html(data);
  18. break;
  19. }
  20. });

核心是前两行代码:mode获取更新方式,共有三种模式,InsertBefore,InsertAfter,Replace(在枚举InsertionMode中定义),从上面的代买来看,默认的是Replace模式;再看下面的element.getAttribute("data-ajax-update"),对于上面的例子,获取到的应该是"#searchResult",如果将上例中的<UpdateTargetId = "searchResult">改成<UpdateTargetId ="searchResult, div.update">,那么被更新的除了# searchResult之外,还有所有具有‘update’这样的css类的div元素,原因就在那个each()!

》InsertionMode:只有在设置了UpdateTargetId之后才会生效

》LoadingElementDuration:只有在设置了LoadingElementId之后才会生效

Unobtrusive JavaScript介绍相关推荐

  1. Unobtrusive JavaScript 不唐突的JavaScript的七条准则

    Unobtrusive JavaScript是一种将Javascript从HTML结构抽离的设计概念,避免在HTML标签中夹杂一堆onchange.onclick--等属性去挂载Javascript事 ...

  2. ASP.NET MVC Unobtrusive JavaScript 实现 onfocusout 验证, onfocusin 清除错误

    在 ASP.NET MVC 中启用 Unobtrusive JavaScript 功能,可以在运行时由服务器端根据Model中设置的验证规则,自动生成客户端验证js代码(结合jquery.valida ...

  3. JavaScript 介绍

    简介 JS诞生于1995年 ,开发者希望它看起来像Java,因此取名为Javascript.两者的关系仅仅是名字相近,语法风格实际上与Self及Scheme较为接近. JS是一种解释型语言,它不需要编 ...

  4. 11.1 JavaScript介绍

    了解过一些编程的人都知道,有一门叫Java的编程语言,那么Java和JavaScript有什么关系呢? 打个比方,Java和JavaScript的关系就好像菠萝和菠萝蜜一样,也就是说,没啥关系. Ja ...

  5. [JavaScript高级程序设计]JavaScript介绍

    1.历史 1995年出生,提供浏览器中的输入验证功能: 如今,与浏览器窗口及其内容所有的交互功能,成为一门功能全面的编程语言: 特性:闭包.匿名函数等 2.内容 3.与ECMAScript ECMA- ...

  6. 前端三要素:HTML、CSS、JavaScript介绍以及三者的关系理解

    最近在重新系统的学习前端,接下来的博客会把一些前端知识总计下来,以便自己和大家学习参考. Web前端开发技术包括三个要素:html(结构).css(样式).javascript(行为动作). 现在前端 ...

  7. JavaScript是什么?JavaScript介绍

    JavaScript是什么 JavaScript(下面简称"JS"),是一个运行在客户端(浏览器)的脚本语言. JS可以分为两大块,分别是ECMAScript和Web APIs. ...

  8. JavaScript介绍

    1 引言 Javascript 简称:js 独立的编程语言. 作用:实现网页动态效果(改变的是页面的结构)的技术. 注意事项: 1.javascript 和 java 没关系 原名:ECMAScrip ...

  9. 10个强大的Javascript表单验证插件推荐

    创建一个JavaScript表单验证插件,可以说是一个繁琐的过程,涉及到初期设计.开发与测试等等环节.实际上一个优秀的程序员不仅是技术高手,也应该是善假于外物的.本文介绍了10个不错的JavaScri ...

最新文章

  1. 乔春洋:话说文化模式
  2. Asp.net core 启动流程
  3. 代码注入之远程线程篇
  4. 网络推广专员浅析网络推广切忌一蹴而就
  5. 第三百九十二节,Django+Xadmin打造上线标准的在线教育平台—sql注入攻击,xss攻击,csrf攻击...
  6. lintcode-easy-Insertion Sort List
  7. 图像处理之让手心长出眼睛,其实嘴也可以
  8. Android平台Qt开发入门教程
  9. java 登陆系统设计_Java 程序设计——登录系统
  10. 图像压缩算法动态规划c语言,动态规划算法实现数字图像压缩的研究.pdf
  11. 过椭圆外一点引两条切线方程_S16-2 二次曲线和圆方程
  12. 腾讯元老辞职后,回到老家每天对着200亩地发呆
  13. 消费者人群画像-信用智能评分(金融风控模型经典案例)
  14. python作业火车票订购系统_[Python爬虫]使用Selenium操作浏览器订购火车票
  15. CSDN 第六期编程竞赛做题记录
  16. [译]连接池和 Timeout expired异常
  17. 入门nosql数据库(非关系型数据库)
  18. linux下安装mariadb
  19. 恒源云(GPUSHARE)_未闻Prompt名(论文学习笔记)
  20. 火车头V9翻译插件-终极稳定版(翻译10000篇文章不报错)

热门文章

  1. Spring Cloud微服务实战(五)-应用通信
  2. Linux中ifcfg-eth0配置参数解释
  3. Spring Boot☞HelloWorld开篇
  4. 商城项目:装nginx时碰到的各种问题
  5. 看完这些福利才知道,为什么说双12一定要出去浪
  6. silverlight5
  7. mysql差异备份实现_结合Git实现Mysql差异备份,可用于生产环境
  8. Activiti源码 之 DataManager EntityManager
  9. android sqlite查询某个字段,Android的sqlite:如何检索特定列的特定数据?
  10. php qcloud sdk weapp_微信小程序源码+PHP后台