原文:Progressive Enhancement with JavaScript
如果你閱讀了本系列的第一篇和第二篇文章,現在應當開始進入漸進增強的思維模式了。本文討論如何將漸進增強的哲學應用到客戶端腳本。你將很快看到,這涉及兩個方面:約束與規劃。
也許你已經聽說過“權力腐敗”。這個詞能引發很多聯想,不過根據我們的用意,還是繼續將其看成兩個簡單的詞吧。JavaScript是一個能力強大到讓人難以置信的工具,很長一段時間它的威力讓web一團糟。它給web沖浪者扔下路障、錯誤信息還有太多的彈出窗口。對JavaScript的極大誤解,可能導致了它的濫用,並使其在實踐中更像一門黑色藝術。
JavaScript不僅造成的傷害多過好處,它還變得難以控制。實質上,它就像一輛破自行車,除了鈴兒不響,其它哪兒都響。因為晦澀並經常私下開發的代碼分支迅速繁殖,維護也是一場噩夢。
當年,JavaScript只能是丑陋的:浏覽器還需對適當標准提供支持,而開發者們正忙於在HTML中書寫像意大利面條一般雜亂的代碼。為了完成跨浏覽器兼容的任何事情,甚至簡單得像圖像翻轉,JavaScript也必須解決很多令人頭疼的問題。
謝天謝地,我們現在的處境好多了,終於能使得JavaScript代碼干淨許多。然而,我們必須尊重JavaScript的能力並且表現得負責。我們需要關注JavaScript如何使用,或許更需要關注JavaScript能做什麼。我們需要學會約束。漸進增強能夠幫助我們做到這一點,因為它讓我們關注內容並且從內容開始構建。
使用漸進增強,我們在可用代碼的基礎上構建站點。JavaScript的關鍵理念需要牢記於心:即便沒有客戶端腳本,任何用戶需要用來理解頁面意圖的內容也應當存在於該頁面中。這是第一課。
例如:上面討論的內容可能是一個售出產品的對照表格。如果站點的需求指明在點擊列頭時,需要對數據排序,你可能會考慮通過Ajax來將數據加載到頁面中,這樣發送簡短的請求就可以在服務器端進行排序。看起來很美妙,不是嗎?
錯了。
當禁用JavaScript的潛在用戶訪問該頁面時,會發生什麼?如果內容通過JavaScript加載到頁面,這些用戶根本訪問不到內容,即便在沒有排序的狀態。如果他們連產品都看不見,你認為他們達成交易的可能性還有多大呢?
上面的場景還沒有考慮到搜索的影響。搜索引擎爬蟲不會執行JavaScript,因此如果使用JavaScript來將內容加載到頁面,搜索引擎將永遠不會讀取或索引你的內容。如果產品信息不能在Google, Microsoft或Yahoo中索引和搜索到,你將喪失多少潛在客戶?
如果帶著漸進增強的想法,為了完成上面同樣的需求,可以將基本的表格包含在HTML標記中。大部分情況下依舊需要後台程序來生成,但是需要直接嵌入在頁面中而不是通過Ajax來加載。你仍然需要編寫腳本,在DOM中找到表格,使其具有交互性,生成排序鏈接,並在它們的onclick
事件中綁定Ajax調用,最後打造出一個可排序的表格。
用這種方式完成挑戰,不但滿足了需求,還為搜索引擎爬蟲和沒有JavaScript的用戶提供了一個“低保真”的體驗。
再考慮遠些,甚至還可以給表格頭部手動添加一些排序鏈接,通過傳遞排序表格的相應參數,讓它們能刷新頁面。這使得沒有JS的用戶也能重新對數據排序,雖然響應速度稍微慢些,但仍然是功能齊全的“高保真”體驗。
在腳本中加入些簡單的小調整,還可以通過hijack技術讓上面的排序鏈接依舊像以前一樣執行Ajax請求,將最好的體驗帶來大部分有能力的用戶。最後,你擁有了一個漸進增強實戰的完美例子。
到這裡,已經擁有了對JavaScript漸進增強的基本理解,我們能夠討論一些可以立刻使用的技巧了。
有效集成漸進增強的一個關鍵是為腳本的管理建立規劃。為了做到這一點,首先要熟悉“無侵入式JavaScript”的概念。無侵入式JavaScript是漸進增強在客戶端腳本世界的基礎。
“做到無侵入”最明顯的辦法是去掉所有行內的事件處理器,因為它們可以很簡單地通過DOM來注冊:
<a href="http://msdn.com"onclick="return newWin(this.href);">
接下來,將所有腳本移動到外鏈的文件中,而不是嵌入在script
元素中:
<script type="text/javascript"> // my script </script>
這可以讓維護變得更輕松同時讓你擁有一些代碼庫。(坦率地說,這兩個變化可能需要不少工作量,因為這麼多所見即所得編輯器和web應用程序開發框架生成可怕的侵入式JavaScript代碼。值得慶幸的是,很多系統已經有補丁和插件用來克服它們的壞習慣。)
要使得腳本無侵入,接下來的步驟是決定何時以及如何包含它們。在最簡單的層次上,這意味著檢查,調用方法前先測試是否支持此方法,以確保腳本能在用戶浏覽器上運行:
if( document.getElementById ){ scriptUsingGetElementById(); }
你可能還想測試所有需要的對象,甚至還想測試用作腳本鉤子的標識元素是否存在。對使用的每個腳本都按這個流程走,可以創建一個點菜式的交互體驗,只有那些用戶浏覽器可以處理以及當前頁面需要的腳本會被執行。