iT邦幫忙

2021 iThome 鐵人賽

DAY 28
0
Modern Web

你阿嬤成為網頁前端工程師的第一步系列 第 28

[Day 28] 阿嬤都看得懂的怎麼操縱 DOM

阿嬤都看得懂的怎麼操縱 DOM

前兩天我們介紹了厲害的神燈精靈,以及 JavaScript 這個神燈精靈語的方言。既然我們知道, JavaScript 是用來編寫網頁與使用者互動的腳本;接下來,我們就要開始讓神燈精靈幫助我們操縱網頁中的元素囉!

還記得我們說過,整個網頁中的元素,可以看成一棵橫放的樹,而且我們把這棵樹叫做 "DOM" 嗎?

我們都知道,雖然神燈精靈很萬能,但是它必須接受很明確的指示。我們沒辦法指著網頁上的某個元素,告訴它就是操縱這個元素就對了!因為神燈精靈沒有眼睛,看不到我們手指哪裡。我們必須使用文字描述,告訴它我們希望改變的元素是哪一個。

蝦米,那我們應該怎麼使用 JavaScript 向神燈精靈指出特定元素呢?

我們使用的是 document 這個物件。說是物件聽起來有點嚇人,我們就理解為神秘關鍵字咒語就可以了。當我們對神燈精靈喊 "document" 的時候,它就會知道我們要說的事情和網頁元素有關。事實上,document 也是整棵 DOM 樹的樹根,因此,我們用這個樹根的名稱,來告訴神燈精靈,我們要對網頁元素做些什麼囉!

讓我們先來找到目標元素。找目標元素的方法,和 CSS 選擇器非常類似。我們可能從元素的分堆方式 (id, class, tagname) 來找,也可以從元素的從屬關係來找。例如,讓我們回顧之前的貼紙簿練習:
https://codepen.io/LogosChen/pen/jOwzgpz

在這個練習中,我們可以怎麼找到「粉紅貼紙」的字樣呢?這個字樣被包在 .pink 的元素中;因此,只要我們能夠找到 .pink,就能夠找到這個字樣。找到 .pink 元素的方式非常簡單,先喊出 document 以後,再使用 .getElementsByClassName(類別名稱的文字串) 這個功能,就可以得到屬於這個類別的所有元素構成的陣列。

為什麼我們會獲得陣列呢?因為在網頁中,可能會有許多元素共享同個類別名稱。在我們得到這個元素構成的陣列以後,就可以用陣列項目的方式,把特定的元素挑選出來。也因為這樣,我們這個功能的名稱是 getElement"s"ByClassName。如果是用 id 取得元素,功能名稱就是 getElementById,中間沒有複數的 "s",獲得的結果也是單一元素。

因此,我們要找到「粉紅貼紙」的字樣,就可以使用類別名稱 .pink,來獲得同屬 .pink 類別中的元素所構成的清單,再將這個標籤挑選出來。那麼,我們要怎麼挑出子元素呢?在 JavaScript 中,每個網頁元素都有底下這些屬性:

  • childNodes: 子元素構成的陣列
  • firstChild: 第一個子元素
  • lastChild: 最後一個子元素
  • parentNode: 父元素 (每個子元素只會有 1 個父元素,反之不然)
  • previousSibling: 同層前一個元素
  • nextSibling: 同層後一個元素

因此,聰明的阿嬤應該知道怎麼抓出「粉紅元素」這個元素了!我們可以用 getElementsByClassName 這個方法用 .pink 找出屬於 .pink 這個類別的元素陣列,然後選取第 0 項,再挑出 firstChild 就可以了!用 JavaScript 寫看起來會像這樣:

document.getElementsByClassName("pink")[0].firstChild

不過,當我們使用 console.log() 試著印出這個元素時,卻發現完全沒東西!這是怎麼回事呢?原來,瀏覽器會把我們的換行符號也當作 1 個元素。因此,我們必須使用 childNodes[1] 才有辦法找到當中的 p 元素。

document.getElementsByClassName("pink")[0].childNodes[1]

找到特定的元素以後,我們就可以偵測這些元素發生的事件,並且操縱這些元素了。

偵測元素發生事件的方式,是在選定的元素後,加上事件監聽器方法 .addEventListener(事件型態文字串, 執行功能)。事件型態文字串的值非常多,可以參考這份文件。我們這邊只列舉比較常見的:

  • "click": 點下滑鼠
  • "dblclick": 雙擊滑鼠
  • "mouseover": 滑鼠移上
  • "mouseout": 滑鼠移出
  • "mousedown": 按下滑鼠
  • "mouseup": 放開滑鼠
  • "drag": 拖曳滑鼠
  • "drop": 拖曳滑鼠至該元素上釋放

而執行功能就是任意功能,同樣用 function 字樣帶出。不過,由於這個功能只存在於這個事件監聽器當中,所以不用給它任何名稱。只需要寫成

function(){
}

就可以了喔!

讓我們試試看寫出這個腳本:當使用者點擊「粉紅貼紙」字樣的時候,字樣後方要加上 " Clicked" 字樣。

我們上面已經知道怎麼找到「粉紅貼紙」字樣這個元素,不過由於神燈精靈的記性很差,所以必須也給個變數來儲存這個元素。因此,我們在加上事件監聽器後,整個結構會長這樣:

let pinkText = document.getElementsByClassName("pink")[0].childNodes[1]
pinkText.addEventListener("click",function(){
 「粉紅貼紙」字樣加上 "Clicked" 字樣
})

那麼,我們要怎麼把「粉紅貼紙」字樣加上 "Clicked" 字樣呢?我們可能會想,既然我用 console.log(pinkText) 會得到 <p>粉紅貼紙</p> 這個標籤,我們應該就直接寫

pinkText = pinkText + " Clicked"

就可以了吧?奇怪,怎麼完全沒變化呢?因為我們獲得的網頁元素本身是個物件,具備很多屬性,其中的文字內容只是其中一項。當我們想修改其中的文字內容,我們必須修改「文字內容」這個屬性。文字內容這個屬性我們是使用

  • .innerText 找出或修改;另外我們還可以用
  • .innerHTML 找出或修改該元素的整個 HTML 標籤;甚至可以用
  • .id 找出或修改該元素的 id;同理也可以用
  • .className 找出或修改該元素所屬的 class;
  • .style.屬性 找出或修改該元素的屬性。

因此,聰明的阿嬤們應該會知道,我們這邊應該就是需要寫下

  pinkText.innerText = pinkText.innerText + " Clicked" 

來改變「粉紅貼紙」這個字樣。

是說,各位阿嬤會不會覺得上面這段程式碼也很不 DRY 呢?沒錯,這整個 pinkText.innerText 都重複啦!因此,懶惰注重效率的工程師當然會想節省筆墨囉!每當我們想要寫

變數 = 變數 + 某個值

的時候,我們都可以直接寫成

變數 += 這個值

另外,不只 + 這個運算方式,任何運算方式, -, *, /, %, &&, || 都可以使用這種寫法。因此,當我們想寫

	x = x/2

的時候,也可以寫成

	x /= 2

甚至,懶惰注重效率的工程師連

	x += 1

都懶得寫,而改寫成

	x++

;減法也以此類推。

因此,我們就可以使用下面這段 JavaScript,來達成使用者點選「粉紅貼紙」字樣後,再其後加上 " Clicked" 字樣的目標。

let pinkText = document.getElementsByClassName("pink")[0].childNodes[1]
pinkText.addEventListener("click",function(){
 pinkText.innerText = pinkText.innerText + " Clicked" 
})

最後實現的貼紙簿長這樣:
https://codepen.io/LogosChen/pen/oNeXGRM

各位阿嬤可以自己 fork 回去玩喔!

最後,我們應該怎麼引入我們寫好的 JavaScript 檔呢?讓我們來把在 CodePen 上寫好的檔案抓下來看看吧!我們會發現 index.html 這個檔案的結構長這樣:

<html>
	<body>
	.
	.
	.
		<script src="./script.js"></script>
	</body>
</html>

我們會發現

  1. JacaScript 腳本是存成 .js 檔;
  2. 這個 .js 檔是使用 script 標籤引入;
  3. 這個檔案引入的地方是在 body 標籤最後。

引入 .js 的位置和引入 .css 的位置不同,是因為 .js 檔通常必須對 DOM 元素做些操作,所以必須等到 DOM 元素都被渲染出來以後, .js 檔當中的腳本才有辦法抓到那些元素,並且加以監聽或操縱。


上一篇
[Day 27] 阿嬤都看得懂的 JavaScript 怎麼寫
下一篇
[Day 29] 阿嬤都看得懂的 JQuery 怎麼寫
系列文
你阿嬤成為網頁前端工程師的第一步30

1 則留言

0
南國ㄟ安迪
iT邦新手 5 級 ‧ 2021-10-13 11:31:09

阿罵這個車速好像有點快啊,瞬間飆過好多JS語法。

哈哈,篇幅和時間有限,會再修正喔~感謝你的一直追蹤!彼此加油喔~

一起加油,恭喜要完賽

謝謝~這篇已經修正囉!

我要留言

立即登入留言