2012年5月31日

Single Page Application 實作 - 使用hash change event


前言

在上一篇文章『監聽 hash change 事件方法』中簡單的提到我們該如何監聽網址的Hash值改變,在本文章中我們將會利用這項技術,實際完成Single Page Application(SPA)的基本架構,有著這樣的架構,我們就能夠使用ajax的方式更新畫面,而不需要將整個畫面重新載入。



這個系統會實作以下功能 : 

  • 使用hashchange plugin監聽Hash改變事件
  • 使用jQuery動態載入外部html
  • 使用jQuery.on動態綁定外部html

如果你還不了解SPA你可以參考『Single-page application

監聽Hash改變

首先我們先來完成監聽Hash事件的程式碼,如下
$(document).ready(function(){ $(window).hashchange( function(e){ var hash = window.location.hash; }); $(window).hashchange(); });

撰寫UI介面

介面部分預期提供三個按鈕,分別可以連結到三個不同的頁面,將外部頁面讀取到page-container容器中,page-container會自動切換該顯示哪一個page,其它以載入的頁面會被隱藏起來。如下程式碼
<body> <h1>Single Page Application Sample</h1> <ul> <li> <a class="nav" href="#main" data-role="url">Main</a> <a class="nav" href="#code" data-role="url">程式碼</a> <a class="nav" href="#about" data-role="url">About</a> </li> </ul> <!--存放頁面的容器--> <div id="page-container"> </div> </body>

Ajax載入外部頁面

我們已經可以監聽Hash改變事件以及PageContainer架構,下一步就把監聽到的Hash值對應到外部HTML檔案載入,在這裡我們使用jQuery來實作。jQuery提供load方法讓我們能夠載入外部的元素到容器內,範例如下
// param1 : extend html url // param2 : callback function $result = $page.load(id + '.html', function(){}); 
透過這個方法,我們將載入外部HTML放入PageContainer寫成一個函式
appendPage(pageid)
function appendPage(id){ var $container = $('#page-container'); var $innerpage = $container.find('#' + id); console.log($innerpage); if($innerpage.length == 0){ var $page = $('<div class="page"></div>').attr('id', id); $result = $page.load(id + '.html', function(){ $result.appendTo($container); $page.siblings('.page').hide(); $page.hide().fadeIn(500); }); } else{ $innerpage.siblings('.page').hide(); $innerpage.fadeIn(500); } }

修改監聽Hash function,加入appendPage

$(window).hashchange( function(e){ var hash = window.location.hash; var pageid = location.hash.substring(1); // 預設沒填寫載入首頁 if(!hash) appendPage("main"); else appendPage(pageid); }); $(window).hashchange();

完成了!!
打開網頁試試看,當我們按下超連結切換頁面,你會發現新的頁面會載入到page-container裡面,如果該頁面已經載入,就會直接切換進去。





到目前為止一切都完成了,頁面已經可以動態的載入近來,但是好像忘了甚麼??


好像叫....事事事『事件』!!沒錯,如果你有拿上述的程式碼擴充,你會發現外部載入的頁面就算我們事先在$.ready()內綁上事件處理但仍然無法觸發,原因很簡單因為使用ajax載入的畫面在$(document).ready()中並還沒載進來,所以我們必須做小小個修正。


每當有Page被外部讀進來時,就觸發pagecreate事件,當Page切換顯示時觸發pageshow事件;有了這個機制,我們可以在$(document).ready()中監聽這些事件,然後在pagecreate事件發生裡去綁上該頁面的元素事件,修改後程式碼如下 : 


appendPage修正
function appendPage(id){ var $container = $('#page-container'); var $innerpage = $container.find('#' + id); console.log($innerpage); if($innerpage.length == 0){ var $page = $('<div class="page"></div>').attr('id', id); // 可能會有一些動畫,所以pagecreate會比pageshow更快觸發 // pagecreate重頭到尾只有在page第一次被讀取進來時才會觸發 // pageshow每次切換頁面都會觸發 // 所以一些按鈕的事件初始化因該寫在pagecreate $result = $page.load(id + '.html', function(){ $result.appendTo($container); $page.trigger('pagecreate', [$page]); $page.siblings('.page').hide(); $page.hide().fadeIn(500, function(){ $page.trigger('pageshow', [$page]); }); }); } else{ $innerpage.siblings('.page').hide(); $innerpage.fadeIn(500, function(){ $innerpage.trigger('pageshow', [$innerpage]); }); } }
$(document).ready()修正
$(document).ready(function(){ $(window).hashchange( function(e){ var hash = window.location.hash; var pageid = location.hash.substring(1); if(!hash) appendPage("main"); else appendPage(pageid); }); $(window).hashchange(); $("#page-container").on('pageshow', '#main', function(event, $page){ showNoty("main頁面顯示", "success"); }); $("#page-container").on('pageshow', '#code', function(event, $page){ showNoty("code頁面顯示", "success"); }); $("#page-container").on('pageshow', '#about', function(event, $page){ showNoty("about頁面顯示", "success"); }); $("#page-container").on('pagecreate', '#main', function(event, $page){ $("#main-btn").click(function(){ showNoty("你點擊了Main頁面的按鈕!!", "error") }); showNoty("main頁面被創建", "error"); }); $("#page-container").on('pagecreate', '#code', function(event, $page){ $("#contact-btn").click(function(){ showNoty("你點擊了code頁面的按鈕!!", "error") }); showNoty("code頁面被創建", "error"); }); $("#page-container").on('pagecreate', '#about', function(event, $page){ $("#about-btn").click(function(){ showNoty("你點擊了About頁面的按鈕!!", "error") }); showNoty("about頁面被創建", "error"); }); $("a[data-role=url]").click(function(e){ e.preventDefault(); console.log(e); var hash = e.target.hash; window.location.hash = hash; }) });

範例程式下載
Dropbox載點 DEMO網址

結論

在本文章中我們透過實戰了解SPA的原理與實現方式,在HTML5中提供了一個更能夠控制頁面的技術『History API』,但基於流覽器相容性問題,目前該技術尚未成為主流,在下一篇文章中我們將介紹HistoryAPI的操作方式,並了解如何使用History.js(plugin)實現跨瀏覽器的HistoryAPI。


沒有留言:

ShareThis