1. 程式人生 > 其它 >第11章 客戶端JavaScript實踐

第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 物件的文件的下載的那個伺服器。不過,只要讓伺服器轉發該請求,就能夠將請求傳送至不同域的伺服器。

跨源通訊

JSONP

iframe攻擊(iframe hack)

window.postMessage

跨源通訊的安全問題

11.3 表單

表單元素

表單控制元件

內容驗證

內容驗證的必要性
進行內容驗證的時機

可用於驗證的事件

submit
focus、blur
change
keydown、keyup、keypress
input

使用表單而不產生頁面跳轉的方法

form的target屬性