javascript創建可維護幻燈片效果。
幻燈片效果
顯然,效果很實用。對于這個效果,我們并不解釋如何去使用效果庫,而是講解如何創建類似的效果,并保持他的可用性,分離式(unobtrusive),可維護性(讓未來的維護者,在不需要修改你的腳本的情況下,修改圖片,外觀或文本標簽)。
無 javascript 狀態下,用戶將看到下面的效果:
添加和移除圖片、改變圖片的順序以及添加標題,這些在 html 中都很容易做到。并且最后的解決并不意味著維護者需要懂任何 javascript 或者在源碼中搜索在哪里修改 css 的 class, id 或者文本標簽。
你有沒有準備好花費一些時間去一步一步的嘗試創建這個效果?
第一步:分析問題(analizing the problem)
創建一個好的腳本,第一步應該是去分析哪些是你要完成的:我們想要創建一個照片的幻燈片效果,并且我們想要保持維護的方便。
如何創建一個幻燈片效果
在一個網站上擁有幻燈片有幾種方法:
在我們的案例中,我們采取下面的圖片列表,用向前和向后的按鈕把他變成一個幻燈片效果,并且一個指示器告訴我們,照片總數中的哪張照片是當前顯示的。
<ul id="slideshow">
<li><img src="img/flat1.jpg" alt="hallway" /></li>
<li><img src="img/flat2.jpg" alt="hob" /></li>
<li><img src="img/flat3.jpg" alt="bathroom" /></li>
<li><img src="img/flat4.jpg" alt="living room" /></li>
<li><img src="img/flat5.jpg" alt="bedroom" /></li>
</ul>
最后的輸出會看起來像例子中的幻燈片效果。
依賴關系檢查
我們這里有一些元素依賴于 javascript 生成:文字指示器和向前和向下的鏈接。為了保持我們解決方法的可用性,我們需要確保一些事情:
|||
第二步:規劃腳本(planning the script)
一旦你已經評估了問題,并挑選出你想使用的解決方法,你便可以開始規劃腳本。本質上,我們的腳本應該做這些:
不同的功能處理
我們有一些方法處理這個問題。其中之一是使用 dom 遍歷每個 li 條目并隱藏他。在這個事件監聽函數,我們先隱藏先前顯示的 li (如果有的話),并顯示當前的這個。
注:顯示和隱藏代替圖片的 li 更有意義,因為他允許維護者在每個幻燈片上添加其他的元素,比如,一些標題。
這個方法的問題在于,我們在 javascript 中做必要的樣式改變,這意味著如果有需要比剛才我們腳本中改變 display 從 block 到 none 更復雜的樣式改變,將使腳本變得更雜亂(沒有從行為中分離表現)。
樣式留給 css 解析器
更簡潔的方法是將所有的外觀改變(在所有列表項下載完之后隱藏某些)都留給瀏覽器的 css 解析器。在我們的例子中,我們可以在幻燈片中使用一個 css 規則很容易地隱藏所有的列表項,并用一個特定的 class 重寫當前條目的樣式。
html:
<ul id="slideshow">
<li><img src="img/flat1.jpg" alt="hallway" /></li>
<li><img src="img/flat2.jpg" alt="hob" /></li>
<li><img src="img/flat3.jpg" alt="bathroom" /></li>
<li><img src="img/flat4.jpg" alt="living room" /></li>
<li><img src="img/flat5.jpg" alt="bedroom" /></li>
</ul>
css:
#slideshow li{
display:none;
}
#slideshow li.current{
display:block;
}
唯一的問題是,如果我們使 css 和 javascript 不可用,訪客將永遠不能訪問到其他圖片。因此,我們需要僅當 javascript 可用時,應用這些樣式。技巧是,當 javascript 可用,在幻燈片的 ul 上應用 class ,例如名為 js 。這允許我們僅當 javascript 可用時,顯示效果,通過在 css 中簡單的修改:
css:
#slideshow.js li{
display:none;
}
#slideshow.js li.current{
display:block;
}
這個 class 的鉤子(hook)也能被用來對幻燈片的靜態和動態版本提供一個完全不同的外觀。
我們所有的腳本需要做的是,通過移除或添加 current 的 class 來顯示和隱藏當前及以前的照片。
為了確保我們的腳本將不會影響同一頁面上的其他腳本,我們將創建一個主要的對象,并在其上構造所有的方法和屬性。這可以確保我們的 init() 函數將不會被覆蓋或覆蓋其他任何相同名字的函數。
javascript::
slideshow = {
current:0, // 當前幻燈片編碼
init:function(){
// 初始化和設置事件處理函數
},
show:function(e){
// 事件監聽器
}
}
|||
第三步、基本的工具方法( essential tools)
現在,我們有了規劃和建立我們腳本的框架。是時候思考我們需要完成這個功能的一些工具方法。在其最低要求的情況下,dom 腳本的幫助庫應該包括:
我們添加這些工具方法到主要的對象上,并準備開始:
javascript:
slideshow = {
current:0, // 當前幻燈片編碼
init:function(){
// 初始化和設置事件處理函數
},
show:function(e){
// 事件監聽器
},
addevent:function( obj, type, fn ) {
if ( obj.attachevent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){
obj['e'+type+fn]( window.event );
}
obj.attachevent(’on’+type, obj[type+fn] );
} else
obj.addeventlistener( type, fn, false );
},
removeclass:function(o,c){
var rep=o.classname.match(’ ‘+c)?’ ‘+c:c;
o.classname=o.classname.replace(rep,”);
},
addclass:function(o,c){
var test = new regexp(”(^|//s)”+c+”(//s|$)”).test(o.classname);
if(!test){o.classname+=o.classname?’ ‘+c:c;}
},
cancelclick:function(e){
if (window.event){
window.event.cancelbubble = true;
window.event.returnvalue = false;
}
if (e && e.stoppropagation && e.preventdefault){
e.stoppropagation();
e.preventdefault();
}
}
}
當文檔完全載完,第一件事情就是需要執行 init() 方法:
javascript:
slideshow = {
current:0, // 當前幻燈片編碼
init:function(){
// 初始化和設置事件處理函數
},
show:function(e){
// 事件監聽器
},
addevent:function( obj, type, fn ) {
if ( obj.attachevent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){
obj['e'+type+fn]( window.event );
}
obj.attachevent(’on’+type, obj[type+fn] );
} else
obj.addeventlistener( type, fn, false );
},
removeclass:function(o,c){
var rep=o.classname.match(’ ‘+c)?’ ‘+c:c;
o.classname=o.classname.replace(rep,”);
},
addclass:function(o,c){
var test = new regexp(”(^|//s)”+c+”(//s|$)”).test(o.classname);
if(!test){o.classname+=o.classname?’ ‘+c:c;}
},
cancelclick:function(e){
if (window.event){
window.event.cancelbubble = true;
window.event.returnvalue = false;
}
if (e && e.stoppropagation && e.preventdefault){
e.stoppropagation();
e.preventdefault();
}
}
}
slideshow.addevent(window,’load’,slideshow.init);
|||
第四步:腳本(the script)
現在,在適當的位置我們有所有的方法工具,以及當窗口載完時被調用的 init() ,我們可以開始具體化此方法。
注:這僅是 init() 方法,而不是整個腳本。因為有行號,復制并粘貼腳本將會導致錯誤。
1: init:function(){
2: if(document.getelementbyid && document.createtextnode){
3: var list = document.getelementbyid(' ');
4: if(list){
5: slideshow.items = list.getelementsbytagname('li');
6: slideshow.all = slideshow.items.length;
7: if(slideshow.all > 1){
8: slideshow.addclass(list, 'js');
9: slideshow.createnav(list);
10: }
11: }
12: slideshow.show();
13: }
14: },
createnav() 方法使用 dom 腳本創建幻燈片正常工作所需的 html。
1: createnav:function(o){
2: var p = document.createelement('p');
3: slideshow.addclass(p, 'slidenav');
4: slideshow.prev = document.createelement('a');
5: slideshow.prev.setattribute('href', '#');
6: var templabel = document.createtextnode('<<');
7: slideshow.prev.appendchild(templabel);
8: slideshow.addevent(slideshow.prev, 'click', slideshow.show);
9: p.appendchild(slideshow.prev);
10: slideshow.count = document.createelement('span');
11: templabel = document.createtextnode( (slideshow.current+1) + ' / ' + slideshow.all);
12: slideshow.count.appendchild(templabel);
13: p.appendchild(slideshow.count);
14: slideshow.next = document.createelement('a');
15: slideshow.next.setattribute('href', '#');
16: var templabel = document.createtextnode('>>’);
17: slideshow.next.appendchild(templabel);
18: slideshow.addevent(slideshow.next, ‘click’, slideshow.show);
19: p.appendchild(slideshow.next);
20: o.parentnode.insertbefore(p, o);
21: },
這些被創建的所有標記都是必要的,最后剩下的是去定義一個當鏈接被點擊時調用的監聽方法 show() 。
1: show:function(e){
2: if(this === slideshow.next || this === slideshow.prev){
3: slideshow.removeclass(slideshow.items[slideshow.current], ‘current’);
4: var addto = (this === slideshow.next) ? 1 : -1;
5: slideshow.current = slideshow.current + addto;
6: if(slideshow.current < 0){
7: slideshow.current = (slideshow.all-1);
8: }
9: if(slideshow.current > slideshow.all-1){
10: slideshow.current = 0;
11: }
12: }
13: var templabel = document.createtextnode((slideshow.current+1) + ‘ / ‘ + slideshow.all);
14: slideshow.count.replacechild(templabel, slideshow.count.firstchild);
15: slideshow.addclass(slideshow.items[slideshow.current], ‘current’);
16: slideshow.cancelclick(e);
17: },
這些是腳本的所有內容?,F在這個腳本可以工作,但仍不是真正可維護的。
|||
第五步:輕松維護(easing maintenance)
腳本功能齊全,分離式而且無懈可擊。真正的問題是,現在并不方便維護。
腳本應用的最大的問題大概是,并不是所有的維護者都懂 javascript 和愿意在你的腳本中尋找需要修改的部分。
為了避免維護者做這些,最安全的方法就是把腳本和 css 中使用的命名和 id 從你的腳本功能中分離出來。此外,從使用的腳本中分離出文本標簽也是個好點子,因為他們可能會改變。例如,當腳本使用其他語言本地化時。
工具方法的復用
第一件要做的事情就是,從主要腳本中分離出其他腳本也可以再用的工具函數。這也許是大部分 javascript 庫的開始。
tools.js:
/* 輔助方法 */
tools = {
addevent:function( obj, type, fn ) {
if ( obj.attachevent ) {
obj['e'+type+fn] = fn;
obj[type+fn] = function(){
obj['e'+type+fn]( window.event );
}
obj.attachevent( ‘on’+type, obj[type+fn] );
} else
obj.addeventlistener( type, fn, false );
},
removeclass:function(o,c){
var rep=o.classname.match(’ ‘+c)?’ ‘+c:c;
o.classname=o.classname.replace(rep,”);
},
addclass:function(o,c){
var test = new regexp(”(^|//s)” + c + “(//s|$)”).test(o.classname);
if(!test){o.classname+=o.classname?’ ‘+c:c;}
},
cancelclick:function(e){
if (window.event){
window.event.cancelbubble = true;
window.event.returnvalue = false;
}
if (e && e.stoppropagation && e.preventdefault){
e.stoppropagation();
e.preventdefault();
}
}
}
css 的 class 和 id —— 外觀
下一步要做的是,分離外觀的 class 和 id 到一個單獨的包含文件。保證他們在 slideshow 命名空間里是安全的,因為其他腳本不太可能用到他們。也不會妨礙寫一個簡短的說明注釋。
slideshow-css.js:
slideshow.css = {
/*
這些都是幻燈片效果中使用到的 classe 和 id。
你可以在這里修改他們中的任何一個。
務必請使用引號包圍名稱,用逗號結尾(除了最后一個)。
*/
showid :'slideshow',
dynamicclass :'js',
slidenavigationclass :'slidenav',
currentclass :'current'
}
文本標簽(text labels)—— 解釋給終端用戶
最后但不是最不重要的,讓我們將文本標簽放到一個單獨的包含文件,再次使用 slideshow 命名空間。
slideshow-labels.js:
slideshow.labels = {
/*
這些都是幻燈片效果中使用到文本標簽。
你可以在這里修改他們中的任何一個。
務必請使用引號包圍名稱。
最后一個結尾不用逗號。
*/
previous : '<<',
next : '>>’,
counterdivider : ‘ of ‘
}
改變的主要腳本
然后,我們需要修改主要腳本使用此信息,而不是依賴嵌入式的數據。沒有太多的改變,很容易用搜索加替換就能做到。
slideshow.js:
slideshow = {
current:0,
init:function(){
if(document.getelementbyid && document.createtextnode){
var list =document.getelementbyid(slideshow.css.showid);
if(list){
slideshow.items = list.getelementsbytagname('li');
slideshow.all = slideshow.items.length;
if(slideshow.all > 1){
tools.addclass(list, slideshow.css.dynamicclass);
slideshow.createnav(list);
}
}
slideshow.show();
}
},
createnav:function(o){
var p = document.createelement('p');
tools.addclass(p, slideshow.css.slidenavigationclass);
slideshow.prev = document.createelement('a');
slideshow.prev.setattribute('href', '#');
var templabel = document.createtextnode(slideshow.labels.previous);
slideshow.prev.appendchild(templabel);
tools.addevent(slideshow.prev, 'click', slideshow.show);
p.appendchild(slideshow.prev);
slideshow.count = document.createelement('span');
templabel =document.createtextnode((slideshow.current+1) + slideshow.labels.counterdivider + slideshow.all);
slideshow.count.appendchild(templabel);
p.appendchild(slideshow.count);
slideshow.next = document.createelement('a');
slideshow.next.setattribute('href', '#');
var templabel = document.createtextnode(
slideshow.labels.next);
slideshow.next.appendchild(templabel);
tools.addevent(slideshow.next, 'click', slideshow.show);
p.appendchild(slideshow.next);
o.parentnode.insertbefore(p, o);
},
show:function(e){
if(this === slideshow.next || this === slideshow.prev){
tools.removeclass(slideshow.items[slideshow.current],
slideshow.css.currentclass);
var addto = this === slideshow.next ? 1 : -1;
slideshow.current = slideshow.current + addto;
if(slideshow.current < 0){
slideshow.current = (slideshow.all-1);
}
if(slideshow.current > slideshow.all-1){
slideshow.current = 0;
}
}
var templabel = document.createtextnode((slideshow.current+1) + slideshow.labels.counterdivider + slideshow.all);
slideshow.count.replacechild(templabel, slideshow.count.firstchild);
tools.addclass(slideshow.items[slideshow.current], slideshow.css.currentclass);
tools.cancelclick(e);
}
}
tools.addevent(window,’load’,slideshow.init);
這些所有文件是確保將來維護者不用麻煩你就可以使用你的腳本工作所需要的。文件名應該很明顯,是什么就是什么,并能隨著時間的推移,成為一個標準的腳本:
tools.js
slideshow.js
slideshow-labels.js
slideshow-css.js
slideshow.css
原作者:christian heilmann 譯者:懌飛
原文:《a detailed explanation how to create a maintainable dynamic slide show in javascript》
新聞熱點
疑難解答