1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        深入理解JavaScript與DOM

        共 16567字,需瀏覽 34分鐘

         ·

        2021-03-22 10:16

        作者 | 湯姆大叔


        文檔對(duì)象模型Document Object Model

        DOM(Document Object Model,文檔對(duì)象模型)是一個(gè)通過和JavaScript進(jìn)行內(nèi)容交互的API。Javascript和DOM一般經(jīng)常作為一個(gè)整體,因?yàn)镴avascript通常都是用來進(jìn)行DOM操作和交互的。

        主要內(nèi)容來自:http://net.tutsplus.com/tutorials/javascript-ajax/javascript-and-the-dom-series-lesson-1/

        關(guān)于DOM,有些知識(shí)需要注意:
        1. window對(duì)象作為全局對(duì)象,也就是說你可以通過window來訪問全局對(duì)象。

        1. 屬性在對(duì)象下面以變量的形式存放,在頁面上創(chuàng)建的所有全局對(duì)象都會(huì)變成window對(duì)象的屬性。

        2. 方法在對(duì)象下面以函數(shù)的形式存放,因?yàn)樽笥业暮瘮?shù)都存放在window對(duì)象下面,所以他們也可以稱為方法。

        2. DOM為web文檔創(chuàng)建帶有層級(jí)的結(jié)果,這些層級(jí)是通過node節(jié)點(diǎn)組成,這里有幾種DOM node類型,最重要的是Element, Text, Document。

        1. Element節(jié)點(diǎn)在頁面里展示的是一個(gè)元素,所以如果你有段落元素(),你可以通過這個(gè)DOM節(jié)點(diǎn)來訪問。

        2. Text節(jié)點(diǎn)在頁面里展示的所有文本相關(guān)的元素,所以如果你的段落有文本在里面的話,你可以直接通過DOM的Text節(jié)點(diǎn)來訪問這個(gè)文本

        3. Document節(jié)點(diǎn)代表是整個(gè)文檔,它是DOM的根節(jié)點(diǎn)。

        3. 每個(gè)引擎對(duì)DOM標(biāo)準(zhǔn)的實(shí)現(xiàn)有一些輕微的不同。例如,F(xiàn)irefox瀏覽器使用的Gecko引擎有著很好的實(shí)現(xiàn)(盡管沒有完全遵守W3C規(guī)范),但I(xiàn)E瀏覽器使用的Trident引擎的實(shí)現(xiàn)卻不完整而且還有bug,給開發(fā)人言帶來了很多問題。

        如果你正在使用Firefox,我推薦你立即下載Firebug插件,對(duì)于你了解DOM結(jié)構(gòu)非常有用。

        Web上的JavaScript

        Script元素

        當(dāng)你在網(wǎng)站頁面上使用JavaScript的時(shí)候,需要使用SCRIPT元素:

        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">      <html xmlns="http://www.w3.org/1999/xhtml" lang="en">          <head>                  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />                  <title>JavaScript!</title>          </head>          <body>                    <script type="text/javascript">              // <![CDATA[  
        // ]]> </script> </body> </html>
        上述代碼,嚴(yán)格來說SCRIPT的TYPE屬性應(yīng)該設(shè)置為application/javascript,但是由于IE不支持這個(gè),所以平時(shí)我們不得不寫成text/javascript或者直接去掉type。另外你也可以看到在SCRIPT元素里的注釋行// ,瀏覽器就不會(huì)再解析成XHTML標(biāo)簽了。

        Defer屬性

        任何在SCRIPT元素里聲明的代碼在頁面加載的時(shí)候都會(huì)運(yùn)行,唯一一個(gè)例外是給SCRIPT元素加上一個(gè)defer屬性。defer屬性告訴瀏覽器加載完HTML文檔以后再執(zhí)行JS代碼,但這個(gè)屬性只能在IE下使用。

        連接外部腳本

        如果你想了解外部腳本,只需要簡(jiǎn)單地在SCRIPT上使用SRC屬性就行了,使用單獨(dú)的JS文件的好處是可以緩存,而且也不需要擔(dān)心CDATA方面的問題:
        <script type="text/javascript" src="my-script.js"></script>

        JavaScript必備

        在我們繼續(xù)DOM之前,我們來復(fù)習(xí)一下JavaScript的核心必備知識(shí),如果你還不了解,也沒關(guān)系,我們?cè)谶@一章節(jié)將稍微花點(diǎn)時(shí)間來回顧一下。

        JavaScript有幾種數(shù)據(jù)類型:Number, String, Boolean, Object, Undefined and Null。

        單行注釋使用雙斜杠//,雙斜杠后面的所有文字都會(huì)被注釋掉,多行注意使用/和/括住。

        Number

        在JavaScript里所有的Number都是浮點(diǎn)型的,當(dāng)聲明一個(gè)數(shù)字變量的時(shí)候,記得不要使用任何引號(hào)。

        // 注:使用var類聲明變量var leftSide = 100;  var topSide = 50;  var areaOfRectangle = leftSide * topSide; // = 5000

        String

        JavaScript里聲明字符串特別簡(jiǎn)單,和其它語言一樣,在JS里使用單引號(hào)或雙引號(hào)都可以。

        var firstPart = 'Hello';  var secondPart = 'World!';  var allOfIt = firstPart + ' ' + secondPart; // Hello World! // +符合是字符連接符。也用于數(shù)字相加

        Boolean

        布爾類型用于條件判斷,布爾類型是只有2個(gè)值:true和false。任何使用邏輯操作符的比較都會(huì)返回布爾值。

        5 === (3 + 2); // = true // 你也可以將布爾值賦給一個(gè)變量var veryTired = true;// 這樣使用if (veryTired) {    // 執(zhí)行代碼 }

        ===也是比較操作符,不僅比較數(shù)值,還比較類型。

        Function

        函數(shù)是特殊的對(duì)象。

        // 使用function操作符來聲明新函數(shù) function myFunctionName(arg1, arg2) {    // 函數(shù)代碼}
        // 你也可以聲明匿名函數(shù) function (arg1, arg2) { // Function code goes here. }
        // 運(yùn)行函數(shù)很簡(jiǎn)單,直接在函數(shù)名稱后面加上小括號(hào)就可以了// 或者也可以帶上參數(shù)myFunctionName(); // 無參myFunctionName('foo', 'bar'); // 有參數(shù)
        // 也可以使用自調(diào)用
        (function () { // 這里自調(diào)用函數(shù)})();

        Array

        數(shù)組也是特殊的對(duì)象,它包含了一批值(或?qū)ο螅L問這些數(shù)據(jù)的話需要使用數(shù)字索引:

        // 2種方式聲明數(shù)組
        // 字面量: var fruit = ['apple', 'lemon', 'banana'];
        // Array構(gòu)造函數(shù): var fruit = new Array('apple', 'lemon', 'banana');
        fruit[0]; // 訪問第1個(gè)項(xiàng)(apple) fruit[1]; // 訪問第2個(gè)項(xiàng)(lemon) fruit[2]; // 訪問第3個(gè)項(xiàng)(banana)

        Object

        一個(gè)對(duì)象是一個(gè)key-value的集合,和數(shù)組相似,唯一的不同是你可以為每個(gè)數(shù)據(jù)定義一個(gè)名稱。

        // 2種類型定義Object對(duì)象
        // 字面量(大括號(hào))var profile = { name: 'Bob', age: 99, job: 'Freelance Hitman'};
        // 使用Object構(gòu)造函數(shù)var profile = new Object();profile.name = 'Bob';profile.age = 99;profile.job = 'Freelance Hitman';

        IF/Else語句

        JS里使用最多的語句莫過于條件語句了:

        var legalDrinkingAge = 21;  var yourAge = 29;  
        if ( yourAge >= legalDrinkingAge ) { alert('You can drink.'); } else { alert('Sorry, you cannot drink.');

        JavaScript操作符

        建議你訪問這個(gè)頁面來查看所有的JS操作符,這里我僅僅給出一些例子:

        // 加減乘除var someMaths = 2 + 3 + 4 - 10 * 100 / 2;  
        // 等于 if ( 2 == (5 - 3 ) { /* 代碼 */ } // == 比較是否相等
        // 不等于 if ( 2 != (5 - 3 ) { /* 代碼 */ }
        // 嚴(yán)格等于(推薦) 2 === 2 // 代替 2 == 2 2 !== 3 // 代替 2 != 3
        // 賦值: var numberOfFruit = 9; numberOfFruit -= 2; // 等價(jià)于 "numberOfFruit = numberOfFruit - 2" numberOfFruit += 2; // 等價(jià)于 "numberOfFruit = numberOfFruit + 2"

        Loop循環(huán)

        Loop循環(huán)在是遍歷數(shù)組或者對(duì)象的所有成員的時(shí)候非常方便,JavaScript里使用最多的是FOR和WHILE語句。

        var envatoTutSites = ['NETTUTS', 'PSDTUTS', 'AUDIOTUTS', 'AETUTS', 'VECTORTUTS'];
        // WHILE循環(huán)var counter = 0;var lengthOfArray = envatoTutSites.length;while (counter < lengthOfArray) { alert(envatoTutSites[counter]); counter++; // 等價(jià)于counter += 1; }
        // FOR循環(huán)// i只是用于迭代,可以任意取名 for (var i = 0, length = envatoTutSites.length; i < length; i++) { alert(envatoTutSites[i]);}

        DOM正文

        訪問DOM節(jié)點(diǎn)

        我們來個(gè)例子,一個(gè)HTML里包含一段文本和一個(gè)無序的列表。

        <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">  <html xmlns="http://www.w3.org/1999/xhtml" lang="en">      <head>              <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />              <title>JavaScript!</title>      </head>      <body>  
        <p id="intro">My first paragraph...</p>
        <ul> <li>List item 1</li> <li>List item 1</li> <li>List item 1</li> <li>List item 1</li> <li>List item 1</li> </ul>
        <script type="text/javascript"> // <![CDATA[
        // ]]> </script>
        </body> </html>

        上面例子里,我們使用getElementById DOM方法來訪問p段落,在SCRIPT里添加如下代碼:

        var introParagraph = document.getElementById('intro');  // 現(xiàn)在有了該DOM節(jié)點(diǎn),這個(gè)DOM節(jié)點(diǎn)展示的是該信息段落

        變量introParagraph現(xiàn)在已經(jīng)引用到該DOM節(jié)點(diǎn)上了,我們可以對(duì)該節(jié)點(diǎn)做很多事情,比如查詢內(nèi)容和屬性,或者其它任何操作,甚至可以刪除它,克隆它,或者將它移到到DOM樹的其它節(jié)點(diǎn)上。

        文檔上的任何內(nèi)容,我們都可以使用JavaScript和DOM API來訪問,所以類似地,我們也可以訪問上面的無序列表,唯一的問題是該元素沒有ID屬性,如果ID的話就可以使用相同的方式,或者使用如下getElementsByTagName方式:

        var allUnorderedLists = document.getElementsByTagName('ul');  // 'getElementsByTagName'返回的是一個(gè)節(jié)點(diǎn)集合// - 和數(shù)組有點(diǎn)相似

        getElementsByTagName

        getElementsByTagName方法返回的是一個(gè)節(jié)點(diǎn)集合,和數(shù)組類似也有l(wèi)ength屬性,重要的一個(gè)特性是他是live的——如果你在該元素里添加一個(gè)新的li元素,這個(gè)集合就會(huì)自動(dòng)更新,介于他和數(shù)組類型,所以可以和訪問數(shù)組一樣的方法來訪問,所以從0開始:

        // 訪問無序列表: [0]索引var unorderedList = document.getElementsByTagName('ul')[0];
        // 獲取所有的li集合: var allListItems = unorderedList.getElementsByTagName('li');
        // 循環(huán)遍歷for (var i = 0, length = allListItems.length; i < length; i++) { // 彈出該節(jié)點(diǎn)的text內(nèi)容 alert(allListItems[i].firstChild.data);}

        以下圖例更清晰地展示了DOM獲取的知識(shí):

        DOM穿梭

        “穿梭”這個(gè)詞主要是用來描述通過DOM查找節(jié)點(diǎn),DOM API提供了大量的節(jié)點(diǎn)屬性讓我們來往上或者往下查詢節(jié)點(diǎn)。

        所有的節(jié)點(diǎn)都有這些屬性,都是可以用于訪問相關(guān)的node節(jié)點(diǎn):

        1. Node.childNodes: 訪問一個(gè)單元素下所有的直接子節(jié)點(diǎn)元素,可以是一個(gè)可循環(huán)的類數(shù)組對(duì)象。該節(jié)點(diǎn)集合可以保護(hù)不同的類型的子節(jié)點(diǎn)(比如text節(jié)點(diǎn)或其他元素節(jié)點(diǎn))。

        2. Node.firstChild: 與‘childNodes’數(shù)組的第一個(gè)項(xiàng)(‘Element.childNodes[0]‘)是同樣的效果,僅僅是快捷方式。

        3. Node.lastChild: 與‘childNodes’數(shù)組的最后一個(gè)項(xiàng)(‘Element.childNodes[Element.childNodes.length-1]‘)是同樣的效果,僅僅是快捷方式。shortcut.

        4. Node.parentNode: 訪問當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn),父節(jié)點(diǎn)只能有一個(gè),祖節(jié)點(diǎn)可以用‘Node.parentNode.parentNode’的形式來訪問。

        5. Node.nextSibling: 訪問DOM樹上與當(dāng)前節(jié)點(diǎn)同級(jí)別的下一個(gè)節(jié)點(diǎn)。

        6. Node.previousSibling: 訪問DOM樹上與當(dāng)前節(jié)點(diǎn)同級(jí)別的上一個(gè)節(jié)點(diǎn)。

        通過這張圖,理解起來就簡(jiǎn)單多了,但有個(gè)非常重要的知識(shí)點(diǎn):那就是元素之間不能有空格,如果ul和li之間有空格的話,就會(huì)被認(rèn)為是內(nèi)容為空的text node節(jié)點(diǎn),這樣ul.childNodes[0]就不是第一個(gè)li元素了。

        相應(yīng)地,的下一個(gè)節(jié)點(diǎn)也不是,因?yàn)楹椭g有一個(gè)空行的節(jié)點(diǎn),一般遇到這種情況需要遍歷所有的子節(jié)點(diǎn)然后判斷nodeType類型,1是元素,2是屬性,3是text節(jié)點(diǎn),詳細(xì)的type類型可以通過此地址:

        Node.ELEMENT_NODE == 1Node.ATTRIBUTE_NODE == 2Node.TEXT_NODE == 3Node.CDATA_SECTION_NODE == 4Node.ENTITY_REFERENCE_NODE == 5Node.ENTITY_NODE == 6Node.PROCESSING_INSTRUCTION_NODE == 7Node.COMMENT_NODE == 8Node.DOCUMENT_NODE == 9Node.DOCUMENT_TYPE_NODE == 10Node.DOCUMENT_FRAGMENT_NODE == 11Node.NOTATION_NODE == 12

        操作元素

        在前面,我們提到了DOM節(jié)點(diǎn)集合或單個(gè)節(jié)點(diǎn)的訪問步驟,每個(gè)DOM節(jié)點(diǎn)都包括一個(gè)屬性集合,大多數(shù)的屬性都提供為相應(yīng)的功能提供了抽象。

        例如,如果有一個(gè)帶有ID屬性intro的文本元素,你可以很容易地通過DOM API來改變?cè)撛氐念伾?/span>

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

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

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

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

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

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

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

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

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

        這僅僅是個(gè)例子,所以該函數(shù)也許沒什么用,語法上來說,直接用還是會(huì)快點(diǎn),例如(elem.style.color = ‘red’)。

        除了style屬性以外,一個(gè)節(jié)點(diǎn)(或元素)也還有其他很多屬性可以操作,如果你使用Firebug,點(diǎn)擊DOM選項(xiàng)卡可以看到所有該節(jié)點(diǎn)(或元素)的所有屬性:

        所有的屬性都可以通過點(diǎn)標(biāo)示符來訪問(例如:Element.tabIndex)。

        不是所有的屬性都是原始數(shù)據(jù)類型(strings, numbers, Booleans等等),sytle屬性也是一個(gè)包含自己屬性的對(duì)象,很多元素的屬性都是只讀的,也就是說不能修改他們的值。

        例如,你不能直接修改一個(gè)節(jié)點(diǎn)的parentNode屬性,如果你修改只讀屬性的時(shí)候?yàn)g覽器會(huì)拋出錯(cuò)誤:例如,拋出錯(cuò)誤“setting a property that has only a getter”,只是我們需要注意的。

        通常DOM操作都是改變?cè)嫉膬?nèi)容,這里有幾種方式來實(shí)現(xiàn)這個(gè),最簡(jiǎn)單的是使用innerHTML屬性,例如:

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

        唯一的問題是該方法沒在規(guī)范里定義,而且在DOM規(guī)范里也沒有定義,如果你不反感的話請(qǐng)繼續(xù)使用,因?yàn)樗任覀兿旅嬉懻撈渌姆椒於嗔恕?/span>

        Node節(jié)點(diǎn)

        通過DOM API創(chuàng)建內(nèi)容的時(shí)候需要注意node節(jié)點(diǎn)的2種類型,一種是元素節(jié)點(diǎn),一種是text節(jié)點(diǎn),上一章節(jié)已經(jīng)列出了所有的節(jié)點(diǎn)類型,這兩種需要我們現(xiàn)在特別注意。

        創(chuàng)建元素可以通過createElement方法,而創(chuàng)建text節(jié)點(diǎn)可以使用createTextNode,相應(yīng)代碼如下:

        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é)點(diǎn)附件到文本字段,這樣做比非標(biāo)準(zhǔn)的innerHTML方法顯得有點(diǎn)長,但了解這些原理依然很重要,這里有一個(gè)使用DOM方法的更詳細(xì)例子:

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

        另外DOM里還有一個(gè)insertBefore方法用于再節(jié)點(diǎn)前面附件內(nèi)容,通過insertBefore和appendChild我們可以實(shí)現(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目表達(dá)式: // 格式:條件?條件為true時(shí)的表達(dá)式:條件為false時(shí)的表達(dá)式

        上面的函數(shù)首先檢查target元素的同級(jí)下一個(gè)節(jié)點(diǎn)是否存在,如果存在就在該節(jié)點(diǎn)前面添加bullet節(jié)點(diǎn),如果不存在,就說明target是最后一個(gè)節(jié)點(diǎn)了,直接在后面append新節(jié)點(diǎn)就可以了。

        DOM API沒有給提供insertAfter是因?yàn)檎娴臎]必要了——我們可以自己創(chuàng)建。

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

        Event事件

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

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

        鼠標(biāo)事件

        1. ‘mousedown’ – 鼠標(biāo)設(shè)備按下一個(gè)元素的時(shí)候觸發(fā)mousedown事件。

        2. ‘mouseup’ – 鼠標(biāo)設(shè)備從按下的元素上彈起的時(shí)候觸發(fā)mouseup事件。

        3. ‘click’ – 鼠標(biāo)點(diǎn)擊元素的時(shí)候觸發(fā)click事件。

        4. ‘dblclick’ – 鼠標(biāo)雙擊元素的時(shí)候觸發(fā)dblclick事件。

        5. ‘mouseover’ – 鼠標(biāo)移動(dòng)到某元素上的時(shí)候觸發(fā)mouseover事件。

        6. ‘mouseout’ – 鼠標(biāo)從某元素離開的時(shí)候觸發(fā)mouseout事件。

        7. ‘mousemove’ – 鼠標(biāo)在某元素上移動(dòng)但未離開的時(shí)候觸發(fā)mousemove事件。

        鍵盤事件

        1. ‘keypress’ – 按鍵按下的時(shí)候觸發(fā)該事件。

        2. ‘keydown’ – 按鍵按下的時(shí)候觸發(fā)該事件,并且在keypress事件之前。

        3. ‘keyup’ – 按鍵松開的時(shí)候觸發(fā)該事件,在keydown和keypress事件之后。

        表單事件

        1. ‘select’ – 文本字段(input, textarea等)的文本被選擇的時(shí)候觸發(fā)該事件。

        2. ‘change’ – 控件失去input焦點(diǎn)的時(shí)候觸發(fā)該事件(或者值被改變的時(shí)候)。

        3. ‘submit’ – 表單提交的時(shí)候觸發(fā)該事件。

        4. ‘reset’ – 表單重置的時(shí)候觸發(fā)該事件。

        5. ‘focus’ – 元素獲得焦點(diǎn)的時(shí)候觸發(fā)該事件,通常來自鼠標(biāo)設(shè)備或Tab導(dǎo)航。

        6. ‘blur’ – 元素失去焦點(diǎn)的時(shí)候觸發(fā)該事件,通常來自鼠標(biāo)設(shè)備或Tab導(dǎo)航。

        其它事件

        1. ‘load’ – 頁面加載完畢(包括內(nèi)容、圖片、frame、object)的時(shí)候觸發(fā)該事件。

        2. ‘resize’ – 頁面大小改變的時(shí)候觸發(fā)該事件(例如瀏覽器縮放)。

        3. ‘scroll’ – 頁面滾動(dòng)的時(shí)候觸發(fā)該事件。

        4. ‘unload’ – 從頁面或frame刪除所有內(nèi)容的時(shí)候觸發(fā)該事件(例如離開一個(gè)頁面)。

        還有很多各種各樣的事件,上面展示的事件是我們?cè)贘avaScript里最常用的事件,有些事件在跨瀏覽器方面可能有所不同。

        還有其它瀏覽器實(shí)現(xiàn)的一些屬性事件,例如Gecko實(shí)現(xiàn)的DOMContentLoaded或DOMMouseScroll等,Gecko的詳細(xì)事件列表請(qǐng)查看這里。

        事件處理

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

        基本事件注冊(cè):

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

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

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

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

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

        高級(jí)事件注冊(cè):

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

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

        嚴(yán)格來說,有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í)上,要做出通用的話,我們可以自定義一個(gè)函數(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!!!');});

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

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

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

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

        addEvent(myIntro, 'click', function () {    alert('WOW!');    removeEvent(myIntro, 'click', arguments.callee);});
        這里我們是有了arguments對(duì)象的callee屬性,arguments對(duì)象包含了所有傳遞進(jìn)來的參數(shù)以及該函數(shù)自身(callee),這樣我們就可以放心地刪除自身的引用了。
        關(guān)于W3C和微軟模型還有其他的少許差異,比如this,在觸發(fā)事件的時(shí)候函數(shù)中的this一般都是該元素上下文,,也就說this引用該元素自身,在基本事件注冊(cè)和W3C模型中都沒有問題,但在微軟模型的實(shí)現(xiàn)里卻可能出錯(cuò),請(qǐng)參考如下代碼:
        function myEventHandler() {    this.style.display = 'none';}
        // 正常工作,this是代表該元素myIntro.onclick = myEventHandler;
        // 正常工作,this是代表該元素myIntro.addEventListener('click', myEventHandler, false);
        // 不正常,這時(shí)候的this是代表Window對(duì)象myIntro.attachEvent('onclick', myEventHandler);

        這里有一些方式可以避免這個(gè)問題,最簡(jiǎn)單的方式是使用前面的基本事件注冊(cè)方式,或者是再做一個(gè)通用的addEvent,通用代碼請(qǐng)參考John Resig或Dean Edward的文章。

        Event對(duì)象

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

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

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

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

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

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

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

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

        事件冒泡

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

        如圖所示,如果a連接被點(diǎn)擊,觸發(fā)觸發(fā)連接的click事件,然后觸發(fā)p的click事件,以此再觸發(fā)div和body的click事件。

        順序不變,而且不一定是在同時(shí)觸發(fā)的。
        這樣你就可以利用該特性去處理自己的邏輯了,并且再任何時(shí)候都可以停止冒泡,比如,如果你只想冒泡到文本節(jié)點(diǎn)上,而不再進(jìn)一步冒泡,你可以在p的click事件處理函數(shù)里丁停止冒泡:

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

        事件委托

        舉例來說,如果你有一個(gè)很多行的大表格,在每個(gè)上綁定點(diǎn)擊事件是個(gè)非常危險(xiǎn)的想法,因?yàn)樾阅苁莻€(gè)大問題。流行的做法是使用事件委托。事件委托描述的是將事件綁定在容器元素上,然后通過判斷點(diǎn)擊的target子元素的類型來觸發(fā)相應(yīng)的事件。

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

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

        總結(jié)

        原生的DOM方法和屬性足夠我們?nèi)粘5膽?yīng)用了,在今天的內(nèi)容中,我們還覆蓋到了DOM元素的操作以及相關(guān)的瀏覽器事件模型,同時(shí)也列舉了一些例子便于大家理解。如果還有什么問題,歡迎留言討論交流。


        本文完?


        瀏覽 66
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            亚洲网站天堂 | 成人大香蕉网站精品免费 | 扒开美女狂揉 | 天天性爱网 | 在电影院摸到她高潮 | 成人免费无码无遮挡黄漫视频 | 亚洲大鸡巴| 玖玖导航| 美女骚屄 | 成人三级在线看 |