在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ JavaScript 與 DOM(下)
代碼復用模式(避免篇)
S.O.L.I.D 五大原則之接口隔離原則 ISP
設(shè)計模式之狀態(tài)模式
JavaScript 核心(晉級高手必讀篇)
設(shè)計模式之建造者模式
JavaScript 與 DOM(上)——也適用于新手
設(shè)計模式之中介者模式
設(shè)計模式之裝飾者模式
設(shè)計模式之模板方法
設(shè)計模式之外觀模式
強大的原型和原型鏈
設(shè)計模式之構(gòu)造函數(shù)模式
揭秘命名函數(shù)表達式
深入理解J avaScript 系列(結(jié)局篇)
執(zhí)行上下文(Execution Contexts)
函數(shù)(Functions)
《你真懂 JavaScript 嗎?》答案詳解
設(shè)計模式之適配器模式
設(shè)計模式之組合模式
設(shè)計模式之命令模式
S.O.L.I.D 五大原則之單一職責 SRP
編寫高質(zhì)量 JavaScript 代碼的基本要點
求值策略
閉包(Closures)
對象創(chuàng)建模式(上篇)
This? Yes,this!
設(shè)計模式之代理模式
變量對象(Variable Object)
S.O.L.I.D 五大原則之里氏替換原則 LSP
面向?qū)ο缶幊讨话憷碚?/span>
設(shè)計模式之單例模式
Function 模式(上篇)
S.O.L.I.D 五大原則之依賴倒置原則 DIP
設(shè)計模式之迭代器模式
立即調(diào)用的函數(shù)表達式
設(shè)計模式之享元模式
設(shè)計模式之原型模式
根本沒有“JSON 對象”這回事!
JavaScript 與 DOM(下)
面向?qū)ο缶幊讨?ECMAScript 實現(xiàn)
全面解析 Module 模式
對象創(chuàng)建模式(下篇)
設(shè)計模式之職責鏈模式
S.O.L.I.D 五大原則之開閉原則 OCP
設(shè)計模式之橋接模式
設(shè)計模式之策略模式
設(shè)計模式之觀察者模式
代碼復用模式(推薦篇)
作用域鏈(Scope Chain)
Function 模式(下篇)
設(shè)計模式之工廠模式

JavaScript 與 DOM(下)

介紹

上一章我們介紹了 JavaScript 的基本內(nèi)容和 DOM 對象的各個方面,包括如何訪問 node 節(jié)點。本章我們將講解如何通過 DOM 操作元素并且討論瀏覽器事件模型。

操作元素

上一章節(jié)我們提到了 DOM 節(jié)點集合或單個節(jié)點的訪問步驟,每個 DOM 節(jié)點都包括一個屬性集合,大多數(shù)的屬性都提供為相應的功能提供了抽象。例如,如果有一個帶有 ID 屬性 intro 的文本元素,你可以很容易地通過 DOM API 來改變該元素的顏色:

document.getElementById('intro').style.color = '#FF0000';

為了理解這個 API 的功能,我們一步一步分開來看就非常容易理解了:

var myDocument = document;  
var myIntro = myDocument.getElementById('intro');  
var myIntroStyles = myIntro.style;  
// 現(xiàn)在,我們可以設(shè)置顏色了:  
myIntroStyles.color = '#FF0000';

現(xiàn)在,我們有了該文本的 style 對象的引用了,所以我們可以添加其它的 CSS 樣式:

myIntroStyles.padding = '2px 3px 0 3px';  
myIntroStyles.backgroundColor = '#FFF';  
myIntroStyles.marginTop = '20px'; 

這里我們只是要了基本的 CSS 屬性名稱,唯一區(qū)別是 CSS 屬性的名稱如果帶有-的話,就需要去除,比如用 marginTop 代替 margin-top。例如,下面的代碼是不工作的,并且會拋出語法錯誤:

myIntroStyles.padding-top = '10em';     
// 產(chǎn)生語法錯誤:
// 在JavaScript里橫線-是減法操作符
// 而且也沒有這樣的屬性名稱

屬性可以像數(shù)組一樣訪問,所以利用這個知識我們可以創(chuàng)建一個函數(shù)來改變?nèi)魏谓o定元素的樣式:

function changeStyle(elem, property, val) {
    elem.style[property] = val; // 使用[]來訪問屬性
}
// 使用上述的函數(shù):  
var myIntro = document.getElementById('intro'); // 獲取intro文本對象
changeStyle(myIntro, 'color', 'red');  

這僅僅是個例子,所以該函數(shù)也許沒什么用,語法上來說,直接用還是會快點,例如(elem.style.color = ‘red’)。除了 style 屬性以外,一個節(jié)點(或元素)也還有其他很多屬性可以操作,如果你使用 Firebug,點擊 DOM 選項卡可以看到所有該節(jié)點(或元素)的所有屬性:

http://wiki.jikexueyuan.com/project/javascript-depth-understanding/images/15.png" alt="" />

所有的屬性都可以通過點標示符來訪問(例如:Element.tabIndex)。不是所有的屬性都是原始數(shù)據(jù)類型(strings,numbers,Booleans 等等),sytle 屬性也是一個包含自己屬性的對象,很多元素的屬性都是只讀的,也就是說不能修改他們的值。例如,你不能直接修改一個節(jié)點的 parentNode 屬性,如果你修改只讀屬性的時候瀏覽器會拋出錯誤:例如,拋出錯誤“setting a property that has only a getter”,只是我們需要注意的。

通常 DOM 操作都是改變原始的內(nèi)容,這里有幾種方式來實現(xiàn)這個,最簡單的是使用 innerHTML 屬性,例如:

var myIntro = document.getElementById('intro');  
// 替換當前的內(nèi)容
myIntro.innerHTML = 'New content for the <strong>amazing</strong> paragraph!';  
// 添加內(nèi)容到當前的內(nèi)容里 
myIntro.innerHTML += '... some more content...';

唯一的問題是該方法沒在規(guī)范里定義,而且在 DOM 規(guī)范里也沒有定義,如果你不反感的話請繼續(xù)使用,因為它比我們下面要討論其它的方法快多了。

Node 節(jié)點

通過 DOM API 創(chuàng)建內(nèi)容的時候需要注意 node 節(jié)點的 2 種類型,一種是元素節(jié)點,一種是 text 節(jié)點,上一章節(jié)已經(jīng)列出了所有的節(jié)點類型,這兩種需要我們現(xiàn)在特別注意。創(chuàng)建元素可以通過 createElement 方法,而創(chuàng)建 text 節(jié)點可以使用 createTextNode,相應代碼如下:

var myIntro = document.getElementById('intro');  
// 添加內(nèi)容
var someText = 'This is the text I want to add';  
var textNode = document.createTextNode(someText);  
myIntro.appendChild(textNode);

這里我們使用了 appendChild 方法將新 text 節(jié)點附件到文本字段,這樣做比非標準的 innerHTML 方法顯得有點長,但了解這些原理依然很重要,這里有一個使用 DOM 方法的更詳細例子:

var myIntro = document.getElementById('intro');  
// 添加新連接到文本節(jié)點
// 首先,創(chuàng)建新連接元素
var myNewLink = document.createElement('a'); // <a/>  
myNewLink.; // <a />  
myNewLink.appendChild(document.createTextNode('Visit Google')); 
// <a >Visit Google</a>  
// 將內(nèi)容附件到文本節(jié)點
myIntro.appendChild(myNewLink);

另外 DOM 里還有一個 insertBefore 方法用于再節(jié)點前面附件內(nèi)容,通過 insertBefore 和 appendChild 我們可以實現(xiàn)自己的 insertAfter 函數(shù):

// 'Target'是DOM里已經(jīng)存在的元素
// 'Bullet'是要插入的新元素
function insertAfter(target, bullet) {  
    target.nextSibling ?  
        target.parentNode.insertBefore(bullet, target.nextSibling)  
        : target.parentNode.appendChild(bullet);  
}  
// 使用了3目表達式:  
// 格式:條件?條件為true時的表達式:條件為false時的表達式

上面的函數(shù)首先檢查 target 元素的同級下一個節(jié)點是否存在,如果存在就在該節(jié)點前面添加 bullet 節(jié)點,如果不存在,就說明 target 是最后一個節(jié)點了,直接在后面 append 新節(jié)點就可以了。DOM API 沒有給提供 insertAfter 是因為真的沒必要了——我們可以自己創(chuàng)建。

DOM 操作有很多內(nèi)容,上面你看到的只是其中一部分。

Event 事件

瀏覽器事件是所有 web 程序的核心,通過這些事件我們定義將要發(fā)生的行為,如果在頁面里有個按鈕,那點擊此按鈕之前你需要驗證表單是否合法,這時候就可以使用 click 事件,下面列出的最標準的事件列表:

注:正如我們上章所說的,DOM 和 JavaScript 語言是 2 個單獨的東西,瀏覽器事件是 DOM API 的一部分,而不是 JavaScript 的一部分。

鼠標事件

  1. ‘mousedown’ – 鼠標設(shè)備按下一個元素的時候觸發(fā) mousedown 事件。
  2. ‘mouseup’ – 鼠標設(shè)備從按下的元素上彈起的時候觸發(fā) mouseup 事件。
  3. ‘click’ – 鼠標點擊元素的時候觸發(fā) click 事件。
  4. ‘dblclick’ – 鼠標雙擊元素的時候觸發(fā) dblclick 事件。
  5. ‘mouseover’ – 鼠標移動到某元素上的時候觸發(fā) mouseover 事件。
  6. ‘mouseout’ – 鼠標從某元素離開的時候觸發(fā) mouseout 事件。
  7. ‘mousemove’ – 鼠標在某元素上移動但未離開的時候觸發(fā) mousemove 事件。

鍵盤事件

  1. ‘keypress’ – 按鍵按下的時候觸發(fā)該事件。
  2. ‘keydown’ – 按鍵按下的時候觸發(fā)該事件,并且在 keypress 事件之前。
  3. ‘keyup’ – 按鍵松開的時候觸發(fā)該事件,在 keydown 和 keypress 事件之后。

表單事件

  1. ‘select’ – 文本字段(input, textarea等)的文本被選擇的時候觸發(fā)該事件。
  2. ‘change’ – 控件失去 input 焦點的時候觸發(fā)該事件(或者值被改變的時候)。
  3. ‘submit’ – 表單提交的時候觸發(fā)該事件。
  4. ‘reset’ – 表單重置的時候觸發(fā)該事件。
  5. ‘focus’ – 元素獲得焦點的時候觸發(fā)該事件,通常來自鼠標設(shè)備或 Tab 導航。
  6. ‘blur’ – 元素失去焦點的時候觸發(fā)該事件,通常來自鼠標設(shè)備或 Tab 導航。

其它事件

  1. ‘load’ – 頁面加載完畢(包括內(nèi)容、圖片、frame、object)的時候觸發(fā)該事件。
  2. ‘resize’ – 頁面大小改變的時候觸發(fā)該事件(例如瀏覽器縮放)。
  3. ‘scroll’ – 頁面滾動的時候觸發(fā)該事件。
  4. ‘unload’ – 從頁面或 frame 刪除所有內(nèi)容的時候觸發(fā)該事件(例如離開一個頁面)。

還有很多各種各樣的事件,上面展示的事件是我們在 JavaScript 里最常用的事件,有些事件在跨瀏覽器方面可能有所不同。還有其它瀏覽器實現(xiàn)的一些屬性事件,例如 Gecko 實現(xiàn)的 DOMContentLoaded 或 DOMMouseScroll 等,Gecko 的詳細事件列表請查看這里。

事件處理

我們將了事件,但是還沒有將到如何將處理函數(shù)和事件管理起來,使用這些事件之前,你首先要注冊這些事件句柄,然后描述該事件發(fā)生的時候該如何處理,下面的例子展示了一個基本的事件注冊模型:

基本事件注冊:

<!-- HTML -->  
<button id="my-button">Click me!</button>  
// JavaScript:  
var myElement = document.getElementById('my-button');
// 事件處理句柄:  
function buttonClick() {
    alert('You just clicked the button!');
}
// 注冊事件
myElement.onclick = buttonClick; 

使用 document.getElementById 命令,通過 ID=my-button 獲取該 button 對象,然后創(chuàng)建一個處理函數(shù),隨后將該函數(shù)賦值給該 DOM 的 onclick 屬性。就這么簡單!

基本事件注冊是非常簡單的,在事件名稱前面添加前綴 on 作為 DOM 的屬性就可以使用了,這是事件處理的基本核心,但下面的代碼我不推薦使用:

<button onclick="return buttonClick()">Click me!</button>

上述 Inline 的事件處理方式不利用頁面維護,建議將這些處理函數(shù)都封裝在單獨的 js 文件,原因和CSS樣式的一樣的。

高級事件注冊:

別被標題迷惑了,“高級”不意味著好用,實際上上面討論的基本事件注冊是我們大部分時候用的方式,但有一個限制:不能綁定多個處理函數(shù)到一個事件上。這也是我們要講解該小節(jié)原因:

該模型運行你綁定多個處理句柄到一個事件上,也就是說一個事件觸發(fā)的時候多個函數(shù)都可以執(zhí)行,另外,該模型也可以讓你很容易里刪除某個已經(jīng)綁定的句柄。

嚴格來說,有 2 種不同的模型:W3C 模型和微軟模型,除 IE 之外 W3C 模型支持所有的現(xiàn)代瀏覽器,而微軟模型只支持 IE,使用 W3C 模型的代碼如下:

// 格式:target.addEventListener( type, function, useCapture );  
// 例子:  
var myIntro = document.getElementById('intro');
myIntro.addEventListener('click', introClick, false);

使用 IE 模型的代碼如下:

// 格式: target.attachEvent ( 'on' + type, function );  
// 例子:  
var myIntro = document.getElementById('intro');
myIntro.attachEvent('onclick', introClick);

introClick 的代碼如下:

function introClick() {  
    alert('You clicked the paragraph!');  
}

事實上,要做出通用的話,我們可以自定義一個函數(shù)以支持跨瀏覽器:

function addEvent(elem, type, fn) {
    if (elem.attachEvent) {
        elem.attachEvent('on' + type, fn);
        return;
    }
    if (elem.addEventListener) {
        elem.addEventListener(type, fn, false);
    }
}

該函數(shù)首先檢查 attachEvent 和 addEventListener 屬性,誰可以就用誰,這兩種類型的模型都支持刪除句柄功能,參考下面的 removeEvent 函數(shù)。

function removeEvent(elem, type, fn) {
    if (elem.detachEvent) {
        elem.detachEvent('on' + type, fn);
        return;
    }
    if (elem.removeEventListener) {
        elem.removeEventListener(type, fn, false);
    }
}

你可以這樣使用:

var myIntro = document.getElementById('intro');
addEvent(myIntro, 'click', function () {
    alert('YOU CLICKED ME!!!');
});

注意到我們傳入了一個匿名函數(shù)作為第三個參數(shù),JavaScript 運行我們定義和執(zhí)行匿名函數(shù),這種匿名函數(shù)特別適合作為參數(shù)傳遞,實際上我們也可以傳遞有名的函數(shù)(代碼如下),但是你們函數(shù)更容易做。

如果你只想在第一次 click 的時候觸發(fā)一個函數(shù),你可以這么做:

// 注意:前提是我們已經(jīng)定于好了addEvent/removeEvent函數(shù)
// (定義好了才能使用哦)  
var myIntro = document.getElementById('intro');
addEvent(myIntro, 'click', oneClickOnly);
function oneClickOnly() {
    alert('WOW!');
    removeEvent(myIntro, 'click', oneClickOnly);
} 

當?shù)谝淮斡|發(fā)以后,我們就立即刪除該句柄,但是有匿名函數(shù)的話卻很難將自身的引用刪除,不過實際上可以通過如下的形式來做(只不過有點麻煩):

addEvent(myIntro, 'click', function () {
    alert('WOW!');
    removeEvent(myIntro, 'click', arguments.callee);
});

這里我們是有了 arguments 對象的 callee 屬性,arguments 對象包含了所有傳遞進來的參數(shù)以及該函數(shù)自身(callee),這樣我們就可以放心地刪除自身的引用了。

關(guān)于 W3C 和微軟模型還有其他的少許差異,比如 this,在觸發(fā)事件的時候函數(shù)中的 this 一般都是該元素上下文,,也就說 this 引用該元素自身,在基本事件注冊和 W3C 模型中都沒有問題,但在微軟模型的實現(xiàn)里卻可能出錯,請參考如下代碼:

function myEventHandler() {
    this.style.display = 'none';
}
// 正常工作,this是代表該元素
myIntro.onclick = myEventHandler;
// 正常工作,this是代表該元素
myIntro.addEventListener('click', myEventHandler, false);
// 不正常,這時候的this是代表Window對象
myIntro.attachEvent('onclick', myEventHandler);

這里有一些方式可以避免這個問題,最簡單的方式是使用前面的基本事件注冊方式,或者是再做一個通用的 addEvent,通用代碼請參考 John ResigDean Edward 的文章。

Event 對象

另外一個非常重要的內(nèi)容是 Event 對象,當事件發(fā)生的時候出發(fā)某個函數(shù),該 Event 對象將自動在函數(shù)內(nèi)可用,該對象包含了很多事件觸發(fā)時候的信息,但IE卻沒有這么實現(xiàn),而是自己實現(xiàn)的,IE 瀏覽器是通過全局對象 window 下的 event 屬性來包含這些信息,雖然不是大問題,但我們也需要注意一下,下面的代碼是兼容性的:

function myEventHandler(e) {
    // 注意參數(shù)e
    // 該函數(shù)調(diào)用的時候e是event對象(W3C實現(xiàn))
    // 兼容IE的代碼
    e = e || window.event;
    // 現(xiàn)在e就可以兼容各種瀏覽器了
}
// 這里可以自由地綁定事件了

這里判斷 e 對象(Event 對象)是否存在我們使用了 OR 操作符:如果 e 不存在(為 null,undefined,0 等)的時候,將 window.event 賦值給 e,否則的話繼續(xù)使用 e。通過這方式很快就能在多瀏覽器里得到真正的 Event 對象,如果你不喜歡這種方式的話,你可以使用 if 語句來處理:

if (!e) {
    e = window.event;
} // 沒有else語句,因為e在其它瀏覽器已經(jīng)定義了

另外 Event 對象下的命令和屬性都很有用,遺憾的是不不能全兼容瀏覽器,例如當你想取消默認的行為的時候你可以使用 Event 對象里的 preventDefault()方法,但 IE 里不得不使用對象的 returnValue 屬性值來控制,兼容代碼如下:

function myEventHandler(e) {
    e = e || window.event;
    // 防止默認行為
    if (e.preventDefault) {
        e.preventDefault();
    } else {
        e.returnValue = false;
    }
}

例如,當你點擊一個連接的時候,默認行為是導航到 href 里定義的地址,但有時候你想禁用這個默認行為,通過 returnValue 和 preventDefault 就可以實現(xiàn),Event 對象里的很多屬性在瀏覽器里都不兼容,所以很多時候需要處理這些兼容性代碼。

注意:現(xiàn)在很多 JS 類庫都已經(jīng)封裝好了 e.preventDefault 代碼,也就是說在 IE 里可用了,但是原理上依然是使用 returnValue 來實現(xiàn)的。

事件冒泡

事件冒泡,就是事件觸發(fā)的時候通過 DOM 向上冒泡,首先要知道不是所有的事件都有冒泡。事件在一個目標元素上觸發(fā)的時候,該事件將觸發(fā)一一觸發(fā)祖先節(jié)點元素,直到最頂層的元素:

http://wiki.jikexueyuan.com/project/javascript-depth-understanding/images/16.png" alt="" />

如圖所示,如果 a 連接被點擊,觸發(fā)觸發(fā)連接的 click 事件,然后觸發(fā) p 的 click 事件,以此再觸發(fā) div 和 body 的 click 事件。順序不變,而且不一定是在同時觸發(fā)的。

這樣你就可以利用該特性去處理自己的邏輯了,并且再任何時候都可以停止冒泡,比如,如果你只想冒泡到文本節(jié)點上,而不再進一步冒泡,你可以在 p 的 click 事件處理函數(shù)里丁停止冒泡:

function myParagraphEventHandler(e) {
    e = e || window.event;
    // 停止向上冒泡
    if (e.stopPropagation) {
        // W3C實現(xiàn)  
        e.stopPropagation();
    } else {
        // IE實現(xiàn)  
        e.cancelBubble = true;
    }
}
// 使用我們自定義的addEvent函數(shù)將myParagraphEventHandler綁定到click事件上:  
addEvent(document.getElementsByTagName('p')[0], 'click', myParagraphEventHandler);

事件委托

舉例來說,如果你有一個很多行的大表格,在每個 <tr> 上綁定點擊事件是個非常危險的想法,因為性能是個大問題。流行的做法是使用事件委托。事件委托描述的是將事件綁定在容器元素上,然后通過判斷點擊的 target 子元素的類型來觸發(fā)相應的事件。

var myTable = document.getElementById('my-table');
myTable.onclick = function () {
    // 處理瀏覽器兼容
    e = e || window.event;
    var targetNode = e.target || e.srcElement;
    // 測試如果點擊的是TR就觸發(fā)
    if (targetNode.nodeName.toLowerCase() === 'tr') {
        alert('You clicked a table row!');
    }
}

事件委托依賴于事件冒泡,如果事件冒泡到 table 之前被禁用的話,那上面的代碼就無法工作了。

總結(jié)

本章我們覆蓋到了 DOM 元素的操作以及相關(guān)的瀏覽器事件模型,希望大家能對 DOM 有了進一步的了解。