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
?? <!doctype?html> ?? < html > ??< head > ??????< title > Obtrusive?JavaScript?Page</ title > ?? ????< script ?type ="text/ecmascript" > ?? ????????function?onload()?{?? ????????????console.log("Page?has?be?loaded");?? ????????}?? ????</ script > ?? </ head > ??< body ?onload ="onload" > ??????< button ?onclick ="alert('hello');" > Hello</ button > ?? ????< div ?onclick ="this.style.color='red'" > ?? ????????Click?to?Change?Color?? ????</ div > ?? </ body > ??</ html > ??
這種代碼就叫做侵入式的JavaScript,因為這種情況下JavaScript代碼和HTML代碼混在在一起,不利于代碼閱讀以及然后維護,還會導致HTML代碼混亂不堪。
而相對與上面的做法,非侵入式JavaScript則采用如下的做法來避免這種混亂:
[html] view plaincopy
?? <!doctype?html> ?? < html > ??< head > ??????< title > Unobtrusive?JavaScript?Page</ title > ?? ????< script ?src ="page.js" > </ script > ?? </ head > ??< body > ??????< button ?id ="btnHello" > Hello</ button > ?? ????< div ?id ="colorChg" > ?? ????????Click?to?Change?Color?? ????</ div > ?? </ body > ??</ html > ??
[javascript] view plaincopy
?? document.onload?=?function ?(e)?{?? ????var ?body?=?document.getElementsByTagName('body' )[0];?? ????body.onload?=?function (e)?{?? ????????console.log("Page?has?be?loaded" );?? ????}?? ?? ????var ?btn?=?document.getElementById('btnHello' );?? ????btn.onclick?=?function ?(e)?{?? ????????alert('hello' );?? ????}?? ?? ????var ?div?=?document.getElementById('colorChg' );?? ????div.onclick?=?function ?(e)?{?? ????????this .style.color?=?'red' ;?? ????}?? };??
當然,為了讓JavaScript看起來更加的優美,寫起來更加的舒暢,更加的不那么費勁,可以使用現成的JavaScript類庫完成上面的操作,比如jQuery:
[javascript] view plaincopy
?? $(function ?(e)?{?? ????$('body' ).click(function ?(e)?{?? ????????console.log("Page?has?be?loaded" );?? ????});?? ????$('#btnHello' ).click(function ?(e)?{?? ????????alert('hello' );?? ????});?? ????$('#colorChg' ).click(function ?(e)?{?? ????????$(this ).css('color' ,?'red' );?? ????});?? });??
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
public ?ActionResult?Index()??{?? ????return ?View();?? }?? public ?string ?s(string ?q)??{?? ????return ?"The?Query?String?is?:?" ?+?q;?? }??
其中Action ‘Index’用于顯示Ajax操作頁面,Action ‘s’用來響應Ajax請求結果。Index頁面的主要內容如下:
[html] view plaincopy
@using?(Ajax.BeginForm(?? ????new?AjaxOptions?? ????{?? ????????Url ?=?"Home/s" ,?? ????????HttpMethod ?=?"GET" ,?? ????????UpdateTargetId ?=?"searchResult" ,?? ????????InsertionMode ?=?InsertionMode .Replace?? ????}))?? {?? ????< input ?type ="text" ?name ="q" ?/> ?? ????< input ?type ="submit" ?value ="查詢" ?/> ?? }?? < 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
@using?(Ajax.BeginForm(?? ????new?AjaxOptions?? ????{?? ????????Url ?=?"Home/s" ,?? ????????HttpMethod ?=?"GET" ,?? ????????UpdateTargetId ?=?"searchResult" ,?? ????????InsertionMode ?=?InsertionMode .Replace,?? ????????OnBegin ?=?"alert('開始請求');" ,?? ????????OnComplete ?=?"Ajax.GetData_Com" ,?? ????????OnSuccess ?=?"alert('請求成功');" ,?? ????????OnFailure ?=?"alert('請求失敗');" ,?? ????????Confirm ?=?"確認發起搜索" ,?? ????????LoadingElementDuration ?=?500 ,?? ????????LoadingElementId ?=?"waiting" ?? ????}))?? {?? ????< input ?type ="text" ?name ="q" ?/> ?? ????< input ?type ="submit" ?value ="查詢" ?/> ?? ????< span ?id ="waiting" ?style ="display:?none" > Loading...</ span > ?? }?? < div ?id ="searchResult" > </ div > ???? < script > ??????Ajax ?=?{};?? ????Ajax.GetData_Com ?=?function ?(data,?status,?xhr)?{?? ????????console.log(data);?? ????};?? </ 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
< form ?action ="/" ???????data-ajax ="true" ??? ????data-ajax-url ="Home/s" ?? ????data-ajax-method ="GET" ??? ????data-ajax-begin ="alert('開始請求');" ?? ????data-ajax-complete ="Ajax.GetData_Com" ??? ????data-ajax-update ="#searchResult" ??? ????data-ajax-mode ="replace" ??? ????data-ajax-success ="alert('請求成功');" ??? ????data-ajax-failure ="alert('請求失敗');" ??? ????data-ajax-confirm ="確認發起搜索" ??? ????data-ajax-loading ="#waiting" ??? ????data-ajax-loading-duration ="500" ??? ????method ="post" > ?????? ????< input ?type ="text" ?name ="q" ?/> ?? ????< input ?type ="submit" ?value ="查詢" ?/> ?? ????< span ?id ="waiting" ?style ="display:?none" > Loading...</ span > ?? </ form > ??< div ?id ="searchResult" > </ div > ???? < script > ??????Ajax ?=?{};?? ????Ajax.GetData_Com ?=?function ?(data,?status,?xhr)?{?? ????????console.log(data);?? ????};?? </ 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
$("form[data-ajax=true]" ).live("submit" ,?function ?(evt)?{?? ????var ?clickInfo?=?$(this ).data(data_click)?||?[];?? ????evt.preventDefault();?? ????if ?(!validate(this ))?{?? ????????return ;?? ????}?? ????asyncRequest(this ,?{?? ????????url:?this .action,?? ????????type:?this .method?||?"GET" ,?? ????????data:?clickInfo.concat($(this ).serializeArray())?? ????});?? });??
或(注意是或)
[javascript] view plaincopy
$("a[data-ajax=true]" ).live("click" ,?function ?(evt)?{?? ????evt.preventDefault();?? ????asyncRequest(this ,?{?? ????????url:?this .href,?? ????????type:?"GET" ,?? ????????data:?[]?? ????});?? });?? 作為jQuery的專家,你一定一眼就能看出此段代碼的意圖,對,監控頁面中所有具有data-ajax屬性的表單的提交操作,此處用的是$.live()方法(關于$.live就不廢話了),這就是頁面中不需要手工再去截獲表單的submit事件或者鏈接的click事件的原因所在!
第二段就是第一段中出現的asyncRequest()函數,該函數中有兩處代碼,大家一看就會明白整個Ajax請求發起的過程了:
??? 代碼段二:
[javascript] view plaincopy
function ?asyncRequest(element,?options)?{??????var ?confirm,?loading,?method,?duration;?? ?? ????confirm?=?element.getAttribute("data-ajax-confirm" );?? ????if ?(confirm?&&?!window.confirm(confirm))?{?? ????????return ;?? ????}?? ?? ????loading?=?$(element.getAttribute("data-ajax-loading" ));?? ????duration?=?element.getAttribute("data-ajax-loading-duration" )?||?0;?? ?? ????$.extend(options,?{?? ????????type:?element.getAttribute("data-ajax-method" )?||?undefined,?? ????????url:?element.getAttribute("data-ajax-url" )?||?undefined,?? ????????beforeSend:?function ?(xhr)?{?? ????????????var ?result;?? ????????????asyncOnBeforeSend(xhr,?method);?? ????????????result?=?getFunction(element.getAttribute("data-ajax-begin" ),?["xhr" ]).apply(this ,?arguments);?? ????????????if ?(result?!==?false )?{?? ????????????????loading.show(duration);?? ????????????}?? ????????????return ?result;?? ????????},?? ????????complete:?function ?()?{?? ????????????loading.hide(duration);?? ????????????getFunction(element.getAttribute("data-ajax-complete" ),?["xhr" ,?"status" ]).apply(this ,?arguments);?? ????????},?? ????????success:?function ?(data,?status,?xhr)?{?? ????????????asyncOnSuccess(element,?data,?xhr.getResponseHeader("Content-Type" )?||?"text/html" );?? ????????????getFunction(element.getAttribute("data-ajax-success" ),?["data" ,?"status" ,?"xhr" ]).apply(this ,?arguments);?? ????????},?? ????????error:?getFunction(element.getAttribute("data-ajax-failure" ),?["xhr" ,?"status" ,?"error" ])?? ????});?? ?? ????options.data.push({?name:?"X-Requested-With" ,?value:?"XMLHttpRequest" ?});?? ?? ????method?=?options.type.toUpperCase();?? ????if ?(!isMethodProxySafe(method))?{?? ????????options.type?=?"POST" ;?? ????????options.data.push({?name:?"X-HTTP-Method-Override" ,?value:?method?});?? ????}?? ?? ????$.ajax(options);?? }??
沒錯,這兩段就完成ASP.NET MVC 的Ajax請求。而從這兩段代碼中你也一定大致看出了前面的OnBegin、OnComplete、OnFailure以及OnSuccess四個屬性對應的Ajax請求過程JavaScript回調對應的是Ajax請求的哪個部分了:
這里有一個關鍵的函數地方‘getFunction’,它定義在jquery.unobtrusive-ajax.js文件的最前面:
[javascript] view plaincopy
function ?getFunction(code,?argNames)?{??????var ?fn?=?window,?parts?=?(code?||?"" ).split("." );?? ????while ?(fn?&&?parts.length)?{?? ????????fn?=?fn[parts.shift()];?? ????}?? ????if ?(typeof ?(fn)?===?"function" )?{?? ????????return ?fn;?? ????}?? ????argNames.push(code);?? ????return ?Function.constructor.apply(null ,?argNames);?? }??
這個函數的功能就是從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
$("a[data-ajax=true]" ).live("click" ,?function ?(evt)?{?? ????evt.preventDefault();?? ????asyncRequest(this ,?{?? ????????url:?this .href,?? ????????type:?"GET" ,?? ????????data:?[]?? ????});?? });??
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
mode?=?(element.getAttribute("data-ajax-mode" )?||?"" ).toUpperCase();?? $(element.getAttribute("data-ajax-update" )).each(function ?(i,?update)?{?? ????var ?top;?? ?? ????switch ?(mode)?{?? ????case ?"BEFORE" :?? ????????top?=?update.firstChild;?? ????????$("<div?/>" ).html(data).contents().each(function ?()?{?? ????????????update.insertBefore(this ,?top);?? ????????});?? ????????break ;?? ????case ?"AFTER" :?? ????????$("<div?/>" ).html(data).contents().each(function ?()?{?? ????????????update.appendChild(this );?? ????????});?? ????????break ;?? ????default :?? ????????$(update).html(data);?? ????????break ;?? ????}?? });?? 核心是前兩行代碼: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介绍 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。