javascript
JavaScript DOM编程艺术(第2版) 笔记
提示:文章寫完后,目錄可以自動生成,如何生成可參考右邊的幫助文檔
目錄
- 基本概念
- 1、JavaScript語法
- 1.1 JavaScript 代碼的執行:
- 1.2 JavaScript 語句
- 1.3 變量
- 1.4 數據類型
- 1.4.1 字符串
- 1.4.2 數值
- 1.4.3 數組
- 1.4.4 對象
- 1.5 操作
- 1.6 條件語句
- 1.6.1 if 條件語句
- 1.6.2 比較操作符
- 1.6.3 邏輯操作符
- 1.6.4 循環語句
- 1.7 函數
- 1.8 對象
- 1.8.1 內建對象
- 1.8.2 宿主對象
- 2、DOM
- 2.1 獲取元素
- 小結
- 2.2 獲取和設置屬性
- 2.2.1 getAttribute() 方法
- 2.2.2 setAttribute() 方法
- 4. JavaScript 圖片庫
- 4.1 事件處理函數
- 4.2 事件處理函數的工作機制
- 4.3 向后兼容
- 4.3.1 對象檢測
- 4.3.2 瀏覽器嗅探
- 4.4 性能考慮
- 4.4.1 盡量少訪問DOM和盡量減少標記
- 4.4.2 合并和放置腳本
- 4.4.3 壓縮腳本
- 5. 動態創建標記
- 5.1 傳統方法
- 5.1.1 document.write()方法
- 5.1.2 innerHTML屬性
基本概念
程序設計語言 分為 解釋型 和 編譯型 兩大類。Java 或 C++ 等語言需要一個編譯器(compiler)。編譯器是一種程序,能夠把用 Java 等高級語言編寫出來的源代碼翻譯為直接在計算機上執行的文件。
JavaScript 是一種解釋型程序設計語言。
解釋型程序設計語言 不需要編譯器——它們僅 需要解釋器。對于 JavaScript 語言,在互聯網環境下,Web瀏覽器負責完成有關的解釋和執行工作。瀏覽器中的 JavaScript 解釋器將直接讀入源代碼并執行。瀏覽器中如果沒有解釋器,JavaScript 代碼就無法執行。
1、JavaScript語法
1.1 JavaScript 代碼的執行:
-
方式一:將JavaScript 代碼放到文檔<head>標簽中的<script>標簽之間;
-
方式二:把JavaScript代碼保存為一個擴展名為 .js 的獨立文件,在文檔的<head>部分放一個<script>標簽,并把它的src屬性指向該文件;
但最好的做法是把<script>標簽放到HTML文檔的最后,</body>標簽之前:
<!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"/><title>Example</title></head><body>Mark-up goes here...<script src="file.js"></script></body> </html>—— 這樣能使瀏覽器 更快 地 加載 頁面。
上例中的<script>標簽沒有包含傳統的type="text/javascript"屬性。是因為腳本默認是 Java Script,所以并沒有必要去指定這個屬性。
1.2 JavaScript 語句
只需簡單地把各條語句放在不同的行上就可以分隔,如下所示:
first statement second statement但是,在每條語句的末尾都加上一個分號,才是一種良好的編程習慣:
first statement; second statement;這樣做讓代碼更容易閱讀。讓每條語句獨占一行的做法能更容易跟蹤JavaScript腳本的執行順序。
1.3 變量
JavaScript 允許 直接對變量賦值而 無需事先聲明。
很多程序設計語言中是不允許的。有很多語言要求在使用任何變量之前必須先對它進行聲明(declare)。
JavaScript 腳本中,如果對某個變量賦值之前未聲明,賦值操作將自動聲明該變量。
雖然 JavaScript 沒有強制要求程序員必須提前聲明變量,但提前聲明變量是一種良好的編程習慣。如下所示:
var mood; var age;不必單獨聲明每個變量,可以用一條語句一次聲明多個變量:
var mood, age;甚至可以 聲明變量 和對該 變量賦值 一次完成:
var mood = "happy"; var age = 33;或者寫成:
var mood = "happy", age = 33;在 JavaScript 里,變量 和 其他 語法元素 的名字都是 區分 字母 大小寫。
1.4 數據類型
必須 明確類型聲明 的語言稱為 強類型(strongly typed)語言。JavaScript 不需要進行類型聲明,因此它是一種 弱類型(weakly typed)語言。
弱類型意味著程序員可以在任何階段改變變量的數據類型。
1.4.1 字符串
下面這兩條語句含義完全相同:
var mood = 'happy'; var mood = "happy";但是,作為一個好的編程習慣,不管選擇用雙引號還是單引號,整個腳本中應保持一致(否則,代碼會變得難以閱讀和理解)。
1.4.2 數值
JavaScript 也支持浮點數
var temperature = -20.333333331.4.3 數組
數組用關鍵字Array聲明。聲明數組的同時還可指定數組的長度(length):
var beatles = Array(4);數組下標是從0開始計數;
如果無法預知數組元素個數,可以在聲明時不給出元素個數:
var beatles = Array();這是一種相對簡單的方式,在聲明數組的同時對它進行填充
var beatles = Array( "John", "Paul", "George", "Ringo" );甚至,用不著明確表明是在創建數組。只需用一對方括號把各個元素的初始值括起即可:
var beatles = [ "John", "Paul", "George", "Ringo" ];甚至也可以把多種數據類型混在一起存入一個數組:
var lennon = [ "John", 1940, false ];1.4.4 對象
與使用Array類似,創建對象使用Object關鍵字。它不使用方括號和下標來獲取元素,而是像任何 JavaScript 對象一樣,使用點號來獲取屬性;
var lennon = { name:"John", year:1940, living:false };用對象來代替傳統數組,就用元素的名字而不是下標數字來訪問它們。這提高了腳本的可讀性。
1.5 操作
賦值使用等號(=)。加法操作符加號(+),減法操作符減號(-),除法操作符斜杠(/),乘法操作符星號(*)。
- +=,可以一次完成 “ 加法和賦值 ”(或“拼接和賦值”)操作:var year = 2010;
var message = "The year is ";
message += year;
alert(message); // The year is 2010
運行結果如下所示:
1.6 條件語句
1.6.1 if 條件語句
條件必須放在if后面的圓括號中。條件的 求值結果 永遠是一個 布爾值,即只能是true或false。
if ('條件') {statements; }并且,只有在給定條件的求值結果是true的情況下才會執行,如下所示:
if (1 > 2) {alert("全世界都瘋了!"); }上例中,1>2 這個條件的值是false,因此alert消息不會出現。
if語句中的{}(花括號)本身并不是必須的。如果if語句中的{}部分只包含一條語句的話,則可不寫{}且寫在同一行上,如下所示:
if (1 > 2) alert("全世界都瘋了!");為提高腳本的可讀性,建議在if語句中使用花括號,這是個 好習慣。
else子句:包含在else子句中的語句會在給定條件為假時執行:
if (1 > 2) {alert("全世界都瘋了!"); } else {alert("一切都很好"); }1.6.2 比較操作符
-
等于(==) :注意,單個等號(=)用于完成賦值操作。
var my_mood = "happy"; var your_mood = "sad"; if (my_mood == your_mood) {alert("我們都有同樣的感覺."); }
-
不等于(!=):由一個感嘆號和一個等號構成。
if (my_mood != your_mood) {alert("我們的心情不一樣."); }相等操作符==并不表示嚴格相等。
例如,比較false與一個空字符串會得到什么結果?
var a = false; var b = ""; if (a == b) {alert("a 等于 b"); }這里,條件語句”a==b“求值結果為true,因為相等操作符==認為 空字符串 與false的含義相同。要進行嚴格比較,就要使用另一種等號(===)。
-
全等(===):會執行嚴格的比較,不僅比較值,而且會比較變量的類型:
var a = false; var b = ""; if (a === b) {alert("a 等于 b"); }這樣,條件表達式的求值結果就是false了,因為Boolean和String不是一種類型。
1.6.3 邏輯操作符
邏輯操作符的操作對象是布爾值。每個邏輯操作數返回一個布爾值true或者是false。
-
邏輯與(&&):只有在它的操作數都為true時,返回true。
if ( num >= 5 && num <= 10 ) {alert("號碼在正確的范圍內."); }
-
邏輯或(||):操作數都是true,或其中有一個是true,返回true;當操作數都為false時,返回false。
if (num > 10 || num < 5) {alert("號碼在正確的范圍內."); }
-
邏輯非(!):把邏輯操作數所返回的布爾值 取反。
if (!(1 > 2)) {alert("世界一切都好"); } if (!(num > 10 || num < 5)) {alert("號碼在正確的范圍內."); }
1.6.4 循環語句
-
while 循環
對循環控制條件的求值發生在每次循環結束之前:
while ('條件') {statements; }
區別:while循環只要條件為true,{}里的代碼將反復執行。
var count = 1; while (count < 11) {alert (count);count++; }在這個while循環的內部,用“++”操作符對變量count的值執行加1操作,而這會重復執行10次。
注: 如果循環控制條件的首次求值結果是false,后面的程序一次也不會被執行。
-
do…while 循環
對循環條件求值在每次循環結束之后:
do {statements; } while ('條件');因此,即使循環條件的首次求值為false,花括號里的語句至少會被執行一次。
示例代碼:
var count = 1; do {alert (count);count++; } while (count < 11);
-
for 循環
for 循環是 while 循環的一種變體,好處是循環控制結構更加清晰。
for (var count = 1; count < 11; count++ ) {alert (count); }常見用途:對某個數組里的全體元素進行遍歷處理。
1.7 函數
為實現同一段代碼的復用,可以把它們封裝成一個函數(function)。每個函數實際上是一個短小的腳本。
定義函數時,可以聲明任意多個參數,中間用逗號分隔。在函數的內部,可以像使用普通變量那樣使用任何一個參數。
注意,函數體內部的語句在執行時,一旦執行到return時,函數就執行完畢,并將結果返回。因此,函數內部通過條件判斷和循環可以實現非常復雜的邏輯。
如果沒有return語句,函數執行完畢后也會返回結果,只是結果為undefined。
-
【語法】:
function name(arguments) {statements; } -
【示例1】:
// 定義函數 function shout() {var beatles = Array("John","Paul","George","Ringo");for (var i = 0 ; i < beatles.length; i++ ) {alert(beatles[i]);} } // 調用函數 shout();
-
【示例2】:
如下所示,該函數需要傳遞兩個參數。如果把 2 個數值傳遞給函數,這個函數將對進行乘法運算:
function multiply(num1,num2) {var total = num1 * num2;alert(total); }在定義了這個函數的腳本里,即可在任意位置調用(調用函數時,按順序 傳入參數即可):
multiply(10,2);
-
【示例3】:
function multiply(num1,num2) {var total = num1 * num2;return total; }
函數不僅能夠(以參數的形式)接收數據,還能夠返回數據。這需要用到return語句:注: 由于 JavaScript 允許傳入任意個參數而不影響調用,因此傳入的參數比定義的參數多也沒有問題,雖然函數內部并不需要這些參數。
-
arguments 關鍵字
function foo(x) {console.log('x = ' + x); // 10for (var i=0; i<arguments.length; i++) {console.log('arg ' + i + ' = ' + arguments[i]); // 10, 20, 30} }// 調用函數 foo(10, 20, 30);// 控制臺結果輸出如下: // x = 10 // arg 0 = 10 // arg 1 = 20 // arg 2 = 30
JavaScript 還有一個特殊的關鍵字arguments,它只在函數內部起作用,并且永遠指向當前函數的調用者傳入的所有參數。arguments類似Array但它不是一個Array:
利用arguments,可以獲得調用者傳入的所有參數。即使函數不定義任何參數,也還是可以拿到參數的值:
function abs() {if (arguments.length === 0) {return 0;}var x = arguments[0];return x >= 0 ? x : -x; }abs(); // 0 abs(10); // 10 abs(-9); // 9
實際上,`arguments`最常用于判斷傳入參數的個數: // foo(a[, b], c) // 接收2~3個參數,b是可選參數,如果只傳2個參數,b默認為null: function foo(a, b, c) {if (arguments.length === 2) {// 實際拿到的參數是a和b,c為undefinedc = b; // 把b賦給cb = null; // b變為默認值}// ... }要把中間的參數b變為 “可選” 參數,就只能通過arguments判斷,然后重新調整參數并賦值。
-
rest 參數
ES6 標準引入了rest參數,該參數只能寫在最后,前面用...標識。
function foo(a, b, ...rest) {console.log('a = ' + a);console.log('b = ' + b);console.log(rest); }foo(1, 2, 3, 4, 5); // 結果: // a = 1 // b = 2 // Array [ 3, 4, 5 ]從運行結果可知,傳入的參數先綁定a、b,多余的參數以數組形式交給變量rest,所以,不再需要arguments我們就獲取了全部參數。
因為rest參數是 ES6 新標準,所以需要先測試一下瀏覽器是否支持:用rest參數編寫一個sum()函數,接收任意個參數并返回它們的和:
function sum(...rest) {??? } // 測試: var i, args = []; for (i=1; i<=100; i++) {args.push(i); } if (sum() !== 0) {console.log('測試失敗: sum() = ' + sum()); } else if (sum(1) !== 1) {console.log('測試失敗: sum(1) = ' + sum(1)); } else if (sum(2, 3) !== 5) {console.log('測試失敗: sum(2, 3) = ' + sum(2, 3)); } else if (sum.apply(null, args) !== 5050) {console.log('測試失敗: sum(1, 2, 3, ..., 100) = ' + sum.apply(null, args)); } else {console.log('測試通過!'); }
1.8 對象
對象 是自包含的 數據集合,包含在對象里的數據可以通過兩種形式訪問—— 屬性(property) 和 方法(method):
- 屬性:隸屬于某個特定對象的 變量;
- 方法:只有某個特定對象才能調用的 函數。
對象就是由一些屬性和方法組合在一起而構成的一個 數據實體 。
創建對象實例需使用new關鍵字:
var jeremy = new Person;1.8.1 內建對象
JavaScript 提供了一系列預先定義好的對象,這些對象稱為 內建對象(native object),例如Array對象、Math對象、Date對象。
在編寫 JavaScript 腳本時,內建對象可以幫助我們 快速、簡單 地完成許多任務。
當使用new關鍵字初始化一個 數組 時,其實是在創建一個Array對象的 新實例:
var beatles = new Array();Math對象的round方法可以把十進制數值舍入為一個與之最接近的整數:
var num = 7.561; var num = Math.round(num); alert(num);Date對象可以用來存儲和檢索與特定日期和時間有關的信息。在創建新實例時,JavaScript 解釋器將自動地使用當前日期和時間對它進行初始化:
var current_date = new Date();1.8.2 宿主對象
對Web應用來說,就是由瀏覽器這個運行環境預先定義好的其他對象。它不是 JavaScript 語言本身提供的。
我們把由瀏覽器提供的預定義對象稱為 宿主對象(host object)。
宿主對象包括Form、Image、Element、document對象等。可以通過這些對象獲得網頁上表單、圖像和各種表單元素的信息。
2、DOM
2.1 獲取元素
有 3 種 DOM 方法可獲取元素節點,分別是通過 元素ID、通過 標簽名字 和通過 類名 來獲取。
注意,JavaScript 語言區分字母大小寫。
-
getElementById
它是document對象特有的函數,getElementById方法只有一個參數。返回一個對象。
document.getElementById(id)
-
getElementsByTagName
該方法返回對象數組,每個對象分別對應文檔里給定標簽的一個元素。只有一個參數,它的參數是標簽的名字:
element.getElementsByTagName(tag)注: 即使整個文檔里這個標簽只有一個元素,getElementsByTagName返回的也是一個數組,此時數組長度為1。
為改善代碼的可讀性:可把document.getElementsByTagName("li")賦值給一個變量。如下所示:
var items = document.getElementsByTagName("li"); for (var i=0; i < items.length; i++) {alert(typeof items[i]); }
-
getElementById和getElementsByTagName綜合運用。
如果想知道id屬性值為purchase的元素包含著多少個列表項,必須通過一個更具體的對象去調用這個方法,如下所示:
var shopping = document.getElementById("purchases"); var items = shopping.getElementsByTagName("*");
-
getElementsByClassName
該方法返回值是一個數組HTML5 DOM(http://www.whatwg.org/specs/web-apps/current-work/)中新增一個方法:getElementsByClassName。該方法能夠通過class屬性中的類名來訪問元素。
document.getElementsByClassName("sale")getElementsByClassName方法只有較新的瀏覽器才支持,下面這個自定義函數能使之 兼容 新老瀏覽器:
function getElementsByClassName(node, classname) {if (node.getElementsByClassName) {// 使用現有方法return node.getElementsByClassName(classname);} else {var results = new Array();var elems = node.getElementsByTagName("*");for (var i=0; i<elems.length; i++) {if (elems[i].className.indexOf(classname) != -1) {results[results.length] = elems[i];}}return results;} }此getElementsByClassName函數接受兩個參數。第一個node表示DOM樹中的搜索起點,第二個classname就是要搜索的類名了。
如果傳入節點上已經存在了適當的getElementsByClassName函數,那么這個新函數就直接返回相應的節點列表。
如果getElementsByClassName函數不存在,這個新函數就會循環遍歷所有標簽,查找帶有相應類名的元素(這個例子不適用于多個類名。)
如果使用這個函數來模擬前面取得購物列表的操作,就可以這樣寫:
var shopping = document.getElementById("purchases"); var sales = getElementsByClassName(shopping, "sale");
小結
2.2 獲取和設置屬性
得到需要的元素以后,我們就可以設法獲取它的各個屬性。
- getAttribute()方法:獲取元素的各個屬性;
- setAttribute()方法:更改屬性節點的值。
2.2.1 getAttribute() 方法
getAttribute方法只有 1 個參數(要查詢的屬性名),由于該方法 不屬于document 對象,所以不能通過 document 對象調用,只能 通過元素節點對象調用。
【語法】:
object.getAttribute(attribute) // attribute:屬性【語法示例】:
var paras = document.getElementsByTagName('p'); for (var i=0; i < paras.length; i++ ) {alert(paras[i].getAttribute('title')); }當某個<p>元素沒有title屬性時,getAttribute("title")方法會返回null值;
如果只在title屬性有值時才彈出消息,則需要增加if語句來檢查getAttribute的返回值是不是null。同時我們增加幾個變量以提高腳本的可讀性:
var paras = document.getElementsByTagName('p'); for (var i=0; i< paras.length; i++) {var title_text = paras[i].getAttribute('title');if (title_text != null) {alert(title_text);} }可以進一步優化如下:
var paras = document.getElementsByTagName('p'); for (var i=0; i< paras.length; i++) {var title_text = paras[i].getAttribute('title');if (title_text) alert(title_text); }2.2.2 setAttribute() 方法
setAttribute() 方法允許對屬性節點的值做出修改。與getAttribute一樣,setAttribute也只能用于元素節點:
【語法】:
object.setAttribute(attribute,value)【示例】:
var shopping = document.getElementById('purchases'); shopping.setAttribute('title','貨物清單');第一條語句獲取到id為purchase的元素,第二條語句把該元素的title屬性的值設置為貨物清單( 注意語法,這里的 屬性 和 值 之間用英文逗號,分隔 —— 本文筆者注)。
驗證title屬性的值:
var shopping = document.getElementById('purchases'); console.log(shopping.getAttribute('title')); // 輸出 nullshopping.setAttribute('title','貨物清單'); console.log(shopping.getAttribute('title')); // 輸出 “貨物清單”控制臺第一次輸出空白,第二次會輸出“貨物清單”;
上例中,設置了一個節點的title屬性,這個屬性原先并不存在。setAttribute實際完成了兩項操作:先創建這個屬性,然后設置它的值。如果setAttribute用在一個本身就有這個屬性的元素節點上,那么該屬性的值會被覆蓋。
通過setAttribute對文檔做出修改后,在通過瀏覽器的view source(查看源代碼)選項去查看文檔的源代碼時看到的仍是改變前的屬性值,也就是說,setAttribute做出的修改不會反映在文檔本身的源代碼里。
這種“表里不一”的現象源自DOM的工作模式:先加載文檔的靜態內容,再動態刷新,動態刷新不影響文檔的靜態內容。這正是DOM的真正威力:對頁面內容進行刷新卻不需要在瀏覽器里刷新頁面。
4. JavaScript 圖片庫
4.1 事件處理函數
事件處理函數的作用是,在特定事件發生時調用特定的 JavaScript 代碼。
- onmouseover:鼠標懸停某個元素上時觸發動作;
- onmouseout:鼠標離開某個元素時觸發動作;
- onclick:用戶點擊某個鏈接時觸發動作。
4.2 事件處理函數的工作機制
onclick存在默認行為,某些情況下需要對其進行阻止。如a標簽的默認跳轉行為。
一旦事件發生,相應的 JavaScript 代碼就會得到執行并返回一個值,該值被傳遞給事件處理函數。
例如,我們給某個鏈接添加一個onclick事件處理函數,并讓處理函數所觸發的JavaScript代碼返回布爾值true或false。當鏈接被點擊時,如果返回的值是true,onclick事件處理函數就認為“這個鏈接被點擊了”,反之亦然。
可通過下面這個簡單測試去驗證這一結論:
<a href="http://blog.driverold.com" onclick="return false;">點我</a>當點擊鏈接時,因為onclick事件處理函數所觸發的JavaScript代碼返回給它的值是false,所以這個鏈接的默認行為沒有被觸發。
同樣道理,如果像下面這樣,在onclick事件處理函數所觸發的 JavaScript 代碼里增加一條return false語句,也可以防止用戶被帶到目標鏈接窗口:
<li><a href="images/fireworks.jpg" onclick="showPic(this); ? return false;" title="煙花表演"> 煙花 </a> </li>4.3 向后兼容
4.3.1 對象檢測
檢測瀏覽器對JavaScript的支持程度:
只要把某個方法打包在一個if語句里,就可以根據這條if語句的條件表達式的求值結果是true(方法存在)還是false(方法不存在)來決定應該采取怎樣的行動。這種檢測稱為 對象檢測(object detection)
注: 在使用對象檢測時,一定要刪掉方法名后面的圓括號,如果不刪掉,測試的將是方法的結果,無論方法是否存在。
【示例】:
function myFunction() {if (document.getElementById) {statements using getElementById} }因此,如果某個瀏覽器不支持getElementById()方法,它就不會執行使用此方法的語句。
但這樣寫會有一個問題,如果要對多個DOM方法、屬性進行判斷,那么就會嵌套無數的花括號,可以采用邏輯非(!)操作符和邏輯或(||)操作符簡化:
if (!document.getElementById || !document.getElementsByTagName) return false;【示例】
window.onload = function() {if (!document.getElementsByTagName) return false; // 退出函數并返回falsevar lnks = document.getElementsByTagName("a");for (var i=0; i<lnks.length; i++) {if (lnks[i].getAttribute("class") == "popup") {lnks[i].onclick = function() {popUp(this.getAttribute("href"));return false;}}} }這樣,可以確保那些 “古老的” 瀏覽器不會因為腳本代碼而出問題。也是為了讓腳本有良好的向后兼容性。
4.3.2 瀏覽器嗅探
瀏覽器嗅探 指通過提取瀏覽器供應商提供的信息來解決向后兼容問題。
從理論上講,可以通過JavaScript代碼檢索關于瀏覽器品牌和版本的信息,但這是一種風險非常大的技術。
- 因為歷史原因,某些瀏覽器會把自己報告為另外一種瀏覽器;
- 瀏覽器嗅探腳本會變得越來越復雜
- 如果想讓瀏覽器嗅探腳本能夠跨平臺工作,就必須測試所有可能出現的供應商和版本號組合。這是一個無窮盡的任務;
- 測試的組合情況越多,代碼就越復雜和冗長。
- 為做到瀏覽器版本的精確匹配,每當市場上出現新版本時,就不得不修改這些腳本。
4.4 性能考慮
不要忽視腳本對 Web 應用整體性能的影響。為保證應用流暢地運行,在為文檔編寫和應用腳本時,需要注意一些問題:
- 盡量少訪問DOM和盡量減少標記;
- 合并和放置腳本;
- 壓縮腳本
4.4.1 盡量少訪問DOM和盡量減少標記
訪問 DOM 的方式對腳本性能會產生非常大的影響。如下所示:
if (document.getElementsByTagName("a").length > 0) {var links = document.getElementsByTagName("a");for (var i=0; i<links.length; i++) {// 對每個鏈接做處理} }不管什么時候,只要是查詢 DOM 中的某些元素,瀏覽器都會搜索整個DOM 樹,從中查找可能匹配的元素。
上列中,這段代碼使用了兩次getElementsByTagName方法去執行相同的操作,浪費了一次搜索。
更好的辦法是把第一次搜索的結果保存在一個變量中,然后在循環里重用該結果,比如:
var links = document.getElementsByTagName("a"); if (links.length > 0) {for (var i=0; i<links.length; i++) {// 對每個鏈接做點處理} }這樣,代碼功能沒有變,但搜索 DOM 的次數由兩次降低到了一次。
另外,盡量減少文檔中的(HTM結構)標記數量。過多不必要的元素只會增加 DOM 樹的規模,進而增加遍歷 DOM 樹以查找特定元素的時間。
4.4.2 合并和放置腳本
推薦的做法是外部腳本文件,即通過<script>元素引入,如下所示:
<script src="script/function.js"></script>-
合并
避免類似下面這種情況:
<script src="script/functionA.js"></script> <script src="script/functionB.js"></script> <script src="script/functionC.js"></script> <script src="script/functionD.js"></script>正確的做法是把functionA.js、functionB.js、functionC.js和functionD.js合并到一個腳本文件中,以減少加載頁面時發送的請求數量。
-
放置腳本
把腳本放在文檔的<head>區域。這是傳統做法,但是位于<head>塊中的腳本會導致瀏覽器 無法并行加載 其他文件(如圖像或其他腳本);
根據 HTTP 規范,瀏覽器每次從同一個域名最多只能同時下載2個文件。
而在下載腳本期間,瀏覽器不會下載其他任何文件,即使是來自不同域名的文件也不會下載,所有其他資源都要等腳本加載完畢后才能下載。
把所有<script>標簽都放到文檔的末尾,</body>標記之前,就可以讓頁面變得更快。這樣,即使在加載腳本,window對象的load事件依然可以執行對文檔進行的各種操作。
4.4.3 壓縮腳本
壓縮腳本,指的是把腳本文件中不必要的字節(如空格和注釋),統統 刪除,從而達到 “ 壓縮 ” 文件的目的。
多數情況下,你應該有兩個版本,一個是工作副本,可以修改代碼并添加注釋;另一個是精簡副本,用于放在站點上。通常,為了與非精簡版本區分開,最好在精簡副本的文件名中加上min字樣:
<script src="scripts/scriptName.min.js"></script>壓縮腳本文件,可以 加快加載速度:
這可以借助工具來替你完成這件事。
推薦的代碼壓縮工具:
Douglas Crockford 的JSMin(http://www.crockford.com/javascript/jsmin.html);
雅虎的YUI Compressor(http://developer.yahoo.com/yui/compressor);
谷歌的Closure Compiler(http://closure-compiler.appspot.com/home)。
5. 動態創建標記
5.1 傳統方法
5.1.1 document.write()方法
document 對象的write()方法可以方便快捷地把字符串插入到文檔里。
缺點: 違背了 “行為應與表現分離” 的原則。即使把 document.write 語句挪到外部函數里,也還是需要在HTML標記的<body>部分使用<script>標簽才能調用那個函數。
function insertParagraph(text) {var str = "<p>";str += text;str += "</p>";document.write(str); }把這個函數保存在外部文件example.js里。為了調用該函數,必須在標記里插入<script>標簽:
<!DOCTYPE html> <html lang="en"> <head><meta charset="utf-8" /><title>Test</title><script src="example.js"></script> </head> <body> <script>insertParagraph("This is inserted.");</script> </body> </html>像上面這樣的例子,這樣的標記既不容易閱讀和編輯,還很容易導致驗證錯誤。比如<script>標簽后面的“<p>”很容易被誤認為是<p>標簽。
只要有可能,就應該用外部CSS文件代替<font>標簽去設定和管理網頁的樣式信息,最好用外部 JavaScript 文件去控制網頁的行為。應避免在<body>部分亂用<script>標簽,避免使用document.write方法。
5.1.2 innerHTML屬性
如今的瀏覽器幾乎都支持屬性innerHTML,該屬性并非 W3C DOM 標準的組成部分,但現已經包含到 HTML5 規范中。
它可用來讀、寫 給定元素 里的 HTML 內容。
總結
以上是生活随笔為你收集整理的JavaScript DOM编程艺术(第2版) 笔记的全部內容,希望文章能夠幫你解決所遇到的問題。