劉金華,郭珂言,俞群愛,汪彥龍★
(1.浙江傳媒學(xué)院媒體工程學(xué)院,浙江杭州 310018;2.浙江傳媒學(xué)院網(wǎng)絡(luò)數(shù)據(jù)中心,浙江杭州 310018)
Web 前端開發(fā)涉及超文本標(biāo)記語言(HTML) 、級(jí)聯(lián)樣式表(CSS) 和JavaScript 編程三大技術(shù)。其中,HTML 文檔利用標(biāo)記定義Web 頁面的結(jié)構(gòu)和內(nèi)容;CSS 利用CSS 規(guī)則定義HTML 各標(biāo)記的顯示風(fēng)格,決定Web頁面的顯示方式,CSS技術(shù)實(shí)現(xiàn)了內(nèi)容與表現(xiàn)的分離;JavaScript是基于對(duì)象和事件驅(qū)動(dòng)并具有相對(duì)安全性的客戶端腳本語言。在HTML基礎(chǔ)上,利用JavaScript 可以開發(fā)交互式Web 頁面,使網(wǎng)頁包含活動(dòng)元素和更加精彩的內(nèi)容。JavaScript 腳本和HTML 文檔對(duì)象模型(DOM) 共同控制Web 頁面的行為[1]。JavaScript 通過事件處理程序來響應(yīng)用戶的事件(如單擊鼠標(biāo)等)和對(duì)Web 頁面的控制,是Web 前端開發(fā)課程的重點(diǎn)內(nèi)容,也是學(xué)生掌握的難點(diǎn)[1-5]。結(jié)合單個(gè)知識(shí)點(diǎn)演示實(shí)驗(yàn)來介紹DOM基本概念和用法的教學(xué)方式不利于學(xué)生從整體上把握基于DOM的動(dòng)態(tài)頁面開發(fā)。通過合理設(shè)計(jì)綜合性實(shí)驗(yàn)有助于學(xué)生理解各個(gè)知識(shí)點(diǎn),提高學(xué)生利用所學(xué)知識(shí)解決實(shí)際問題的工程能力[4,6]。
本文設(shè)計(jì)了一個(gè)DOM 綜合實(shí)驗(yàn),實(shí)驗(yàn)涉及DOM基本概念、DOM 結(jié)點(diǎn)訪問、DOM 事件及處理程序、對(duì)象(函數(shù))的this指針及綁定和類定義及類間通信等內(nèi)容。學(xué)生通過調(diào)試、分析和解決實(shí)驗(yàn)中遇到的問題,有效的加深了對(duì)JavaScript、DOM 相關(guān)知識(shí)點(diǎn)和面向?qū)ο笤O(shè)計(jì)思想的理解。
文檔對(duì)象模型(DOM)是HTML 和XML 文檔的編程接口。它提供了對(duì)HTML 文檔的結(jié)構(gòu)化描述,將HTML 文檔解析為一個(gè)由節(jié)點(diǎn)對(duì)象(包含屬性和方法的對(duì)象)組成的結(jié)構(gòu)集合,即Web 頁面的完全的面向?qū)ο蟊硎?,提供了同一份HTML 文檔的另一種表現(xiàn)、存儲(chǔ)和操作的方式。使用JavaScript 等腳本語言能對(duì)它進(jìn)行修改,修改后的影響會(huì)直接在Web頁面上得到反映[7]。
DOM 將HTML 文檔表示成以HMTL 元素對(duì)應(yīng)對(duì)象作為節(jié)點(diǎn)的樹形結(jié)構(gòu),稱之為節(jié)點(diǎn)樹。瀏覽器在加載HTML 文檔的同時(shí)構(gòu)建其對(duì)應(yīng)的DOM 節(jié)點(diǎn)樹。通常,HTML元素的每個(gè)屬性在其對(duì)應(yīng)DOM節(jié)點(diǎn)對(duì)象都有相對(duì)應(yīng)的屬性,通過DOM節(jié)點(diǎn)對(duì)象的方法與屬性,可以訪問頁面中的任何元素,并進(jìn)行元素的修改、刪除以及添加等操作。以下HTML 文檔對(duì)應(yīng)的DOM 節(jié)點(diǎn)樹如圖1所示。
圖1 DOM節(jié)點(diǎn)樹
圖1中,文檔(document)節(jié)點(diǎn)表示HTML文檔對(duì)應(yīng)DOM 節(jié)點(diǎn)樹的根節(jié)點(diǎn)。HTML 文檔中的每個(gè)元素在節(jié)點(diǎn)樹中都有其對(duì)應(yīng)的DOM 節(jié)點(diǎn),節(jié)點(diǎn)樹中節(jié)點(diǎn)的層次關(guān)系與對(duì)應(yīng)HTML 文檔中文檔元素的層次關(guān)系一致。
DOM 結(jié)點(diǎn)樹的document 根節(jié)點(diǎn)對(duì)象提供了多個(gè)getElement和querySelector方法,分別根據(jù)HTML元素屬性和CSS 選擇器來獲取HTML 元素對(duì)應(yīng)的DOM 節(jié)點(diǎn)對(duì)象[8],利用獲取的節(jié)點(diǎn)對(duì)象可對(duì)HTML進(jìn)行各種操作。以下代碼為返回id 屬性值為id 的HTML 元素對(duì)應(yīng)節(jié)點(diǎn)對(duì)象的兩種方法。
文檔對(duì)象模型中,document 對(duì)象的createElement()方法用來創(chuàng)建新元素,該方法的參數(shù)為需要?jiǎng)?chuàng)建元素的標(biāo)記名。DOM元素對(duì)象的appendChild方法用來將其參數(shù)對(duì)應(yīng)的元素添加為該元素對(duì)象的最后一個(gè)孩子結(jié)點(diǎn)。
創(chuàng)建img元素的代碼如下:
const img=document.createElement(′img′);
img.src=′apple.jpg′;
以上代碼創(chuàng)建了一個(gè)img元素,但因?yàn)樵揇OM元素尚未添加到DOM 節(jié)點(diǎn)樹中,所以不會(huì)影響Web 頁面的顯示。以下代碼將所創(chuàng)建的img元素添加到文檔的
元素中:document.body.appendChild(img);
以上代碼執(zhí)行后,img 元素節(jié)點(diǎn)就插入到了DOM節(jié)點(diǎn)樹中,瀏覽器立即將該圖片渲染出來并在Web頁面上顯示。
DOM 事件是對(duì)用戶輸入,點(diǎn)擊等行為的響應(yīng)。JavaScript 與HTML 頁面的交互是通過事件實(shí)現(xiàn)的。DOM事件有三個(gè)要素:事件源(如:按鈕)、事件名(如:鼠標(biāo)單擊)和事件處理程序(如:自定義的函數(shù))。文檔對(duì)象模型將許多DOM元素定義成為可以接收和處理事件的對(duì)象,這些DOM 元素對(duì)象的addEventListener和removeEventListener方法為指定的事件類型添加和刪除事件處理程序。這兩個(gè)方法接收三個(gè)參數(shù):事件名、事件處理函數(shù)和一個(gè)布爾值,true 表示在捕獲階段調(diào)用事件處理程序,false(默認(rèn)值)表示在冒泡階段調(diào)用事件處理函數(shù)。
DOM 中某個(gè)事件觸發(fā)時(shí),所有與該事件相關(guān)的信息都保存在一個(gè)名為event 的事件對(duì)象中。正常情況下,event 對(duì)象是傳給事件處理函數(shù)的唯一參數(shù),事件處理函數(shù)從中獲取與事件相關(guān)的各種信息[9]。
JavaScript 中的this 是代表函數(shù)運(yùn)行時(shí)自動(dòng)生成的一個(gè)內(nèi)部對(duì)象,其只能在函數(shù)內(nèi)部使用,隨著函數(shù)使用場(chǎng)合的不同,this 的值會(huì)發(fā)生變化。通常有以下五種情況[10]:
1)若函數(shù)前面沒有對(duì)象去調(diào)用,那么this 指向?qū)ο骔indow。在嚴(yán)格模式下,this指向undefined;
2) 若函數(shù)作為對(duì)象的屬性調(diào)用時(shí),this 指向該對(duì)象;
3)若函數(shù)由new 運(yùn)算符調(diào)用時(shí),函數(shù)里的this 就指向new 運(yùn)算符返回的這個(gè)對(duì)象。
4) 事件處理函數(shù)中的this 指向監(jiān)聽該事件的對(duì)象;
5)箭頭函數(shù)內(nèi)部的this 指向是固定的,為定義時(shí)的上層作用域。
JavaScript中的函數(shù)也是對(duì)象,該對(duì)象的bind方法可改變this的指向。
通常情況下,對(duì)象之間通信有以下三種方式:
1)利用通信對(duì)象的雙向包含來實(shí)現(xiàn)對(duì)象的通信。但從軟件工程角度看,這是的處理方式是非常不好的。因?yàn)楹芏嗲闆r下這種包含關(guān)系有悖常理。例如,房間有門很正常,但門中有房間就不可思議了。
2)利用定制事件來實(shí)現(xiàn)對(duì)象的通信。對(duì)象注冊(cè)定制事件的監(jiān)聽器,事件觸發(fā)后對(duì)象就能接收到事件并做相應(yīng)處理。
3)利用回調(diào)函數(shù)實(shí)現(xiàn)對(duì)象通信。包含對(duì)象定義回調(diào)函數(shù),被包含對(duì)象在指定事件的處理程序中調(diào)用該回調(diào)函數(shù)。
該綜合實(shí)驗(yàn)項(xiàng)目實(shí)現(xiàn)了簡單的開盲盒功能,用戶可以設(shè)置盲盒的個(gè)數(shù),程序生成設(shè)定數(shù)目的盲盒并將禮物隨機(jī)放入各個(gè)盲盒之中。用戶點(diǎn)擊所選擇的盲盒,盲盒中的禮物及其價(jià)值會(huì)顯示出來。
根據(jù)項(xiàng)目功能,系統(tǒng)的用戶界面應(yīng)包括:輸入盲盒個(gè)數(shù)的文本輸入框;重新加載盲盒的按鈕;代表盲盒的圖片以及必要的提示文本。具體的界面如下圖2所示。
圖2 系統(tǒng)用戶界面
綜合實(shí)驗(yàn)項(xiàng)目的主要目的是加深學(xué)生對(duì)關(guān)鍵知識(shí)點(diǎn)的理解,培養(yǎng)學(xué)生綜合運(yùn)用所學(xué)知識(shí)解決問題的能力。因此,類設(shè)計(jì)方面要盡可能簡單,避免出現(xiàn)復(fù)雜類設(shè)計(jì)影響學(xué)生理解關(guān)鍵知識(shí)點(diǎn)的問題。根據(jù)綜合實(shí)驗(yàn)項(xiàng)目的功能設(shè)計(jì)了App 和Present 兩個(gè)類。類App負(fù)責(zé)創(chuàng)建用戶界面,類Present負(fù)責(zé)盲盒創(chuàng)建及盲盒點(diǎn)擊事件的處理。系統(tǒng)的類圖[11-12]如圖3所示。
圖3 系統(tǒng)的類圖
圖3中類App的構(gòu)造方法(constructor)涉及盲盒創(chuàng)建和綁定函數(shù)this 指針等知識(shí)點(diǎn)。_onPresentOpened方法是盲盒打開后的回調(diào)函數(shù),涉及DOM操作HTML元素、DOM 事件和JavaScript 對(duì)象通信等知識(shí)點(diǎn)。類Present 的構(gòu)造方法(constructor)涉及DOM 添加HTML元素、DOM 事件和綁定函數(shù)this 指針等知識(shí)點(diǎn)。_openPresent方法實(shí)現(xiàn)盲盒中禮物顯示和盲盒打開后回調(diào)函調(diào)用等功能,涉及DOM 操作HTML 元素、DOM事件和JavaScript 對(duì)象通信等知識(shí)點(diǎn)。以上類的方法中,各個(gè)關(guān)鍵知識(shí)點(diǎn)重復(fù)出現(xiàn),有利于學(xué)生理解和掌握。
類Present構(gòu)造函數(shù)的實(shí)現(xiàn)代碼如下:
構(gòu)造函數(shù)的參數(shù)containerElement 是DOM 對(duì)象,為盲盒(Present類實(shí)例)的容器;參數(shù)present是包含代表盲盒中禮物的圖片路徑(name)和禮物的價(jià)值屬性(value)的對(duì)象;參數(shù)onOpenCallback 為回調(diào)函數(shù)對(duì)象,盲盒打開事件發(fā)生時(shí)被調(diào)用。
代碼this. image. addEventListener(′click′, this._open Present)為盲盒(this.image)的單擊事件添加事件處理函數(shù)this._openPresent。缺省情況下函數(shù)this._openPresent 的this 指針指向this.image。代碼this._openPresent = this._openPresent.bind(this)將函數(shù)this._openPresent的this指針綁定為Present類的實(shí)例。
類Present的成員函數(shù)_openPresent是盲盒單擊事件的處理函數(shù),代碼如下所示:
類App 的成員函數(shù)_fillPresentContainer 的作用是調(diào)用Present 類的構(gòu)造函數(shù)創(chuàng)建盲盒并將他們保存在屬性presents中。具體代碼如下所示:
類App 的成員函數(shù)_onPresentOpened,定義盲盒與App類實(shí)例通信的回調(diào)函數(shù),具體代碼如下所示:
實(shí)現(xiàn)圖2 所示系統(tǒng)用戶界面的HTML文檔(index.html)中的代碼如下:
在瀏覽器中打開2.4中的index.html文檔,運(yùn)行該綜合實(shí)驗(yàn)項(xiàng)目,瀏覽器對(duì)HTML文檔渲染后的結(jié)果如圖2所示。將輸入框中盲盒的個(gè)數(shù)由3改為2,然后單擊Reload Presents 按鈕,系統(tǒng)的運(yùn)行結(jié)果如圖4所示。單擊圖4中的第二個(gè)盲盒,盲盒的顯示會(huì)變?yōu)槠渲卸Y物的圖片。文字顯示由“Click a present to open it:”變?yōu)椤癊njoy your present!Price is$[數(shù)值]”,顯示如圖5所示。盲盒打開之后,其他盲盒和打開盲盒中的禮物都不會(huì)再響應(yīng)鼠標(biāo)單擊事件。
圖4 系統(tǒng)的運(yùn)行結(jié)果
圖5 盲盒打開后的顯示
在Present的構(gòu)造函數(shù)中用于綁定_openPresent函數(shù)this 指針的語句:this._openPresent=this._openPresent.bind(this)之前添加注釋符號(hào),重新運(yùn)行該綜合實(shí)驗(yàn)項(xiàng)目。然后單擊界面中的任意一個(gè)盲盒,程序運(yùn)行會(huì)出錯(cuò),錯(cuò)誤提示如圖6所示。
圖6 錯(cuò)誤提示
圖6 中的錯(cuò)誤是執(zhí)行盲盒單擊事件處理程序_openPresent 成員函數(shù)中的this.image.src = this.present.name 語句,訪問this 所指對(duì)象present 成員的name屬性時(shí)發(fā)生的。產(chǎn)生該錯(cuò)誤的原因是:_openPresent函數(shù)是事件處理程序,缺省情況下該函數(shù)的this 指針指向監(jiān)聽事件的DOM對(duì)象,即Present實(shí)例的成員image。然而,image 對(duì)象并沒有present 成員,所以訪問present 成員的name屬性會(huì)出錯(cuò)。為了確定發(fā)生錯(cuò)誤時(shí)this 指針?biāo)赶虻膶?duì)象,在Present 的_openPresent成員函數(shù)的函數(shù)體的第一行插入代碼console.log(′this->′,this)。重新運(yùn)行該綜合實(shí)驗(yàn)項(xiàng)目,單擊界面中的任意一個(gè)盲盒,程序運(yùn)行都會(huì)出現(xiàn)錯(cuò)誤,錯(cuò)誤提示和this指針向?qū)ο笕鐖D7所示。
圖7 錯(cuò)誤提示和this指針指向的對(duì)象
圖7 顯示,此時(shí)Present 的_openPresent 成員函數(shù)的this 指針指向img 元素對(duì)應(yīng)的DOM 對(duì)象,與前述分析一致。
去掉類Present的構(gòu)造函數(shù)中綁定_openPresent函數(shù)this 指針的語句之前的注釋符號(hào),重新運(yùn)行該綜合實(shí)驗(yàn)項(xiàng)目。然后單擊界面中的第二個(gè)盲盒,程序正確運(yùn)行,運(yùn)行結(jié)果和this指針向?qū)ο笕鐖D8所示。
圖8 盲盒打開后的顯示this指針指向的對(duì)象
圖8 顯示,此時(shí)Present 的_openPresent 成員函數(shù)的this指針指向類Present的實(shí)例,該實(shí)例有present成員,name是present的成員之一。
文檔對(duì)象模型是Web 前端開發(fā)課程的重點(diǎn)內(nèi)容之一,涉及知識(shí)點(diǎn)多,如DOM 事件處理和this 指針等內(nèi)容,學(xué)生掌握起來比較困難。本文設(shè)計(jì)了一個(gè)類似于開盲盒的綜合實(shí)驗(yàn)項(xiàng)目,將多個(gè)知識(shí)點(diǎn)集成在一個(gè)實(shí)驗(yàn)項(xiàng)目中,并突出了事件處理、對(duì)象通信和this指針等難點(diǎn)的分析。該實(shí)驗(yàn)具有一定趣味性,容易激發(fā)學(xué)生的學(xué)習(xí)興趣,有助于學(xué)生掌握相應(yīng)難點(diǎn)。通過該項(xiàng)目的實(shí)現(xiàn)和調(diào)試能夠增強(qiáng)學(xué)生的實(shí)踐動(dòng)手能力。學(xué)生以此項(xiàng)目為基礎(chǔ),通過不斷豐富系統(tǒng)的功能,有利于提高其利用所學(xué)知識(shí)解決實(shí)際問題的工程能力。