第一步:分析問題(Analizing the problem)
創建一個好的腳本,第一步應該是去分析哪些是你要完成的:我們想要創建一個照片的幻燈片效果,并且我們想要保持維護的方便。
如何創建一個幻燈片效果
在一個網站上擁有幻燈片有幾種方法:
在文檔中包含所有的圖片。
當他運行在無 JavaScript 狀態,這是一個安全的選擇。而且,當頁面被載完,所有的圖片也會將被載完。然而,這個方式只適用于少量的圖片。
在文檔中包含第一張圖片,并且有一個創建幻燈片功能的服務器端腳本。
這也是相當安全的,但是對于終端用戶來說,這是非常令人厭煩的——因為我不想加載整個頁面,僅想得到下一張照片。但對頁面展示和廣告點擊比較有效,這也是為什么大量的新聞站點使用這個方法。
在文檔中包含第一張圖片,并按需加載其他圖片。
這個方法令你厭煩的是,必須依賴于 JavaScript ,并且要有一個維護照片列表的 JavaScript 數組。你還需提供一個加載指示器,用來顯示用戶一些正在發生的事情。
在我們的案例中,我們采取下面的圖片列表,用向前和向后的按鈕把他變成一個幻燈片效果,并且一個指示器告訴我們,照片總數中的哪張照片是當前顯示的。
<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 生成:文字指示器和向前和向下的鏈接。為了保持我們解決方法的可用性,我們需要確保一些事情:
僅當 JavaScript 可用(用戶信賴我們提供給他們使用的功能)時,這些元素應該出現。一個鏈接,不能做任何違反用戶對我們的信任的事情。
不論輸入設備(讓我們不要依賴用戶是否有鼠標),交互式元素都應該可用。
圖片不應該被隱藏,除非使用者能再次訪問他們。在技術上,僅顯示第一張圖片,且沒有向前和向后的鏈接是預留退路的做法,但是為什么要用戶已下載所有的圖片僅只看到第一張?
第二步:規劃腳本(Planning the Script)
一旦你已經評估了問題,并挑選出你想使用的解決方法,你便可以開始規劃腳本。本質上,我們的腳本應該做這些:
檢查幻燈片列表是否存在,并且包含一些圖片(有理由為一張圖片創建一個幻燈片效果嗎?)。
隱藏所有的照片,但不是第一個。
創建向前和向后的鏈接,和一個顯示我們在哪的指示器。
添加事件處理程序,使鏈接增加或減少當前顯示的圖片編號。
確保幻燈片效果沒有超出范圍,當圖片編號小于 0 ,他應該變為最后一張圖片,反過來類似。
不同的功能處理
我們有一些方法處理這個問題。其中之一是使用 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 腳本的幫助庫應該包括:
一個注冊事件處理函數的方法,我們目前將使用 John Resig 的 addEvent() 方法。
添加和移除 CSS 樣式名的方法。
一個覆蓋 HTML 元素默認行為的方法。我們不希望出現鏈接的目標頁,而僅是執行腳本。
我們添加這些工具方法到主要的對象上,并準備開始:
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: },
第2行,檢測 DOM 是否被支持。
第3和4行,嘗試檢索 ID 為 slideshow 的元素,如果沒有被定義則不執行余下的方法。
第5和6行,檢索列表項及列表項的個數,并分別儲存在屬性 items 和 all 里。
第7行,檢測是否超多一個列表項,如果不超多則不執行余下的。
第8行,添加 js 樣式類名到列表上,從而隱藏列表項和應該不同的樣式。
第9行,調用 createNav(),并提供這個列表作為參數。
第12行,調用 show() 用來顯示預定義了 current 屬性的滑動門。
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: },
第2和3行,剛開始創建一個 P 元素,用來包含整個幻燈片導航,并應用一個名為 slidenav 的 class。
第4和5行,創建一個新的鏈接元素,儲存在叫 prev 的屬性中,設置 href 屬性為 #。使鏈接顯示為一個真正的鏈接且鍵盤可用,是有必要的。
第6行,創建一個新的文本標簽。
第7行,將文本標簽添加到鏈接上。
第8行,添加一個事件處理函數,指向 show() 監聽方法。
第9行,將新的鏈接添加到 段落上。
第10行,開始計數器,我們創建一個 SPAN 元素,并用 count 屬性儲存他。
第11行,創建一個新的文本節點,顯示當前幻燈片在總數中的位置。我們需要給當前的屬性增加 1,因為人類計數是從 1 開始而非從 0。
第12行,將文本作為新的子節點,添加至 SPAN 上 。
第13行,將 SPAN 元素 添加到段落上。
第14至19行,基本上是復制 4 到 9 行,這次重新創建鏈接唯一不同的是文本標簽,他儲存在 next 屬性上。
第20行,將最近創建的段落插入到文檔中初始的圖片列表前。
這些被創建的所有標記都是必要的,最后剩下的是去定義一個當鏈接被點擊時調用的監聽方法 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: },
第1行,得到作為參數 e 的當前事件對象,這是稍后調用的 cancelClick() 唯一需要。
第2行,檢測點擊的元素是否是向下或者向前鏈接(this 由 addEvent() 返回)。
第3行,從當前顯示的幻燈片上移除 current 的 class。由于現在有一個被點擊的鏈接,這將成為可能。
第4行,通過比較 this 和 next 屬性,決定 current 的計數器是應該增加還是減少。
第5行,修正計數器。
第6到11行,確定計數器將永遠不會超出范圍,當你在第一幻燈片并點擊了向前的鏈接,將設置他為最后一個,而當你在最后一個幻燈片,點擊了向后的鏈接,將設置為第一個。
第13和14行,生成一個新的計數器文本并替代舊的。
第15行,通過設置名為 current 的 class,顯示新的當前幻燈片。
第16行,通過調用 cancelClick() 阻止鏈接的默認行為。
這些是腳本的所有內容。現在這個腳本可以工作,但仍不是真正可維護的。
第五步:輕松維護(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 : '>>',
counterpider : ‘ 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.counterpider + 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.counterpider + 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);
這些所有文件是確保將來維護者不用麻煩你就可以使用你的腳本工作所需要的。文件名應該很明顯,是什么就是什么,并能隨著時間的推移,成為一個標準的腳本:
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com