.st0{fill:#FFFFFF;}

JavaScript 筆記 – DOM 

 2024-02-23

By  Mason - 預計閱讀時間約  分鐘

Document Object Model - DOM

- 文件物件模型 (Document Object Model, DOM) 是 HTML 的程式介面。

- 它提供了一個文件 (樹) 的結構表示法,並定義讓程式可以存取並改變文件架構、風格和內容的方法。

- DOM 提供了文件以擁有屬性和函式的節點與物件組成的結構化表示。節點也可以附加事件處理程序,一旦觸發事件就會行處理程序。

- DOM 將網頁與程式語言連結在一起。

DOM 允許我們在 JavaScript 當中操作 HTML 元素。

- 在 Document Object Model 這棵樹上的每個點被稱之為節點 (node)。節點分為三種:

1. HTML 元素節點 (element nodes or element objects)

2. 文字節點 (text node)

3. 註解節點 (comment node)

DOM 提供 2 種節點集合: 

    HTML Collection (element nodes) - 動態的

    NodeList (包含上述 3 種節點) - 靜態的

    Note:HTML Collection, NodeList are not Array. (array-like object) 代表某些 array methods 無法使用。


三種節點的比較:

每個節點 (Node) 皆有 childNodes 的屬性。Return type 為 NodeList,內部包含此節點在DOM Tree之下的第一層的所有節點。

每個 Element Object 有 children 屬性。Return type 為 HTML Collection,內部包含此節點在 DOM Tree 之下的第一層的所有 Element Object。

Element objects (Nodes) 可以同時使用 childNodes 和 children 屬性。

text node, comment node 只能夠使用 childNodes 屬性,若使用 children 屬性則會顯示 undefined。

不論是使用 childNodes 還是 children 屬性,所獲得的 DOM Tree 元素集合,都只會是本身元素在 DOM Tree下一層的元素。如果希望獲得下下一層的元素,需要使用,像是element.children[i].children 的語法,才能夠取得元素。當然,如果是下下下一層的元素,就需要使用 element.children[i].children[j].children 的語法。

Note 比較2

Window Object

- 在 JavaScript 當中的 window object 代表目前程式碼正在運行的電腦視窗 (瀏覽器視窗)。

window object 可使用常見的 methods:(window 可以不寫)

window.alert()

- 在視窗顯示對話框

window.prompt()

- return 用戶在提示對話框中輸入的文字

window.addEventListener()

- 將事件監聽程式碼附加到 window object

window.setInterval(() => {}, 毫秒)

- 每次經過給定的毫秒數時安排一個函式執行

window.clearInterval()

- 將 setInterval() 所重複執行的程式暫停。

window object 可使用常見的 properties:(window 可以不寫)

window.console

- return 一個 console object,console object 可以對瀏覽器的 debugging console 進行控制與訪問。

- 常用的 methods 為:

     log()
     error()

window.document

- return window 包含的文檔,也就是 HTML 文件。

常用的 methods 有:

    window.document.addEventListener(param1, param2)

    - 第一個參數為 event (事件)

    - 第二個參數為 event (事件)發生時,要執行的 function

    範例:

        function react() {

            alert("有人正在點擊畫面");

        }

        window.addEventListener("click", react);

    // addEventListener 本身是一個 higher order function

    // react 是一個 callback function

    window.document.createElement("tagName")

        - 創建一個 Element Object

    window.document.getElementById("id")

        - return 第一個 id 相符合的 element node (object)

    window.document.getElementByClassName("className")

        - return 一個動態的 HTML Collection ( element node),內部元素包含所有具有給定 className 的元素。

    querySelector("selectors")

       - return 第一個符合特定選擇器群組的 element node (object),採用深度優先搜尋演算法。selectors 為 css 語法

    querySelectorAll("selectors")

       - return 一個靜態 (not live) NodeList,表示與選定選擇器匹配的元素列表。selectors 為 css 語法

Note 比較
Note 比較2


window.localStorage

- return 一個 local storage 物件。

window.sessionStorage

- return 一個 session storage 物件。

物件導向程式概念

- 一個 Object 可以是另一個 Object 的 attribute。

範例:

let Grace = {

    name: "Grace",

    age: 26,

};

let Wilson = {

    name: "Wilson",

    age: 26,

    spouse: Grace,

};

console.log(Wilson.spouse.name); // Grace

Function Declaration and Expression

- JavaScript 中的函數 (function) 是 first-class objects (一等公民)。所謂的函數 (function) 是 first-class objects (一等公民)是指:

    1. 將 function 分配給變數。例如:let hello = function() {...}

    2. 將 function 當作 argument 傳給其他 function。Higher Order Function 的 argument 被稱為 callback function。

- Function Declaration 和 Function Expression 語法幾乎相同。

- Function Expression 可以省略函數名稱以創建匿名函數 (需要放入一個變數中執行)。

Function Expression 語法:

function (param0, param1, /*....*/ paramN) {

    statements

}

範例 1:

Function Declaration:

function addition(a, b) {

    return a+ b;

}

Function Expression:

let addition = function (a, b) {

    return a + b;

}

範例 1:

let Wilson = {

    name: "Wilson",

    greet() {

        console.log(this.name + "打招呼";

    },

    walk: function () {

        console.log(this.name + "正在走路");

    },

};

Wilson.greet();

Wilson.walk();

- Function Expression 的用途主要在於:

1. 創建一個未命名的 Function,之後再把這個 Function 放置到其他的變數內部,讓程式碼更有彈性。

2. 當作 higher order function 的 callback function 使用。例如:forEach 或是 addEventListener。

    範例:

        window.addEventListener("click", function () {

            alert("有人正在點擊畫面");

        });

    // addEventListener 本身是一個 higher order function

    // 內部的 function 是一個 callback function

3. 使用 IIFE (Immediately Invokied Function Expression) 的功能 (立即執行)。

 - 當我們想要避免汙染 global naming space,或是想要立即執行某個匿名 function 時,就可以使用 IIFE。

 - 語法為:

    (function() {

        //...

    })();

    範例 1:

        (function (a, b) {

            console.log(a + b);

        })(10, 5);

        結果為:15

    範例 2:

        (() => {

            console.log("hello");

        })();

        結果為:hello

Arrow Function Expression

- Arrow Function Expression 是 Function Expression 的簡化語法。

- 若 Arrow Function Expression 只有一個 parameter,則不需要加上括號 (可加可不加)

- 若 Arrow Function Expression 有 0 個或 2 個以上的 parameter,則一定要加上括號

- Arrow Function Expression 的主體若不加上 curly brackets {},則 return expression 的值。

- Arrow Function Expression 的主體有多個計算式時,則一定要加上 curly brackets {}。

- 若 Arrow Function Expression 有加上curly brackets {},則一定要加 return 關鍵字才會回傳一個值。

- Arrow Function Expression 沒有 this 關鍵字綁定,不應該用做 Objects 的 methods。

- 語法如下:

    () => expression;

    param => expression

    (param1, param2, ...) => expression

    param => {return expression}

    範例 1:

        let hello = () => {

            console.log("hello world");

        };

        hello();

    範例 2:

        let Wilson = {

            name: "Wilson",

            walk:  () => {

                console.log( "Wilson 正在走路"); // 注意:arrow function expression 沒有 this keyword 綁定,不能使用 this.name

            },

        };

        Wilson.walk();

    範例 3:

        window.addEventListener("click",  () =>  {

            alert("有人正在點擊畫面");

        });

forEach() Method

- forEach() 本身為 higher order function。

- forEach() 為陣列裡面的每個元素執行一次所提供的函式。

- forEach() 與 arrow function 協作的語法如下:

    1. forEach(element => ...)

    2. forEach(callbackFn) // 只放入一個 callback function

範例 1:

let myLuckyNumbers = [1, 2, 3, 4, 5, 6, 7];

for (let i = 0; i < myLuckyNumbers.length; i++) {

  myLuckyNumbers[i] = myLuckyNumbers[i] + 3;

}

console.log(myLuckyNumbers);

範例 2:

let myLuckyNumbers = [1, 2, 3, 4, 5, 6, 7];

function plus3(n) {

  console.log(n + 3);

}

myLuckyNumbers.forEach(plus3);

範例 3:function expression

let myLuckyNumbers = [1, 2, 3, 4, 5, 6, 7];

myLuckyNumbers.forEach(function (n) {

  console.log(n + 3);

});

範例 4:arrow function

let myLuckyNumbers = [1, 2, 3, 4, 5, 6, 7];

myLuckyNumbers.forEach((n) => {

  console.log(n + 3);

});

     3. forEach((element, index) => ...)

範例 :

let myLuckyNumbers = [1, 2, 3, 4, 5, 6, 7];

myLuckyNumbers.forEach((n, index) => {

  console.log(n + " is at index" + index);

});

forEach in NodeList

- 使用 querySelectorAll("selectors"),會 return 一個靜態 (not live) NodeList,因此可以使用 forEach() method。(表格二)

範例:(提取 html 裡面所有 class 為 hello 的 Nodelists)

let hellos = document.querySelectorAll(".hello");

    hellos.forEach((hello) => {

        console.log(hello);

});

Element Objects

- 在 DOM Tree 當中,每個 HTML 元素 (可能) 有自己獨特的 properties and methods。除了這些獨特的 properties and methods 之外,所有 Element Objects 都必須具有以下的屬性和方法:

    addEventListener(event, callbackFn)

    appendChild(element)

     - 附加子元素 (與 document.createElement() 搭配)

       範例:(在 htmel body 裡面新增一個 h1 tag)

            let body = document.querySelector("body");

            let myH1 = document.createElement("h1");

            myH1.innerHTML = "<a href='htpps://www.google.com'>Google</a>";

            body.appendChild(myH1);

    innerHTML

    - 可以顯示裡面的 html tag 功能及其文字

    innerText

    - 單純只顯示文字,

    children

    - HTML Collection

    childNodes

    - NodeList 

    parentElement

    -尋找上一層的標籤

    classList

    - class 列表 (列出所有的 class) 

    - 此物件可用的 methods 有:

         classList.add("className") - 新增 class

         classList.remove("className"), - 刪除 class

         classList.toggle("className"), - 如果有此 className 則刪除,如果沒有的話則將此 className 加入。

         classList.contains("className"), - 有無包含此 className,

    getAttribute(attributeName)

    - 取得 tag 屬性的內容

        範例:(取得 a 標籤裡面 href 屬性內容)

            let a = document.querySelector("a");

            console.log(a.getAttribute("href");

    querySelector(selector)

    querySelectorAll(selector)

    remove()

    - 讓 tag 消失

    範例:(點擊 button 之後讓 anchor tag 消失)

        let button = document.querySelector("button");

        button.addEventListener("click", () => {

            let a = document.querySelector("a");

            a.remove();

        });


    style 

    - 可以用來改變 element object 的 inline styling。因為 JS 中不允許使用 hyphen,因此,JS 中的 CSS 屬性都被更改為 camelCase

    範例:

        let button = document.querySelector("button");

        // button.style.backgroundColor = "green";

        //  button.style.color = "white";

        button.style = "background-color: green; color: white";

Inheritance (繼承)

- 在物件導向的程式語言當中,我們可以將 attributes 和 methods 從一個 class (類別) 繼承到另一個 class (類別)。 此過程稱為 Inheritance (繼承)。

    1. subclass (子類) - 從另一個 class 繼承的 class。也稱為 child class

    2. superclass (父類) - 繼承自的 class。也稱為 parent class

- 所有 HTML 元素都從 Element Object 繼承 attributes 和 methods。除了繼承來的屬性和方法,其中某些 HTML 元素有自己獨特的attributes 和 methods。

例如:

        <form> 有 reset(), submit

        範例:(按下一個按鈕之後重置表單)

        let button = document.querySelector("button"); // 按鈕 CSS class 為 button

        button.addEventListener("click", () => {

          let form = document.querySelector("form"); // 表單 CSS class 為 form

          form.reset(); // 重置表單

        });

        <video> 有 play(), pause()

        <input> 有 value

JavaScript Events

- Event 表示了一個在 DOM 物件上所發生的事件,通常是由使用者的操作行為所產生 (點擊滑鼠按鈕、敲打鍵盤或調整螢幕大小)。當某個事件在某個元素發生時,我們可以撰寫程式碼來做出相對應的回應。

- addEventListener() 可以讓我們在 window object, document object 以及 element object 上面掛一個事件監聽器 (Event Listener)。當有特定事件發生時,事件監聽器 (Event Listener) 就會執行所被賦予的 function。

    addEventListener(type, listener)

    - type 是指事件類型。例如:button 可以掛著 click 這種事件的事件監聽器、window object 可以掛著 resize 這種事件的事件監聽器。

    -listener 通常為一個一般的 function,或更常見的是一個 arrow function expression。當事件發生在 HTML element 上面時,JavaScript 會自動執行 listener 這個 callback function。Callback function 被執行時,JavaScript 會再把 event object 當作 argument,放進 listener 內部去執行函式。

        Events Objects 最常用到的幾個屬性與方法:

            target - 指向最初觸發事件的 DOM 物件。

    範例:

        let button = document.querySelector("button");

        button.addEventListener("click", (e) => {

            console.log(e.target); // 點擊按鈕後,指向 button object

        });

            preventDefault() - 取消事件的預設行為,但不會影響事件的傳遞,事件仍會繼續傳遞。

    範例:

        let form = document.querySelector("form");

        form.addEventListener("submit", (e) => {

            e.preventDefault; // 取消所有 JS 內的預設值

        });

            stopPropagation() - 可以防止 event bubbling 進一步傳播當前事件。

event bubbling

- 當一個事件發生在一個元素上時,首先會在其上運行 event handler,然後會運行其 parent element 的 event handler,然後一直往上運行其祖先的 event handler。(事件從內部元素一直往上層運行)

範例:當點擊藍色 div b 啟動監聽事件之後,隨之會自動執行紅色 div a 的監聽事件 (event bubbling)。

為了防止 event bubbling,則可以在 div b 監聽事件程式碼中加入stopPropagation() 來終止它

        // html (紅色 div a 裡面有一個藍色 div b)

        <div class="a" style="width: 300px; height: 300px; background-color: red">

            <div

                class="b"

                style="width: 150px; height: 150px; background-color: blue"

             ></div>

        </div>

        // JavaScript

        let a = document.querySelector("a");

        let b = document.querySelector("a");

        a.addEventListener("click", () => {

            alert("紅色框的是件監聽器正在被執行"); 

        b.addEventListener("click", (e) => {

            e.stopPropagation(); //防止 event bubbling, (NOTE:如果使用 preventDefault() 則 event bubbling 事件仍會繼續傳遞。)

            alert("藍色框的是件監聽器正在被執行"); 

        });

            currentTarget - 在 event bubbling 發生時,event object 的 target 屬性在 child element 與 parent element 的 event handler 內會是一樣的。例如:

currentTarget1

這段程式碼的運行結果會是:

currentTarget2

這裡可以看出三個 event handler 內的 event.target 屬性都是 <button id=“inner”>。這是一個很特別的規則。但這個規則的壞處是我們無法知道目前是哪個元素上的event handler 正在被執行。因此,JavaScript 的 event object 除了 target 屬性之外,有另一個屬性叫做 currentTarget。

MDN 上對 currentTarget 的定義是「The currentTarget read-only property of the Event interface identifies the element to which the event handler has been attached. This will not always be the same as the element on which the event was fired, because the event may have fired on a descendant of the element with the handler, and then bubbled up to the element with the handler. The element on which the event was fired is given by Event.target.」 (https://developer.mozilla.org/en-US/docs/Web/API/Event/currentTarget

簡單來說,addEventListener 監聽的是誰,則 e.currentTarget 就是誰。例如:

currentTarget3

這段程式碼運行的結果是:

currentTarget4

這裡可以看出,currentTarget 不斷在變化,指向的都是 addEventListener 的監聽對象。

Local Storage and Session Storage

- Storage 是瀏覽器中儲存數據的地方(非數據庫)。

-Storage 內部儲存的數值都是 key-value pair,且不論是 key or value,資料型態都必須是 String。如果將要儲存的資料不是 String,那麼資料會先強制轉換成 String,再被儲存到 Storage 裏面。

    localStorage - 存放在此的資料並沒有過期的時效,即使把瀏覽器關了,電腦關機 localStorage 的資料仍然在瀏覽器內。

    sessionStorage - 一旦用戶關閉瀏覽器,sessionStorage 的資料就會被銷毀。

Local Storage and Session Storage methods

- Local Storage and Session Storage 可以使用的 methods 都一樣。

    setItem(key, value) - 當傳遞一個 key and value 時,會將該 key-value pair 添加到給定的 Storage,或者如果該 key 的值已經存在,則會更新該 key 的 value。

        localStorage.setItem("name", "Stan");

        localStorage.setItem("age", "26");

    查詢已儲存的資料:

localStorage-setItem

    getItem(key) - 從給定的 Storage 中返回該 key 的 value 值,如果該  key 不存在,則返回 null。

        let myName = localStorage.getItem("name");

        let myAge = localStorage.getItem("age");

        console.log(myName); // Stan

        console.log(myAge);  // 26 (儲存的資料為 String 非 Number)

        console.log(typeof myAge); // String

    removeItem(key) - 從給定的 Storage 中刪除該 key-value pair (如果 key-value pair 存在的話)。

       localStorage.removeItem("name");  // 刪除 name 的 key-value pair

    clear() - 清除儲存在給定的 Storage 中的所有的 key-value pair。

       localStorage.clear();  // 刪除 Storage 中的所有的 key-value pair

JSON and Storage

- 由於 Storage只能儲存 String,若想要把 object, array 等等資料類型存放在 Storage 內部,則需要用到 JSON Object。

- JSON 代表 JavaScript Object Notation,是一種資料交換格式。

- JSON Object 有兩個 methods:

    JSON.stringify(value) - 將 value 轉換為 JSON String。

        let myLuckyNumbers = [1, 2, 3, 4, 5, 6];

        localStorage.setItem("myNumbers", JSON.stringify(myLuckyNumbers)); // 將 array 轉換為 JSON String 保持 array 的完整資料型態。

    JSON.parse(text) - 解析 JSON String,製作出 JSON String 描述的 JavaScript 值或 Object。

        let myArr = JSON.parse(localStorage.getItem("myNumbers")); // 利用 JSON.parse 將 array 完整的解析出來。

        myArr.forEach((n) => {

            console.log(n);

        }); // 結果為 1 2 3 4 5 6

正確的操作流程:

JSON and Storage
{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}
>