第11章 客戶端JavaScript實踐
11.1 樣式
對樣式進行操作指的是對頁面的外觀進行操作,而不是頁面的內容。只要設計出合適的樣式,就能夠創建出易讀、清晰、美觀的 Web 應用程式。話雖如此,易讀和美觀主要由設計師負責,屬於靜態設計的範疇,需要 CSS 方面的知識。而藉助 JavaScript,則能夠動態地變更樣式,增強頁面的可讀性。
JavaScript 實現了動態樣式變更,其目的是為使用者提供視覺反饋。例如,針對可被點選的元素,如果在滑鼠指標移動至該元素上方時改變其圖示,並改變 DOM 元素的顏色,就能夠向用戶傳達該元素可被點選的資訊。當滑鼠指標位於 DOM 元素之上時,將觸發 mouseover 事件。只需準備一個能夠改變滑鼠指標以及 DOM 元素色彩的事件偵聽器,並將其註冊至該事件,就能夠實現這一效果。
11.1.1 樣式的變更方法
可以通過以下這些方法對樣式進行變更。
- 通過 className 屬性更改 class 名
- 通過 classList 屬性更改 class 名
- 更改 style 屬性
- 直接更改樣式表
以上任意一種方法都能夠實現樣式的變更。根據不同的用途選擇合適的方法即可。
通過 className 屬性更改 class 名
通過更改 DOM 元素的 class 名來改變樣式是一種最為簡單的做法。即事先通過 CSS 定義好對應於更改前與更改好的 class 名的樣式,並在JavaScript 中替換 class 名。可以通過 className 屬性設定 class 名,如程式碼清單 11.1 所示。在這個例子中,當點選時,字元的顏色與背景色將會調換。
程式碼清單 11.1 通過 className 屬性更改 class 名
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>通過 className 屬性更改 class 名</title> <style> .hzh1 { color: red; } .hzh2 { color: orange; } .hzh3 { color: yellow; } .hzh4 { color: green; } .hzh5 { color: cyan; } .hzh6 { color: purple; } .hzh7 { color: blue; } .hzh8 { color: limegreen; } .hzh9 { color: palevioletred; } .hzh0 { color: dodgerblue; } </style> </head> <body> <h1 id="hzh" class="hzh1">點選這個會切換文字顏色</h1> <script> var hzh = document.getElementById('hzh'); hzh.onclick = function huangzihan() { this.className = ("hzh" + Math.floor(Math.random()*10)) console.log("在控制檯輸出目前的類名:"); console.log("hzh" + Math.floor(Math.random()*10)); } </script> </body> </html>
在更改 class 名時應該注意並清楚瞭解的是,如果更改了 class 名,到底有哪些元素將會受到影響。例如對於程式碼清單 11.2 的情況,如果更改了 1 個元素的 class 名,其相鄰元素與子孫元素的樣式也會一起改變。這時,如果需要改變的元素過多,則可能出現效能上的問題。不過,如果要考慮效能的話,指定了相鄰元素與子元素的CSS 的寫法本身就是有問題的。最好對這些元素分別設定不同的 class 名並進行式樣操作。
程式碼清單 11.2 在更改 class 名後相關元素的樣式也會改變
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>在更改 class 名後相關元素的樣式也會改變</title>
<style>
.hzh-before {
background-color: white;
color: black;
}
.hzh-before p {
text-decoration: none;
}
.hzh-before + div {
text-decoration: none;
}
.hzh-after {
background-color: black;
color: white;
}
.hzh-after p {
text-decoration: underline;
}
.hzh-after + div {
text-decoration: line-through;
}
</style>
</head>
<body>
<div id="hzh" class="hzh-before">
<p>hzh1</p>
<p>hzh2</p>
<p>hzh3</p>
<p>hzh4</p>
</div>
<div>
黃子涵
</div>
<script>
var hzh = document.getElementById('hzh');
hzh.onclick = function toggleStyle() {
this.className = (this.className === 'hzh-before') ? 'hzh-after' : 'hzh-before';
};
</script>
</body>
</html>
通過 classList 屬性更改 class 名
還可以通過對在 HTML5 中新增的 classList 屬性進行操作,來更改 class 名。與通過對 className 屬性進行操作以更改 class 名相比,這種方法更加容易理解(程式碼清單 11.3)
程式碼清單 11.3 在更改 class 名後相關元素的樣式也會改變
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>在更改 class 名後相關元素的樣式也會改變</title>
<style>
.hzh-before {
background-color: white;
color: black;
}
.hzh-before p {
text-decoration: none;
}
.hzh-before + div {
text-decoration: none;
}
.hzh-after {
background-color: black;
color: white;
}
.hzh-after p {
text-decoration: underline;
}
.hzh-after + div {
text-decoration: line-through;
}
</style>
</head>
<body>
<div id="hzh" class="hzh-before">
<p>hzh1</p>
<p>hzh2</p>
<p>hzh3</p>
<p>hzh4</p>
</div>
<div>
黃子涵
</div>
<script>
var hzh = document.getElementById('hzh');
hzh.onclick = function toggleStyle() {
this.classList.toggle('hzh-before');
this.classList.toggle('hzh-after');
};
</script>
</body>
</html>
表 11.1 總結了可以使用 classList 屬性的方法。classList 屬性是一種對 DOM TokenList 介面的實現。
表 11.1 可以使用 classList 屬性的方法
方法名 | 說明 |
---|---|
contains(clazz) | 判斷在 class 名中是否含有 clazz |
add(clazz) | 向 class 名中新增 clazz |
remove(clazz) | 從 class 名中刪除 clazz |
toggle(clazz) | 如果在 class 名中含有 clazz 則將它刪除,否則向 class 名中新增 clazz |
更改 style 屬性
可以直接更改 DOM 元素的 style 屬性的值以實現樣式的更改。這與更改 class 名的情況不同,樣式更改的範圍被明確地限定於這個元素。另外,通過 style 屬性指定的內容的應用優先順序將僅次於 CSS 中被標記為 !important 的元素。
style 屬性中的各個屬性名是由在 CSS 中所指定的屬性名演變而來的,其更改之處為去除了屬性名中的連字元(-)並將連字元之後的字母改為了大寫形式。例如,對於 CSS 中的 margin-top 屬性,在 JavaScript 中則可以通過 marginTop 的名稱使用。之所以要將連字元去除,是因為連字元在 JavaScript 將被識別為減號。此外,由於 float 在 JavaScript 中是作為保留字使用的,因此 float 屬性不能直接使用。如果要在 JavaScript 中更改 float 屬性,則需要通過 cssFloat 屬性來進行操作。
在程式碼清單 11.4 中是一個更改 style 屬性更改的例子。
程式碼清單 11.4 更改 style 屬性
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>更改style屬性</title>
<style>
#hzh {
background-color: white;
color: black;
}
</style>
</head>
<body>
<div id="hzh">黃子涵。請點選這裡。</div>
<div id="huangzihan">黃子涵是帥哥!</div>
<script>
var hzh = document.getElementById('hzh');
hzh.onclick = function toggleStyle() {
var style = this.style;
if(!style.cssFloat) {
style.cssFloat = 'left';
style.backgroundColor = 'black';
style.color = 'white';
} else {
style.cssFloat = '';
style.backgroundColor = 'white';
style.color = 'black';
}
};
</script>
</body>
</html>
直接更改樣式表
還可以直接對是否應用樣式表進行設定。如果將 link 元素與 style 元素的 disabled 屬性設為 true,相應的樣式表就將被禁用(程式碼清單11.5)。
程式碼清單 11.5 直接更改樣式表
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>直接更改樣式表</title>
<link rel="stylesheet" type="text/css" href="hzh1.css" id="hzh1" disabled="true">
<link rel="stylesheet" type="text/css" href="hzh2.css" id="hzh2" disabled="true">
<style id="hzh3" disabled="true">
#hzh {
background-color: #999;
}
</style>
<script>
function change(id, enable) {
// 在勾選了複選框之後啟用樣式
document.getElementById(id).disabled = !enable;
}
window.addEventListener('load', function () {
// 在初始化處理中禁用所有樣式
var style = document.styleSheets;
for (var i = 0; i < style.length; i++) {
style[i].disabled = true;
}
}, false);
</script>
</head>
<body>
<div id="hzh">黃子涵</div>
<input type="checkbox" onchange="change('hzh1', this.checked)">
<input type="checkbox" onchange="change('hzh2', this.checked)">
<input type="checkbox" onchange="change('hzh3', this.checked)">
</body>
</html>
<!--
/* hzh1.css的內容*/
#hzh {
font-size: x-large;
}
/* hzh2.css的內容*/
#hzh {
text-decoration: underline;
}
-->
11.2 AJAX
AJAX 是 Asynchronous JavaScript + XML 的簡稱。AJAX 一詞的實際含義為“不發生頁面跳轉、非同步載入內容並改寫頁面內容的技術”。在實際操作中,AJAX 不僅僅會使用 XML 資料,很多時候也會對 JSON 或純文字進行操作。AJAX這個詞是由 Jesse James Garrett 在 2005 年命名的。不過,在那之前就已經有使用 AJAX 的網站。眾所周知,Google 的 Gmail 就是一個利用了AJAX 的優秀範例。如今,AJAX 正在全世界範圍內迅速地得到普及。
非同步處理的優點
AJAX 的關鍵在於它是以非同步的方式執行的。非同步處理的優點是不會讓使用者白白等待。對於同步處理來說,在處理完來自伺服器的響應之前,使用者無法進行任何其他操作,只能等待。如果伺服器的響應發生了延遲,會讓使用者誤以為頁面失去了響應。在優先考慮使用者體驗時,與同步處理相比,採用非同步處理的方式更為合適,這一點是顯而易見的。JavaScript 是一種事件驅動程式設計語言,在很多地方都會用到非同步處理,所以要理解非同步處理並不是一件難事。
XMLHttpRequest
如果要通過 JavaScript 動態地向伺服器傳送請求,則需要使用 XMLHttpRequest 物件。不過在這裡要稍微提醒一下大家,XMLHttpRequest 目前尚未被制定為標準。之所以說是“稍微”,是因為那些現代的瀏覽器自然不必多說,即使是Internet Explorer 也在 7 以及之後的版本里全都使用了通用的 API,因此它已經成為了一種事實標準。考慮到這點,尚未標準化也就不是什麼問題了。問題在於 Internet Explorer 6。不過它也僅僅是在物件的建立方式上有所不同,物件所含有的方法與其他瀏覽器中的實現是通用的。
基本的處理流程
XMLHttpRequest 物件的建立
傳送請求
readyState的含義
同步通訊
超時
響應
跨源限制
所謂跨源限制指的是,對源不同的通訊進行限制。而這裡的源指的是由 URL 的協議(http: 或 https: 等)、主機名、埠號所構成的元素。在 Web 領域,為了確保安全性,只有同源的通訊才能被允許進行,這稱為同源策略。
雖然可以在 HTML 中使用 iframe 以實現在一個頁面中同時顯示來自不同域的文件,不過 JavaScript 仍然只能訪問同一個源的文件。如果文件的 URL 和 iframe 的不同,則無法通過文件中所包含的 JavaScript 對 iframe 內的 DOM 進行操作,而 iframe 內的 JavaScript 也無法操作文件中的 DOM。如果不這樣,就會發生諸如不同域的 Cookie 能夠相互訪問等安全問題。
對於 XMLHttpRequest 來說,同源策略的含義是,一個 XMLHttpRequest 物件只能傳送至一個特定的伺服器,即提供了使用該 XMLHttpRequest 物件的文件的下載的那個伺服器。不過,只要讓伺服器轉發該請求,就能夠將請求傳送至不同域的伺服器。