AJAX! Not Just AJAX

每個人都在談論 Web 2.0,以技術上來說最有名的莫過於 AJAX,當每個人看到了不換頁的網頁,往往都會會情不自禁地說「哇!好炫的 AJAX!」 但實際上不換頁的技術不是只有 AJAX,甚至很多時候我們會考量其他因素而棄 AJAX 不用、改採其他技術。這次的 Tech Talk 就是為了讓大家對不換頁的技術有更深一層的認識、以應用在相關的工作上。

Joseph Jiang,
josephj@yahoo-inc.com

Yahoo! Connection Manager

自從有了 Yahoo! Connection Manager 之後,想使用 AJAX 變得非常簡單,只需要三個步驟即可:

<script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0.js"></script> <script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/connection_2.0.0.js"></script> 首先如上所示,我們先把所需要的兩個 Library 給讀取進來。一個是負責讓物件有唯一命名空間的 Library、一個是 Connection 的 Library。

YAHOO.util.Connect.asyncRequest('GET', 'service/reset/?method=bbs.getlist', oCallback); 再來只需要使用 YAHOO.util.Connect 的 asyncRequest() 方法,依序帶入GET / POSTService URLRequest 完畢後的指定處理物件即可。其中 Service URL 只要是會以文字形式傳回即可,包括 *.css, *.js, *.txt, *.html, *.xml 皆可,大多數人會使用 PHP 或 ASP 等後端技術、讓 Service 可因參數的帶入有不同的回應值。

var oCallback = { success:function(e){ alert(e.responseText); }, failure:function(e){ alert(e.errorText); } }; 另外就是撰寫 Request 完畢後的指定處理物件、可以看到 Callback 物件內有兩個子物件,這兩個子物件都是 function,如同字義、分別負責處理成功及失敗的錯誤訊息。這個 Callback 物件要放在 asyncRequest 方法之前,否則會因造成找不到此物件而失敗。


範例

Proxy

雖然 AJAX 用起來很方便,但很可惜它的致命缺點就是無法跨網域。不要懷疑、連從 tw.yahoo.com 用 AJAX 存取 tw.news.yahoo.com 都會收到 Access Deny 的訊息… 但這樣子豈不是太可惜了嗎?如果其他網域有提供一個 Service URL、恰巧是我很需要的資料,難道我就沒辦法用 AJAX 存取了嗎?
當然不是這個樣子的,我們只需要在本地端寫幾行短短的後端程式來幫我們當轉接站即可,也就是我們這邊所謂的 Proxy。( Proxy HOWTO )

<?php $remoteUrl = ($_GET['url'])?$_GET['url']:''; $remoteFile = fopen($remoteUrl, 'r'); while($sOutput = fread($remoteFile, 1024)) { echo $sOutput; } fclose ($remoteFile); ?> 這隻程式叫 proxy.php,簡單說來就是用 fopen 抓取遠端的資料而已,有在 Parse 網頁的人一定都會這個技術。

<script type="text/javascript"> var sServiceUrl = 'http://www.google.com'; YAHOO.util.Connect.asyncRequest('GET', 'proxy.php?url=' + encodeURIComponent(sServiceUrl), oCallback); </script> 改寫 Javascript 也不難,假設我們想抓 Google 首頁的原始碼,就把 Service URL 改為 'proxy.php?url=' + encodeURIComponent(sServiceUrl) 即可。

<?php define ('HOSTNAME', 'http://www.google.com/'); $remotePath = ($_POST['path']) ? $_POST['path'] : $_GET['path']; $remoteUrl = HOSTNAME.$remotePath; $session = curl_init($remoteUrl); if ($_POST['path']) { $postvars = ''; while ($element = current($_POST)) { $postvars .= key($_POST).'='.$element.'&'; next($_POST); } curl_setopt ($session, CURLOPT_POST, true); curl_setopt ($session, CURLOPT_POSTFIELDS, $postvars); } curl_setopt($session, CURLOPT_HEADER, false); curl_setopt($session, CURLOPT_RETURNTRANSFER, true); $xml = curl_exec($session); header("Content-Type: text/xml"); echo $xml; curl_close($session); ?> 剛剛那個 proxy.php 實在是很方便,而且不管遠端網站是那個網域都可以用。但是存在兩個問題,第一個是它只支援 GET,POST 就不行用了、另外一個是「網路釣魚」實在太多了,公開一個開放所有網域的 proxy.php 就等於提供一個跳板給人家使用,實在太危險。為了解決這兩個問題,可以用上面這一隻程式。

範例

Useful Tools

Connection Watcher

在我們把 AJAX 寫的複雜一點時,會衍生出一些問題,我們可能會連續送出好幾個 Asynchrous Request,當然這些請求有可能會失敗,或者是其實你寫錯了!根本沒有送出,所以我們需要一個監控的工具。我們的美國同事 Hedger, 在他自己的網站 Hedgerwow 上有一個 Connection Watcher 的工具,專門幫你監視 AJAX 的送出、成功、與失敗。

javascript:void((function(){ if(document.getElementById('yui-connection-watcher-bookmark')){return;}; var js = document.createElement('script'); js.setAttribute('src','http://www.hedgerwow.com/360/dhtml/yui-connection-watcher/' + 'watcher-bookmark.js'); js.setAttribute('id','yui-connection-watcher-bookmark'); document.getElementsByTagName('head')[0].appendChild(js); })()) 請把上面這段加到當成 Bookmark 連結的內容,只要你到任何有用 Connection Manager 做 AJAX 的頁面,按一下這個 Bookmark、他就會幫你即時監控!Cool!

xmldom.js

當我們去抓遠端資料的時候,除了一般的網頁 HTML,最常見的就是 XML 的資料格式了(如 RSS 或 Web Service 的傳回值),但是若想直接用 Javascript 來處理 XML,會因為瀏覽器的不同而變得十分棘手,所以我們需要一個好的 XML Parser 來協助我們處理。我現在都是用 xmldom.js 來做 XML 的解析。(這邊還是要做個提醒,Client 端 DOM 的 Parsing 是很消耗瀏覽器資源的,最好的方式還是在 Server 端抓取資料回來時就先處理成為 HTML 或 JSON 格式)

<script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0.js"></script> <script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/connection_2.0.0.js"></script> <script type="text/javascript" src="http://tw.yimg.com/i/tw/hp/wd/xmldom.js" ></script> <script> var oCallback = { success:function(e){ var oXml = new XMLDoc(e.responseText,function(e){alert(e)}); var dUser = oXml.docNode.getElements('user')[0]; var sBuffer = [ 'id = ' + dUser.getAttribute('id'), 'nsid = ' + dUser.getAttribute('nsid'), 'username = ' + dUser.getElements('username')[0].getText() ]; alert(sBuffer.join('\n')); }, failure:function(e){ alert(e.errorText); } }; var sServiceUrl = 'http://www.flickr.com/services/rest/?method=flickr.people.findByUsername&username=josephj6802&api_key=61b01dbad10133f42f0ebbababd7f01b'; YAHOO.util.Connect.asyncRequest('GET', 'proxy.php?url=' + encodeURIComponent(sServiceUrl), oCallback); </script> 上述的例子是去用 AJAX 對 Flickr 的 API ( flickr.people.findByUsername ) 做 Request,Flickr 那邊傳回的資料是 XML 的格式,我們把 xmldom.js 加到我們的頁面上,我們就可以利用 getElements(), getAttribute(), getText() 等方法輕鬆 Parse XML 了!

範例

AJAX Examples

  • AJAX 討論區
    第一個比較像樣的 AJAX 應用,不過也沒有全部做完 :p
  • AJAX Datagrid
    我們常用 Datagrid 這樣的概念來做網站後端的管理,像是 Daily Report 也是一例,網頁總是為了一點點資料的更新、不斷地整頁讀取。讓我們用 AJAX 來改進它的效能吧!
  • 以地址查詢 UrMap API
    UrMap 是我覺得台灣唯一有 Open API 觀念的本土廠商,但是不知道為什麼要在小地方鬧彆扭:不開放以地址直接查詢地圖,只給用經緯度查 :p 所以我只好自立自強,去 parse 其他網站讓地址可以轉經緯度。(有興趣可以看看這兩個網站:地址轉二分帶座標二分帶座標轉經緯度
  • 以地址查詢 UrMap API 的延伸:UrMap 結合網站資料庫
    不應用一下就可惜了,結合我晃頭晃鳥的資料庫看看吧!
  • 知識+ 用到 AJAX 的區塊:發表問題問題預覽 等...
    知識+ 改版用到許多不換頁的技術,但以 AJAX 專門來說,其實並不是很多、主要是考慮到上一頁下一頁的問題。
  • Yahoo! TW 新首頁
    PA 模組及 Tab 切換都有用到 AJAX。PA 部分是希望使用者沒有用到時就不會去 Request 到後端。 Tab 部分是因為 Product Central 希望首頁初始時的檔案大小能夠小些、讓使用者快一點看到。
  • Yahoo! Auto 的選擇車款
    在 YUI Connection Manager 還沒有時就寫的,以往的汽車頻道是選一個就會切換一次頁面的(謝謝 Midoli 的提供)
  • 新聞的投票
    使用者投票送出後會同步 Insert 到後端資料庫,並抓回最新的投票情況。(謝謝 Midoli 的提供)

Introduction to External Javascript

<script type="text/javascript" src="http://www.example.com/example.js"></script> 這個技術也有人稱為 Remote Scripting,看起來真的再平常也不過了,就只是把本地端或遠端 Javascript 用外部檔的方式放在原始碼中。 其實這類技術在網頁上應用得十分廣泛,如小小的計數器、或 Yahoo! Search Marketing 的 Linkspot 都是利用這樣的技術做資料的交換。

<script type="text/javascript" src="http://del.icio.us/feeds/json/josephj?count=20" id="feed"></script> 但是可沒有人規定 src 的值一定要是 *.js 的檔案喔,只要回傳的格式是符合 Javascript 結構的便可以。因此簡單來說,我們可以像上面一樣利用 GET 的方式傳遞參數、到一隻 CGI, PHP, ASP 中,這些後端程式會依照參數的不同當輸出對應的資料。而資料格式當然要 Javascript 認得才行、通常以陣列最多。
接著我們就可以利用這些傳回的陣列,將頁面上現有的資料做改變。

<script type="text/javascript" src="http://del.icio.us/feeds/json/josephj?count=20"></script> <script type="text/javascript">document.write('共取得 ' + Delicious.posts.length + ' 筆 del.icio.us 資料<br/>');</script> <script type="text/javascript" src="http://del.icio.us/feeds/json/josephj?count=30"></script> <script type="text/javascript">document.write('共取得 ' + Delicious.posts.length + ' 筆 del.icio.us 資料<br/>');</script> <script type="text/javascript" src="http://del.icio.us/feeds/json/josephj?count=40"></script> <script type="text/javascript">document.write('共取得 ' + Delicious.posts.length + ' 筆 del.icio.us 資料<br/>');</script> 我們可以去看 http://del.icio.us/feeds/json/josephj?count=20 所輸出的字串,會發現他裡面會宣告一個 Delicious 物件、底下有一個 posts 的子物件,所以如果我們執行上面的原始碼,順利的話在網頁上會秀出取得 20、30、40 筆資料的訊息,但是有時候並不一定會成功,因為我們所抓取得是遠端的資料,頁面執行是由上而下,當你去執行 alert 時,你的外部 Javascript 檔可能還沒讀到、或正在讀取,如此一來就會造成資料不正確或程式發生錯誤,所以我們必須要使用 setTimeout 的方式、或者依瀏覽器的不同使用 onreadystatechange ( For IE ) 及 onload ( For FF ) 這兩個事件處理程序、但還是有瀏覽器需要多加考慮:Safari 對於兩種事件處理程序都不支援、必須要利用 AJAX 的方式,把原始碼貼到頁面上。

getLibrary.js

我們必須利用一些現有的函式庫讓我們的工作馭繁為簡!Again!Hedgerwow 網站上有一個 Install Libraries on Demand,正好可以協助我們來解決這個問題。

<script type="text/javascript" src="http://tw.yimg.com/i/tw/hp/wd/getlibrary.js"></script> 讓我們先把這個外部的函式庫給加入。

<script type="text/javascript"> Library.loadJs(fCallback,'example.js','example2.js','example3.js'); </script> 利用 Library.loadJS 這個方法,第一個參數是載入完畢後所會執行的 Function,後面的都是欲載入的 Javascript Url 路徑。

<script type="text/javascript" src="http://tw.yimg.com/i/tw/hp/wd/getlibrary.js"></script> <script type="text/javascript"> var fCallback = function() { alert(ybidDandyData); } Library.loadJS(fCallback,'http://tw.yimg.com/i/tw/music/web/data2.js'); </script> 跟 AJAX 的 oCallback 類似,我們只要把後續的處理寫在 fCallback 裡即可。另外,你必須放一隻 proxy.php 在目前的目錄下,給 Safari 專門使用(會把檔案的內容抓出來,直接塞到 <script></script> 標籤中。)。

About JSON

前幾個月在 dot com 那邊有一股 JSON 的旋風,緣由可以看這一篇 Web Services + JSON = Dump Your Proxy。大意主要是說 AJAX 雖然好用、可以存取各種格式,但如果要做跨網域的存取時就必須做一個 proxy.php,若討論到安全性會更麻煩、而且有些人的 Server 上根本不允許使用 Server-Side Language, 這樣一來,我們在 developer.yahoo.net 所提供許多好用的 api (原來是 xml only) 就沒辦法被許多人所應用了... 因此,有人大力地將所有 api 服務都提供轉換為 JSON 的格式,配合我們在這邊所講的 Remote Scripting,我們就可以把 Connection Manager 給丟掉、把 Proxy 丟掉,用最 lightweighted 的方式來存取 API

http://api.local.yahoo.com/MapsService/V1/geocode?appid=dantheurer&location=la&output=json&callback=getLocation 重點就是最後兩個參數,只要指定 output=json,所輸出的格式就不再是 xml、而是 JSON。但是別忘記,我們必須讓這段 JSON 存在一個變數中,你可以利用 callback 這個參數定義你的變數名稱。 我們就以 XML + AJAX 的方式與 JSON + Remote Scripting 的方式比較看看哪種好寫

用 AJAX + XML 存取 | 用 Remote Scripting + JSON 存取

Remote Scripting Examples

  • 時尚精品品牌搜尋
    在 Yahoo! 拍賣上的一個模組,實際的資料檔存放在 yimg 上的一個 js 檔,讓 Producer 可以隨時修改(資料檔即為 JSON)、也不會吃到拍賣系統本身的資源。
  • YSM Hybird
    這是 Linkspot 加上 ECM 的整合,所有資料(Keyword & Listing)都是使用 External Js 的方式即時去取得的 ( Source Code )

Introduction to Iframe

上面的技術都很炫,但是不知道大家有沒有發現一個問題,當 User 利用 AJAX 或 Remote Scripting 做換頁時,他們很有可能會按「上一頁」,這時他們所得到的結果可能是一種驚訝與不悅,因為這並不是他們所期待的頁面。

<div id="show" style="border:solid 1px #000;padding:10px;">待會兒資料會讀進此 DIV 喔!</div> <a href="page1.html" target="ioFrame">第 1 頁</a> | <a href="page2.html" target="ioFrame">第 2 頁</a> | <a href="page3.html" target="ioFrame">第 3 頁</a> | <a href="page4.html" target="ioFrame">第 4 頁</a> <iframe name="ioFrame" style="display:none"></iframe> 為了避免這樣的情況,很多時候我們會在頁面上放一個隱藏的 Iframe、把換頁的連結 target 指向到這個 Iframe 去

<html> <head> <script type="text/javascript"> window.onload = function() { top.getElementsById('show').innerHTML = document.body.innerHTML; }; </script> </head> <body> 目前在第 N 頁 </body> </html> 當 Iframe 的頁面讀取完畢時,就把此頁的資料丟到母視窗的 Div 標籤中。如原始碼所示,這個 id 為 show 的區塊就會依讀取頁面的不同而顯示不同的資料。而且當我們按上一頁跟下一頁時可以符合 User 的期待。

Connection Manager Upload

YUI 在最近新增了一個上傳 (AJAX 本身沒有辦法做上傳) 的功能,雖然放在 Connection Manager 裡,但實際上是利用 Iframe 的技巧,我們也順便在這邊介紹一下

<script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0.js"></script> <script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/connection_2.0.0.js"></script> <script type="text/javascript"> var oCallback = { // 沒有 success 與 failure 了,在 upload 時只有一個 upload 的子 function upload:function(e){ alert(e.responseText); } }; //傳入 form 物件、第二個參數代表上傳與否 YAHOO.util.Connect.setForm(document.forms[0],true); YAHOO.util.Connect('POST','upload.php',oCallback); </script> <form> <label>File : </label> <input type="file" name="file"/> <input type="submit" value="submit"/> </form> 如此一來你就可以做到不換頁上傳檔案、並取得伺服器端所輸出的資訊,找個時間用這樣的方法把 Image Uploader 改一下吧。

Web 2.0 Batch Uploader | Upload Example (IE Only)

Iframe Examples

  • 知識+ My
    知識+ 改版用了許多不換頁的技巧,但是希望仍能保有上一頁、下一頁的彈性,所以主要是用 Iframe 這種技巧來做
  • 知識+ 問題頁
    知識+ 改版用了許多不換頁的技巧,但是希望仍能保有上一頁、下一頁的彈性,所以主要是用 Iframe 這種技巧來做

Image Replacement

<!-- 欲送出的變數值 --> 請輸入使用者帳號:<input id="txt" type="text" value="JosephJ"/> <!-- 回傳的訊息會顯示在此 --> <p id="msg">在您輸入的過程就會幫您檢查此帳號是否存在唷...</p> <!-- 被用來發出 Request 的圖片 --> <img id="img" alt="hidden image"/> 圖片<img/>本身就具有可以利用 js 做動態更替載入的特性。在這邊我們不是要顯示圖片,而是利用這一個特性將我們帶有參數的網址送出、將 src 設為此帶有參數的網址,那麼自然就會對 Server 做出 GET 的 HTTP Request(想當然爾,圖片就是破圖)。 至於資料的傳回,我們就不再利用圖片了!而是在負責的 Server 端網頁(如 PHP 或 ASP),將所需的資料寫入至 Cookie 中。 最後,我們再利用 Javascript 來取得這更新的 Cookies 顯示在網頁上。如此一來,整個網頁不需要重新整理就可以抓到資料了!

範例:增測使用者帳號是否存在 ( Server 端程式碼 ) | 範例改進:每打一個字就做一次增測

圖片替換的缺點:Cookies 不能用的時候就失效了、只能傳送少量的資訊(Cookies 最多只有 4KB 的容量)、無法存取遠端資訊、若未來對 img 做出安全性限制(僅可使用圖片格式),屆時就無法利用此方法取得資訊。

204 Piggyback

其實這是使用 HTTP 的狀態 (如 5XX 是伺服器錯誤)。利用這種技巧,連一個假的圖片都省掉了,除了 Js 的處理外,你可以用一般的方式連結、或送出表單到某一特定的網頁(Server-side Language)。這個特定的網頁有一個很重要的設定:就是必須將網頁的 Header 設為 HTTP/1.1 204 No Content(可參考:Status Code Definitions

<?php header('HTTP/1.1 204 No Content'); ?> 上面是 PHP 的設定法

<% Response.Status = "204 No Content" %> 上面是 ASP 的設定法

204 No Content 代表了網頁收到了 Request,但是不做任何的回應。所以當這樣設定了之後,瀏覽器的確會有送出的動作,但是你可以發現所在的網頁將是會靜止不動的。這樣一來,我們「製造 Request 但不重新整理網頁」的目標就達成了。這個目標的網頁,如上面的圖片替代方法,一樣會把所需的回應寫到 Cookies 中。開發者再一次,又可以在 Client 端利用 Js 取得由 Server 端所變動的 Cookies

範例:以使用者帳號取得其資訊 | Server 端程式碼 | Web 2.0 Batch File Uploader | 知識+ 編輯我的知識檔案

window.open()

在跳出視窗尚不會被攔截的時代,常有許多網站會利用這樣的方式來做不換頁的更新。而現在主流的瀏覽器 IE 、Firefox 預設已經會攔截跳出視窗,用這種方式來更新已經少之又少了。

我們可以利用跳出的視窗去取得伺服器端的資料,再把取得的資料寫回原本的視窗。這是一種蠻直覺的做法,剛剛提到「封鎖快顯視窗」的緣故,若這個跳出視窗的動作不是由使用者所驅動,那麼十之八九會被擋掉,功能就會因此而失效了。
輸入帳號取得使用者的 Email (ASP Code) | 自動取得伺服器上的時間(被視窗封鎖的失敗範例)

window.showModalDialog()

由於視窗內建的 window.alert(), window.prompt(), winfow.confirm() 外觀都是一成不變的,基於這個問題,微軟就發展出了 window.showModalDialog。showModalDialog 的中文名稱就是「對話視窗」,跟 alert(), prompt(), confirm(), 一樣,使用者必須關閉此視窗、或按下某個 button 才可以繼續下一步,也可以稱為「同步化作業」(必須等待處理完畢、不能在等待的時間作其他事情)。這是一個蠻好使用的方式,但很可惜的是 FireFox 完全不支援、而且在 IE 如果不是使用者所驅動、也一樣會有封鎖視窗的問題在。

什麼是 showModalDialog | 查詢使用者的 Email (ASP Code)

YUI Container Widget Family

在處理 AJAX 這類的不刷新網頁,因為這些命令都是非同步的,我們往往需要對畫面做一些處理,讓使用者知道我們的動作正在進行中。 像是 Activity Indicator 、把整頁刷淡、讓使用者無法操作等效果。

但說真的,這樣的效果要自己去做其實要處理的事情還不少,像是每次視窗的縮放我們必須動態地去偵測大小,讓 Activity Indicatior 放在正中央的位置、或者是針對刷淡的 Layer 處理大小,以免大小超過時導致 scrollbar 的出現。

感謝 Yahoo User Interface Library,最近推出了一個 Container 的 widget,可以讓我們事半功倍啊!!

<script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/yahoo_2.0.0.js"></script> <script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/event_2.0.0.js" ></script> <script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/dom_2.0.0.js" ></script> <script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/dragdrop_2.0.0.js" ></script> <script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/utils/2/animation_2.0.0.js" ></script> <script type="text/javascript" src="http://us.js2.yimg.com/us.js.yimg.com/lib/common/widgets/2/container/container_2.0.1.js"></script> 要用 Container 家族的完整功能,得要加入 6 個 Javascript Library,實在是很肥,有時候會覺得 YUI 是個胖子 :(

<link type="text/css" href="http://us.js2.yimg.com/us.js.yimg.com/lib/common/widgets/2/container/css/container_2.0.1.css" rel="stylesheet" media="screen" /> 另外你也可以加上預設樣式,連樣式也不用自己花心思了

Panel Widget

Panel Widget 主要用來顯示資訊用,與使用者沒有任何的直接互動(但是你還是可以設定關閉、拖拉等行為)。這樣的東西很適合我們用來顯示剛剛所提到的 Activity Indicator, 只要把 modal 設為 true,使用者也沒辦法對後面的頁面進行互動。

var oWaitPanel = new YAHOO.widget.Panel("wait",{ width:"240px", fixedcenter:true, underlay:"shadow", close:false, visible:true, draggable:false, modal:true, effect:{effect:YAHOO.widget.ContainerEffect.FADE, duration:0.5} }); window.onload = function() { oWaitPanel.show(); setTimeout(function(){oWaitPanel.hide();},1500); }; 新增 Panel 的 Instance 後,需要給定一個 id,若不存在頁面上時,則會自動建立。其餘的就是屬性的設定,依序是寬度、是否要鎖定在正中央
Panel 的範例

Dialog Widget

剛剛我們有提到 IE Only 的 showModalDialog(),有時我們很需要像這樣的東西讓我們做一個獨立的 AJAX 新增、修改介面。現在有了 Dialog Widget 之後,不用再擔心瀏覽器支援性的問題、也不用另外做一個頁面。

<div id="myDialog"> <div class="hd">Please enter your information</div> <div class="bd"> <form name="dlgForm" method="POST" action="php/post.php"> <p>Please enter your personal contact information:</p> <label for="firstname">First Name:</label><input type="textbox" name="firstname" /> <label for="lastname">Last Name:</label><input type="textbox" name="lastname" /> </form> </div> </div> 我們有一個表單是長這樣子的,外層的 div id 叫做 myDialog
Dialog 的範例

<script type="text/javascript"> var myDialog; var onInit = function(e) { myDialog = new YAHOO.widget.Dialog('myDialog',{ postmethod:'form', buttons:[ { text:'Submit', handler:function(){myDialog.submit();}, isDefault:true }, { text:'Cancel', handler:function(){myDialog.hide();} } ] }); myDialog.render(); myDialog.hide(); } var onEditClick = function(e) { myDialog.show(); YAHOO.util.Event.stopEvent(e); return false; } YAHOO.util.Event.addListener(window,'load',onInit); YAHOO.util.Event.addListener('edit','click',onEditClick); </script> 我只設了兩個屬性,一個是 postmethod、一個是 buttons。postmethod 有 "async"(AJAX), "form"(傳統FORM送出), 及 "none"(不送出) 三種屬性可以設定。 另外 button 則是你希望這個 Dialog 有哪些按鈕可以用、完全隨您自訂。

Simple Dialog

有時我們只是希望顯示一個簡短的訊息,如「您確定嗎?」、使用者只選擇是跟否,雖然我們還是可以用 Dialog 來做、但其實有個更簡單的東西叫 Simple Dialog

<script> var onDelClick = function(e) { var mySimpleDialog = new YAHOO.widget.SimpleDialog('dlg', { modal:true, draggable:false, width: '20em', icon:YAHOO.widget.SimpleDialog.ICON_WARN, buttons:[ { text:'Submit', handler:function(){ e.target.parentNode.parentNode.parentNode.removeChild(e.target.parentNode.parentNode); mySimpleDialog.hide(); mySimpleDialog = null; }, isDefault:true }, { text:'Cancel', handler:function(){mySimpleDialog.hide();} } ], effect:{ effect:YAHOO.widget.ContainerEffect.FADE, duration:0.25 }, fixedcenter:true }); mySimpleDialog.setHeader("Warning!"); mySimpleDialog.setBody("您確定要刪除這一列嗎?"); mySimpleDialog.render(document.body); mySimpleDialog.show(); } YAHOO.util.Event.addListener('del','click',onDelClick); </script> <table border="1"> <tr> <td><a href="#" id="del">刪除</a></td><td>Awoo~ Awoo~ Awoo~</td><td>Awoo~ Awoo~ Awoo~</td> </tr> </table> SimpleDialog 的範例