《JavaScript DOM 編程藝術》 閱讀摘要

概念:javascript

  • 平穩退化
  • 漸進加強
  • 以用戶爲中心

第一章 js簡史

可使用DOM(Document Object Model)給HTML(HyperText Markup Language)文檔增長交互能力,就像CSS(Cascading Style Sheet)給文檔增長樣式同樣。DOM是一種API(Application Programing Interface),就是一種已獲得各方認同的基本約定,做爲一種標準可使得人們更方便的進行交流和合做,相似於化學元素週期表、莫爾斯碼,W3C聯盟推出的標準化DOM理論上可讓任何一種程序設計語言對使用任何一種標記語言編寫出來的任何一份文檔進行操控。css

W3C聯盟對DOM的定義:一個與系統平臺和編程語言無關的接口,程序和腳本能夠經過這個接口動態的訪問和修改文檔的內容、結構和樣式。html

第二章 js語法

準備工做

程序語言分爲解釋型和編譯型兩大類前端

  1. 編譯型

Java、C++等語言須要一個編譯器(Compiler)。編譯器是一種程序,可以把用Java等解釋型高級語言編寫出來的源代碼翻譯爲直接在計算機上執行的文件。
編譯型的語言編寫的代碼若是有錯誤,這些錯誤會在代碼編譯階段就會被發現。與解釋型語言相比速度更快,可移植性更高,但學習曲線也更陡峭。java

  1. 解釋型

解釋型語言不須要編譯器,僅須要解釋器。對於JavaScript語言,在互聯網環境下Web瀏覽器負責完成有關的解釋和執行工做。瀏覽器中的JavaScript解釋器將直接讀入源代碼並執行。瀏覽器中若是沒有解釋器,JavaScript代碼就沒法執行。
解釋型語言編寫的代碼只能等到解釋器執行到有關代碼時才能被發現。node

語法

每行結尾並不必定須要分號,不過爲了更容易追蹤JavaScript腳本的執行順序仍是推介加上;
var不關心變量的類型,ex:web

var str="happy", str1 = 50;
str = 33;

字符串能夠包括在雙引號或者單引號裏,若是在引號中包括引號,那麼須要對這個字符進行轉義(escaping):編程

var str="about 5'10\" tall";
  • 數組聲明:
var beatles = ["John", "Paul", "George", "Ringo", true, 44, strA, [0, false, "my Dog", strB]];
var arr1 = Array();
var arr2 = [];
arr2["name"] = "John";
  • 對象聲明:
var lennon = Object();
lennon.name = "Smith";
var lennon1 = {name: "Ringo", age: 78};
  • 比較操做符:
var strC = false;
if (strC == "") {
    alert("xixi");        //會輸出
}

相等操做符 == 會認爲false與空字符串含義相同,若是要進行嚴格比較,則須要全等操做符 ===,全等操做符會執行嚴格的比較,不只會比較值,還會比較變量的類型,嚴格不相等也相似 !== 。ex:api

var strC = false;
if (strC === "") {
    alert("xixi");        //不會輸出
}

在if()或者while()的判斷條件中,整數中0被判斷爲false,除了0以外都爲true包括負數。數組

while (-1.1) {
    alert("memeda");      //會一直輸出
}

for循環:

var beatles = ["John", "Paul", "George", "Ringo", true, 44, [0, false, "my Dog", strB]];
for (var strE = 0; strE < beatles.length; strE++) {
    alert(beatles[strE]);
}

函數:

function shout(para) {
    var beatles = [para, 44];
    for (var strE = 0; strE < beatles.length; strE++) {
        alert(beatles[strE]);
    }
}
shout("xixixi");        //彈出2條alert: xixixi , 44

變量和函數的命名(爲了能一眼看出哪些是變量,哪些是函數):

  • 變量使用下劃線來分割各個單詞,如temp_celsius
  • 函數名使用首字母小寫的駝峯命名法,如convertToCelsius

內建對象(Native Object):

  • Array
  • Math
var num = 7.454;
num = Math.round(num);
alert(num);                    //輸出四捨五入結果:7
  • Date
var current_date=new Date();
var today=current_date.getDate();
alert(today);                  //輸出當天日期

etc...

第三章 DOM

DOM是針對XML可是通過擴展用於HTML的應用程序編程接口。DOM把整個頁面映射爲一個多層節點結構。

DOM中的D

D指的是document,當建立了一個網頁並將其加載到web瀏覽器中時,DOM已經被建立,它把編寫的網頁文檔轉換成一個文檔對象。

DOM中的O

O指的是對象object,js中的對象包括:

  • 用戶自定義對象(user-defined object):自行建立的對象
  • 內建對象(native object):內建在js中的對象,如Array、Math、Date
  • 宿主對象(host object):由瀏覽器提供的對象

最基礎的宿主對象:window對象:

window.open("http://www.baidu.com", "","", false);    //彈出百度窗口,也能夠是新窗口

DOM中的M

M指的是Model,有三種DOM方法能夠獲取元素節點,分別是經過ID,經過標籤名,和經過類名來獲取。

  • getElementById

返回一個有着給定ID屬性值的元素節點對應的對象,它是document對象特有的函數,而且形參是要獲取的id值,放在單引號或雙引號裏:

<h1 title="memeda" id="header_h1">Hello World!</h1>
<script>
    alert(document.getElementById("header_h1").getAttribute("title"));    //彈出 memeda
</script>
  • getElementsByTagName

返回一個對象數組,每一個對象對應着文檔裏有着給定標籤的一個元素,形參是標籤,且在引號內:

<ul>
    <li>Data1</li>
    <li>Data2</li>
    <li>Data3</li>
</ul>
<script>
    alert(document.getElementsByTagName("li").length);    //彈出 3
</script>
  • getElementsByClassName

返回具備Class名的對象數組,還能夠查找那些帶有多個類名的元素,並非全部瀏覽器都支持:

<ul>
    <li class="data_1 data_2">Data1</li>
    <li class="data_1">Data2</li>
    <li class="data_2">Data3</li>
</ul>
<script>
    alert(document.getElementsByClassName("data_1 data_2").length);    //彈出 1 ,只有一個元素既有data_1也有data_2
</script>

若是瀏覽器不必定支持,那麼可使用:

function getElementsByClassName(node, classname) {    //node是DOM樹中搜索起點,也能夠是document,不支持多個class搜索
    if (node.getElementsByClassName) {
        return node.getElementsByClassName(classname);
    } else {
        var results = [];
        var elems = node.getElementsByTagName("*");
        for (var i = 0; i < elems.length; i++) {
            if (elems[i].className.indexOf(classname) != -1) {
                results[results.length] = elems[i];
            }
        }
    }
}
  • getAttribute & setAttribute

能夠用來獲取和設置節點的屬性:

<h1 title="memeda" id="header_h1">Hello World!</h1>
<script>
    var shop=document.getElementById("header_h1");
    alert(shop.getAttribute("title"));
    shop.setAttribute("title","xixi");
    alert(shop.getAttribute("title"));
</script>

經過setAttribute對文檔做出修改後,經過查看源代碼發現文檔源代碼仍然是改變以前的值,也就是說,setAttribute做出的修改並不會反映到文檔自己的源代碼裏,緣由是DOM的工做模式:先加載文檔的靜態內容,再動態刷新,動態刷新不影響文檔的靜態內容
這正是DOM的真正威力:對頁面內容進行刷新卻不須要在瀏覽器裏刷新頁面。

第四章 js圖片庫

須要說明的是,若一個站點要用到多個js文件,爲了減小對站點的請求次數提升性能,應該把這些js文件合併到一個文件中。圖片同理。

實例:

<ul>
    <li><a href="../imgs/aaa.jpeg" onclick="showpic(this); return false;">Data1</a></li>
    <li><a href="../imgs/step.png" onclick="showpic(this); return false;">Data2</a></li>
    <li><a href="../imgs/arrow-right-bold.png" onclick="showpic(this); return false;">Data3</a></li>
    <li><a href="../imgs/dazongdianpin_logo.png" onclick="showpic(this); return false;">Data4</a></li>
</ul>
<img id="placeholder" src="../imgs/CD.jpg" alt="My image gallery">

<script>
    function showpic(whichpic) {
        document.getElementById("placeholder").setAttribute("src", whichpic.getAttribute("href"));
    }
</script>

實現一個在點擊a標籤時把placeholder佔位圖片替換成a標籤對應的圖片,並攔截點擊a時網頁的默認行爲。
另外,setAttribute方法是DOM Level1 的內容,它能夠設置任意元素節點的任意屬性,在DOM Level1出現以前的HTML-DOM還能夠經過:element.value="value"來直接設置元素的屬性,不過不推介使用這種方式,這種方式只適用於web文檔,而DOM則適用於任何一種標記語言,由於「DOM是一種適用於多種環境和多種程序設計語言的通用型API」,好比xml,爲了更好的移植性,嚴格遵照DOM Level1能夠避免不少問題。

  • nodeType屬性能夠獲得任何節點的節點屬性:

    • 元素節點的nodeType值爲1
    • 屬性節點的nodeType值爲2
    • 文本節點的nodeType值爲3
  • nodeValue屬性:

能夠用來檢索節點的值,也能夠用來設置節點的值。

<ul>
    <li><a href="#" onclick="showa(this);return false;">xixi1</a></li>
    <li><a href="#" onclick="showa(this);return false;">xixi2</a></li>
    <li><a href="#" onclick="showa(this);return false;">xixi3</a></li>
</ul>
<p id="description">Hello</p>
<script>
    function showa(whicha) {
        document.getElementById("description").firstChild.nodeValue = whicha.firstChild.nodeValue;
    }
</script>

第五章 最佳實踐

平穩退化

平穩退化(graceful degradation)確保網頁在沒有js的狀況下也能正常工做;就是說,雖然某些功能沒法使用,但最基本的操做仍能順利實現。

<a href="http://www.baidu.com/" onclick="openw(this.href);return false;">Example</a>
<script>
    function openw(urlStr) {
        window.open(urlStr, "ppp", "width=400,height=600");
    }
</script>

這種方式比JavaScript僞協議:href="javascript:..."href="#" onclick=「...」,方式效果要好得多,即便在js功能已被禁用或者遇到爬蟲的狀況下,連接也是可用的,雖然功能上打了折扣,可是並無完全失效。

漸進加強

漸進加強(progressive enhancement)原則基於這樣一種思想:實現良好的結構應該從最核心的部分,也就是從內容開始。根據內容使用標記實現良好的結構;而後再逐步增強這些內容,這些加強工做能夠是經過css改進呈現效果,也能夠經過DOM添加各類行爲,若是使用DOM來添加核心內容,那麼這是不推介的,而且也不利於網站SEO,js也沒有任何空間去平穩退化,那些缺少js支持的訪問者就沒法看到其內容。

向後兼容

確保老版本的瀏覽器不會由於你的js而掛掉;
可使用對象檢測(object detection):

window.onload = function () {
    if (!document.getElementsByTagName()) return false;
}

檢測瀏覽器是否支持這一方法,來避免老版本瀏覽器不會由於新加入的js出錯。

分離js

把網頁的結構和內容與js腳本的動做行爲分開;

性能考慮

確保腳本執行的性能最優;

  • 避免搜索浪費
if(document.getElementsByTagName("a").length>0){        //不推介
    var links=document.getElementsByTagName("a");
    for(var i=0;i<links.length;i++){        
    }
}

這樣會進行兩次.getElementsByTagName()搜索,形成性能浪費,所以不推介。若是有多個函數重複作一件事,那麼能夠把搜索結果放在全局變量中,或者把一組元素直接以參數形式傳遞給函數,以避免形成搜索浪費。

  • 合併與放置腳本

<head>部分的腳本會致使瀏覽器沒法並行加載其餘文件,所以推介在文檔末尾</body>以前引入script元素,可讓頁面變得更快;根據HTTP規範,瀏覽器每次從同一個域名最多隻能同時下載兩個文件,而在下載腳本期間,瀏覽器不會下載其餘任何文件,即便是來自不一樣域名的文件也不會下載,全部其餘資源要等腳本加載完畢後才能下載。

  • 壓縮腳本

可使用壓縮工具來刪除腳本文件中沒必要要的字節,好比空格和註釋,有的壓縮工具設置會重寫部分代碼,使用更短的變量名,從而減小總體文件大小。精簡後的代碼不易看懂,但會大幅減少文件大小,通常會在壓縮後的文件名後加上.min.,好比 scriptName.min.js

第六章 案例:圖片庫改進

ex:

<ul id="imagegallery">
    <!--平穩退化-->
    <li><a href="../imgs/aaa.jpeg">Data1</a></li>
    <li><a href="../imgs/step.png">Data2</a></li>
    <li><a href="../imgs/arrow-right-bold.png">Data3</a></li>
    <li><a href="../imgs/dazongdianpin_logo.png">Data4</a></li>
</ul>
<img id="placeholder" src="../imgs/CD.jpg" alt="My image gallery">

<script>
    addLoadEvent(prepareGallery);
    function prepareGallery() {
        if (!document.getElementById) alert("1");
        if (!document.getElementsByTagName) alert("2");               //檢查瀏覽器是否支持這個DOM方法
        if (!document.getElementById("imagegallery")) alert("3");     //即便在網頁上刪除了這個img或者id,js也不會出錯
        
        var gallery = document.getElementById("imagegallery");
        var links = gallery.getElementsByTagName("a");
        for (var i = 0; i < links.length; i++) {
            links[i].onclick = function () {
                return !showpic(this);                                //由showpic返回值決定是否取消瀏覽器執行連接被點擊時的默認操做
            }
        }
    }
    function showpic(whichpic) {
        if (!document.getElementById("placeholder")) return false;
        
        whichpic.getAttribute("href").setAttribute("src", whichpic.getAttribute("href"));
        return true;                                                  //setAttribute成功纔會返回true
    }
    function addLoadEvent(func) {                                     //將函數添加到頁面加載時執行
        var oldonload = window.onload;
        if (typeof window.onload != 'function') {
            window.onload = func;
        } else {
            window.onload = function () {
                oldonload();
                func();
            }
        }
    }
</script>

第七章 動態建立標記

傳統方法

document.write能夠方便的把字符串插入文檔(不推介)

<div id="test-div"></div>
<script>
    document.write("<p>This is a <em>content</em> script!;</p>");
</script>

innerHTML方法會替換元素內的全部內容。

<div id="test-div"></div>
<script>
    window.onload = function () {
        var testdiv = document.getElementById("test-div");
        testdiv.innerHTML = "<p>This is a <em>content</em> script;</p>"
    }
</script>

DOM方法

插入節點的子元素後

<div id="test-div"></div>
<script>
    var newE = document.createElement("p");                        //建立新元素節點,不可使用.firstChild.nodeValue賦值,由於建立的標籤的nodeValue是null
    var newT = document.createTextNode("hello world~");            //建立新文本節點
    newE.appendChild(newT);                                        //把文本節點插入元素節點的最後面
    document.getElementById("test-div").appendChild(newE);         //把元素節點插入原有節點的最後面
</script>

插入元素後:

var place = document.createElement("p");
place.appendChild(document.createTextNode("百度"));
var tar = document.getElementById("tar");

tar.parentNode.insertBefore(place, tar);

插入元素前:

var place = document.createElement("p");
place.appendChild(document.createTextNode("百度"));
var tar = document.getElementById("tar");

insertAfter(place, hhh);

function insertAfter(newElement, targetElement) {       //插入目標元素後
    var parent = targetElement.parentNode;
    if (parent.lastChild === targetElement) {
        parent.appendChild(newElement);
    } else {
        parent.insertBefore(newElement, targetElement.nextElementSibling);
    }
}

Ajax

Ajax的主要優點是對頁面的請求以異步的方式發送到服務器。而服務器不會用整個頁面來響應請求,它會在後臺處理請求,與此同時用戶還能繼續瀏覽頁面並與頁面交互腳本則能夠按需加載和建立頁面內容,而不會打斷用戶的瀏覽體驗。
Ajax的核心是XMLHttpRequest對象,這個對象充當瀏覽器中的客戶端與服務器之間的橋樑角色,以往的請求由瀏覽器發出,而js經過這個對象能夠本身發送請求,同事也本身處理響應。

<div id="new"></div>

<script>
    function getNewContent() {
        var request = new getHTTPObject();
        if (request) {
            request.open("GET", "test.txt", true);
            request.onreadystatechange = function () {
                if (request.readyState === 4) {
                    var para = document.createElement("p");
                    var txt = document.createTextNode(request.responseText);
                    para.appendChild(txt);
                    document.getElementById("new").appendChild(para);
                }
            };
            request.send(null);
        } else {
            alert('sorry, your browser doesn\'t support XMLHttpRequest');
        }
        alert("func done!");
    }

    addLoadEvent(getNewContent);

    function addLoadEvent(func) {                                   //  將函數添加到頁面加載時執行
        var oldonload = window.onload;
        if (typeof window.onload !== 'function') {
            window.onload = func;
        } else {
            window.onload = function () {
                oldonload();
                func();
            };
        }
    }

    function getHTTPObject() {
        if (typeof XMLHttpRequest === "undefined")
            XMLHttpRequest = function () {
                try {return new ActiveXObject("Msxml2.XMLHTTP.6.0");}
                catch (e) {}
                try {return new ActiveXObject("Msxml2.XMLHTTP.3.0");}
                catch (e) {}
                try {return new ActiveXObject("Msxml2.XMLHTTP");}
                catch (e) {}
                return false;
            };
            return new XMLHttpRequest();
    }
</script>

第八章 充實文檔內容

js腳本只應該用來充實文檔的內容,而避免使用DOM來建立核心內容;
遍歷快捷鍵,ex:

<ul id="navigation">
    <li><a href="#" accesskey="1">home</a></li>
    <li><a href="#" accesskey="2">contact</a></li>
    <li><a href="#" accesskey="3">search</a></li>
</ul>
<script>
    function displayAccesskeys() {
        if (!document.getElementsByTagName) return false;
        var links = document.getElementsByTagName("a");
        var tags = [];
        if (links.length < 1) return false;
        for (var i = 0; i < links.length; i++) {
            if (!links[i].getAttribute("accesskey")) continue;
            var source = links[i].lastChild.nodeValue;
            var key = links[i].getAttribute("accesskey");
            tags[key] = source;
        }
        var list = document.createElement("ul");
        for (key in tags) {
            var txt = key + " : " + tags[key];
            var li = document.createElement("li");
            var li_txt = document.createTextNode(txt);
            li.appendChild(li_txt);
            list.appendChild(li);
        }
        insertAfter(list, document.getElementById("navigation"));
    }
    addLoadEvent(displayAccesskeys);
    function addLoadEvent(func) {                           //將函數添加到頁面加載時執行
        var oldonload = window.onload;
        if (typeof window.onload !== 'function') {
            window.onload = func;
        } else {
            window.onload = function () {
                oldonload();
                func();
            };
        }
    }
    function insertAfter(newElement, targetElement) {       //插入目標元素後
        var parent = targetElement.parentNode;
        if (parent.lastChild === targetElement) {
            parent.appendChild(newElement);
        } else {
            parent.insertBefore(newElement, targetElement.nextElementSibling);
        }
    }
</script>

PS:歡迎你們關注個人公衆號【前端下午茶】,一塊兒加油吧~

另外能夠加入「前端下午茶交流羣」微信羣,長按識別下面二維碼便可加我好友,備註加羣,我拉你入羣~

相關文章
相關標籤/搜索