# JavaScript ###### tags: `JavaScript` `programming` ## 目錄 [TOC] ## 參考資料 [1. Understanding ECMAScript 6 (ES6)](https://github.com/nzakas/understandinges6) [2. 從ES6開始的JavaScript學習生活](https://eddy-chang.gitbook.io/javascript-start-from-es6/) ## 1、簡介 ## 2、JavaScript ## 3、變數 - 避免重複的計算 - 抽象化獨立每一層結果 - 儲存使用到的中途狀態 ### <i class="fa fa-square" aria-hidden="true"></i> 資料型態 - Undefined var 宣告變數後沒有給值的變數,這樣的變數內含一個值,就是undefined - Null null 型態只有一個值,就是 null,表示缺少資料 - Boolean true 或 false 通常做為邏輯或條件運算式回傳的結果 - String 字串,由字元組成,必須包含在成雙的「"」或「'」之內 - Number 數值,包括整數或浮點數 - Object 代表這是一個物件 ### <i class="fa fa-square" aria-hidden="true"></i> 不同型態變數 - 數字會相加,文字會相接 ```javascript= 50 * 3 // 150 "NT" + 50*3 + "$" // NT 150 $ 50 > 30 // true parseInt("50") + 30 // 80 ``` ### <i class="fa fa-square" aria-hidden="true"></i> 變數規則 - 首字 - 必須是英文字母或底線(_),不可以是數字或其他符號 - 名稱 - 不可以有空格 - 大小寫英文字母 - X1 和 x1 是不同的變數。 - 不能使用關鍵字 - JavaScript 內建名字,如: if、else、true、false… - 變數需有語意化 (建議) ### <i class="fa fa-square" aria-hidden="true"></i> undefined V.S. is not defined #### <i class="fa fa-arrow-right" aria-hidden="true"></i> undefined - 宣告變數未給定數值 ```javascript= var value; console.log(value); /*------------------------------------------------------ 於主控台顯示Undefined ------------------------------------------------------*/ ``` #### <i class="fa fa-arrow-right" aria-hidden="true"></i> is not defined - 未宣告變數 ```javascript= .console.log(value); /*------------------------------------------------------ 在未宣告變數value情況下,會得到value is not defined ------------------------------------------------------*/ ``` ### <i class="fa fa-square" aria-hidden="true"></i> 作用域(Scope)與提升(Hoisting) 根據變數使用有效範圍的不同,可區分為兩種 1. 全域變數 - 在==函式外==宣告的變數 - 過多的全域變數會占用記憶體空間,影響程式計算 2. 區域變數 - 在==函式內==宣告的變數 - 執行完函式後的區域變數會自動被消除 :::warning JavaScript程式語言的作用範圍,是使用==函式作用範圍(Function Scope)==,或稱之以函式為基礎(Function-based)的作用範圍。 只有使用函式才能劃出一個本地端的作用範圍,其他的區塊像if、for、switch、while等等,雖然有使用區塊語句({...}),但卻是無法界定出作用範圍。 ::: 程式碼: ```javascript= function scope() { if (true) { var value = "local"; } console.log(`value in function:`,value); } scope(); console.log(`value:`,value); ``` 執行結果: ```shell= value in function: local Uncaught ReferenceError: value is not defined ``` #### <i class="fa fa-arrow-right" aria-hidden="true"></i> 變數提升(Hoisting)機制 在函數作用域或全局作用域中,透過關鍵字var宣告的變數,**無論在哪裡宣告的**,都會被當成在==當前作用域頂端宣告的變數==。 程式碼: ```javascript= if(true) { var if_value; console.log(`if_value:`, if_value); } console.log(`global:`, if_value); // 等同於以下寫法 /*------------------------------------- var if_value; if(true) { console.log(`if_value:`, if_value); } console.log(`global:`, if_value); -------------------------------------*/ ``` 執行結果: ```shell= if_value: undefined undefined ``` ### <i class="fa fa-square" aria-hidden="true"></i> var宣告中的遮蔽效應(Shadowing) var 可以讓同一個==變數名稱==,在作用域中被定義很多次 程式碼: ```javascript= function showMSG() { var msg = "Good"; alert(msg); } var msg = "Hello"; showMSG(); alert(msg); ``` 程式碼: ```javascript= var foo="bar"; var foo ="abc"; console.log(foo); // abc ``` ### <i class="fa fa-square" aria-hidden="true"></i> let、const變數宣告 - let、const 屬於==區塊級作用域(Block Scope)== - 不會有Hoisting機制 - 相同作用域中禁止重複宣告變數 #### <i class="fa fa-arrow-right" aria-hidden="true"></i> let宣告 - 用法與var相同,用let代替var來宣告變數,可以把變數的作用域限制在區塊中 ```javascript= if(true) { let if_value = "Hello"; } console.log(`global:`, if_value); ``` 執行結果: ```shell= Uncaught ReferenceError: if_value is not defined ``` - 禁止重複宣告避免發生遮蔽效應(Shadowing) 程式: ```javascript= var count = 30; let count = 40; ``` 執行結果: ```shell= Uncaught SyntaxError: Identifier 'count' has already been declared ``` 程式: ```javascript= var count = 30; // Does not throw an error if (true) { // 只作用於if區塊中 let count = 40; // more code } ``` #### <i class="fa fa-arrow-right" aria-hidden="true"></i> const宣告 1. 使用const宣告的是常數(constant),其值一但被設定後==不可更改== 2. const宣告常數必須進行初始化(指派數值) ```javascript= // 有效的const宣告 const maxValue = 30; // 語法錯誤:常數未初始化 const minValue; // Uncaught SyntaxError: Missing initializer in const declaration ``` ```javascript= if(true) { // 此處會拋出錯誤 // Uncaught ReferenceError: Cannot access 'maxValue' before initialization console.log(maxValue); let maxValue = 5; } // 此處無法訪問maxValue console.log(maxValue); ``` #### :star:【補充】const原理與例外狀況 - const原理 JavaScript在對變數的參考進行讀取時,會從變數目前對應的記憶體位址讀取內容,當使用者改變變數的數值時,引擎會重新從記憶體中分配一個新的記憶體空間以儲存新的值,並將新的記憶體位址與變數進行綁定。 const的原理便是在變數名稱與記憶體之間建立不可變的綁定,當後面的程式嘗試申請新的記憶體空間時,引擎便會拋出錯誤。 - const 例外狀況 const的實現原理,確實只限於創造一個不可變的記憶體綁定,而在某些情況下,並非**值不可變**。如字串、數字、Boolean、undefined等基礎類型,這些類型的值,在記憶體空間中是不被拆分的;而對於==陣列、物件==等類型,在記憶體中可能會被拆分成許多段落。 :::danger :loudspeaker: 所以物件類型的變數使用const**可以修改** ::: ```javascript= const a = 2; a = 3; // Uncaught TypeError: Assignment to constant variable. const arr = [1, 2, 3, 4]; arr.push(5); // arr變成[1,2,3,4,5] const obj = { a: 1 }; obj.b = 2; // obj變成 {a:1, b:2} ``` ## 4、流程控制判斷 ( 運算子、 if 、 switch ) ### <i class="fa fa-square" aria-hidden="true"></i> 比較運算子 | **符號** | **意義** | **說明** | | -------- | -------- |-------- | | == | 相等 |判別運算子左邊是否等於右邊| | === | 相等(嚴謹) | 判別運算子左邊「型別」與「數字」是否完全等於右邊| | != | 不相等 | 判別運算子左邊是否完全等於右邊| | !== | 不相等(嚴謹) | 判別運算子左邊「型別」與「數字」是否完全不等於右邊| | true | 為真 | 判別式為真| | false | 為假 | 判別式為假| | < | 小於 | 判別運算子左邊是否小於右邊| | > | 大於 | 判別運算子左邊是否大於右邊| | <= | 小於等於 | 判別運算子左邊是否小於等於右邊| | >= | 大於等於 | 判別運算子左邊是否大於等於右邊| ### <i class="fa fa-square" aria-hidden="true"></i> 邏輯運算子 | **符號** | **意義** | **說明** | | -------- | -------- |-------- | | ! | not |將邏輯值相反,輸入true得到false| | && | and | 當運算子左右邊均為true時才輸出true| | \|\| | or | 當運算子左右邊其中之一為true時即輸出true| ### <i class="fa fa-square" aria-hidden="true"></i> 流程判斷 if -else if - else #### <i class="fa fa-arrow-right" aria-hidden="true"></i> if - else ![](https://i.imgur.com/jvHndNn.png) ```javascript= let confirmed = true; if(confirmed) { console.log("有朋友"); } else { console.log("我是邊緣人"); } ``` #### <i class="fa fa-arrow-right" aria-hidden="true"></i> if - else if - else ![](https://i.imgur.com/ERG3URg.png) ```javascript= let count = 0; if(count === 1) { console.log("學期成績60分"); } else if(count === 2) { console.log("學期成績75分"); } else if(count === 3) { console.log("學期成績90分"); } else { if(count === 0) { console.log("學期成績50分"); } else { console.log("學期成績100分"); } } ``` ### <i class="fa fa-square" aria-hidden="true"></i> switch條件結構 - 根據不同的變數值執行不同的程式區塊 - 出現不在設定條件值中的例外情況,則==執行default的程式區塊== - 如不會有例外情況出現,則可以省略default程式區塊 - 以case訂定條件值 - 每個條件值,需加入`break`跳出條件結構 - 若符合條件值卻無使用break,則無視後續case直接執行程式區塊 ```javascript= let count = 0; switch(count) { case 1: console.log("學期成績60分"); break; case 2: console.log("學期成績75分"); break; case 3: console.log("學期成績90分"); break; default: if(count === 0) { console.log("學期成績50分"); } else { console.log("學期成績100分"); } break; } ``` ## 5、陣列與物件 :::info 在JavaScript中支援的型別,主要可以分成==基本型別(Primitives)== 與 ==物件型別(Object)== 兩大類,基本型別如[3.變數](#資料型態)中提到string、number、boolean、null、undefined,symbol(ES6之後),其餘的可以歸類至物件型別如陣列、物件。 - 基本型別只能代表一個值 - 物件代表多個或複合值 ::: ### <i class="fa fa-square" aria-hidden="true"></i> 陣列 - 陣列是由多個元素所組成 - JavaScript 支援一維或多維陣列 - 陣列是==有順序性==的集合 - 陣列大小不固定,可以隨時新增或移除元素 - 陣列內容不一定是同一種資料型態 - 陣列==索引(index)從0開始==,也就是陣列的第一個元素是元素0 - 使用 ==變數[index]== 來提取陣列中的元素 :::danger :dart: **重要**: 提取陣列元素是從0開始不是從1開始,所以提取第一個元素是**0**,最後一個元素是**length-1** ::: #### <i class="fa fa-arrow-right" aria-hidden="true"></i> 陣列建立方式 I - Array建構式 (很少用) ```javascript= let a1 = new Array(); // 空陣列 a1[0] = "apple"; a1[1] = "boy"; a1[2] = "cat"; //此時 a1陣列為 ["apple", "boy", "cat"] let a2 = new Array(1,2,3); // [1, 2, 3] let a3 = new Array(2); //長度為2的陣列(所有元素都是undefined) let a4 = new Array("2"); //["2"] console.log("al array:",a1); console.log("a2 array:",a2); console.log("a3 array:",a3); console.log(a3[0]); console.log(a3[1]); console.log("a4 array:",a4); a3[2] = "a"; ``` #### <i class="fa fa-arrow-right" aria-hidden="true"></i> 陣列建立方式 II - 陣列常值 (Array Literal) - 此種方式簡潔、方便,實務上比較常使用此方式建立陣列 ```javascript= let a1 = []; a1[0] = "apple"; a1[1] = "boy"; a1[2] = "cat"; //此時 a1陣列為 ["apple", "boy", "cat"] a1.length; // 3 // 也可以這樣宣告 let a2 = ["apple", "boy", "cat"]; a2.length; // 3 // 存取元素 a2[0]; // 存取第一個元素 apple a2[1]; // 存取第二個元素 boy a2[2]; // 存取第三個元素 cat,此處也是最後一個元素等同於a3[length-1] a2[a2.length-1]; // 存取最後一個元素也就是第三個元素 // 增加元素 (陣列大小) a2[3] = "dog"; a2; // ["apple", "boy", "cat", "dog"] a2[6] = "fish"; // 中間新增之元素因為指派所以為undefined a2; // ["apple", "boy", "cat", "dog", undefined, undefined, "fish"]; // 修改元素 a2[1] = "girl"; // 第二個元素從boy修改成girl a2; // ["apple", "girl", "cat", "dog", undefined, undefined, "fish"]; ``` #### <i class="fa fa-arrow-right" aria-hidden="true"></i> push、pop、unshift、shift 在開頭或結尾處添加或移除一個元素 - 在結尾添加或移除一個元素 - push()結尾添加一個元素,並回傳==陣列最新長度== - pop()結尾移除一個元素,並回傳==被移除的元素== - 在開頭添加或移除一個元素 - unshift()開頭添加一個元素,並回傳==陣列最新長度== - shift()開頭移除一個元素,並回傳==被移除的元素== ```javascript= let arr = ["b", "c", "d"]; arr.push("e"); // 回傳陣列最新長度4;arr目前變成["b", "c", "d", "e"] arr.pop(); // 回傳"e";arr目前變成["b", "c", "d"] arr.unshift("a"); // 回傳陣列最新長度4;arr目前變成["a", "b", "c", "d"] arr.shift(); // 回傳"a";arr目前變成[b", "c", "d"] ``` #### <i class="fa fa-arrow-right" aria-hidden="true"></i> concat 在結尾處加入多個元素 concat方法可在陣列加入多個元素並回傳副本,也就是說concat並不影響原陣列 ```javascript= let arr = [1, 2, 3]; arr.concat(4,5,6); // 回傳[1, 2, 3, 4, 5, 6]; arr並未被修改 arr.concat([4,5,6]); // 回傳[1, 2, 3, 4, 5, 6]; arr並未被修改 arr.concat([4,5],6); // 回傳[1, 2, 3, 4, 5, 6]; arr並未被修改 // concat只會直接拆開你提供給他的陣列(如上述狀況),不會拆開陣列中的陣列(如底下範例) arr.concat([4,[5,6]]); // 回傳[1, 2, 3, 4,[5, 6]]; arr並未被修改 ``` #### :star::star: 【補充】陣列的操作 forEach、map、filter、reduce 陣列處理技巧是 JavaScript 中非常重要的一塊,尤其是Node.js問世後,JavaScript可以做為前後端開發的程式語言,大量的資料從後端取得或是從前端傳輸,這些資料通常都以陣列物件方式在拋轉,只要能夠對於陣列資料操作熟悉,配合框架就能夠將開發效率更上層樓。 > 資料集來源:https://data.cdc.gov.tw/dataset/aagsdctable-day-19cov ##### <i class="fa fa-caret-right" aria-hidden="true"></i> [forEach()](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach) forEach 是這幾個陣列函式最單純的一個,不會額外回傳值,只單純執行每個陣列內的物件或值 - 等同於使用for迴圈執行陣列操作 ```javascript= let classes = [ { className: "網際網路應用", class: "機械四甲", teacher: "丁丁", students: 40 }, { className: "MATLAB", class: "機械一甲", teacher: "丁丁", students: 92 }, { className: "工業大數據", teacher: "鍾文仁", students: 30 }]; //傳統陣列操作 for (let i =0; i < classes.length; i++) { console.log(classes[i].className); } //forEach 作法 classes.forEach(function(ele,idx) { console.log(`forEach className: ${ele.className}`); ele.classCode = `MEXXX${idx}`; }) console.log(classes); ``` ##### <i class="fa fa-caret-right" aria-hidden="true"></i> map() 使用 map() 時他需要回傳一個值,他會透過函式內所回傳的值組合成一個陣列 - 如果不回傳則是 undefined - 回傳數量等於原始陣列的長度 - 很適合將原始的變數運算後重新組合一個新的陣列 ```javascript= // newArr處理完以後的新陣列 // function舊陣列要處理的函數 // value正在處理的元素 // index正在處理的元素索引 (可省略) // array舊陣列 (可省略) let newArr = arr.map(function (value, index, array){ //... }); ``` 假如我是一個包租婆,陣列A儲存我每間房收的房租,但我最近比較缺錢所以打算漲個價斂財,每間房子直接改收兩倍的租金,這時候就可以使用 map() 方法快速計算每間房子漲價後的房租,並儲存在B陣列中: ```javascript= let A = [9000, 8500, 5500, 6500]; let B = A.map(function (value, index, array) { return value*2; }); console.log(A) // [9000, 8500, 5500, 6500] - 原陣列不會被修改 console.log(B) // [18000, 17000, 11000, 13000] - 發財摟 ^____^ ``` ##### <i class="fa fa-caret-right" aria-hidden="true"></i> filter() filter() 會回傳一個陣列,其條件是 return 後方為 true 的物件,很適合用在搜尋符合條件的資料 ```javascript= let newArray = arr.filter(callback(element[, index[, array]])[, thisArg]) // element(必備)原陣列目前所迭代處理中的元素。 // index目前所迭代處理中的元素之索引。 // array選擇性呼叫 filter 方法的陣列。 const words = ['spray', 'limit', 'elite', 'exuberant', 'destruction', 'present']; let filter_result = words.filter(word => word.length > 6); console.log(filter_result); // [ 'exuberant', 'destruction', 'present' ] ``` ##### <i class="fa fa-caret-right" aria-hidden="true"></i> reduce() ```javascript= let newArray = arr..reduce(callback[accumulator, currentValue, currentIndex, array], initialValue) ``` reduce() 和其他幾個差異就很大了,他可以與前一個回傳的值再次作運算,參數包含以下: - accumulator: 前一個參數,如果是第一個陣列的話,值是以另外傳入或初始化的值 - currentValue: 當前變數 - currentIndex: 當前索引 - array: 全部陣列 :::info 簡單來說就是 reduce 方法跟其他陣列方法(例如:map、filter)的差別是它會 return 一個值,而不是一個新陣列 ::: ```javascript= const arr = [1, 2, 3, 4, 5]; const reduceArr = arr.reduce(function(accumulator, currentValue) { console.log(`accumulator:${accumulator}`); // 1, 3, 6, 10 console.log(`currentValue:${currentValue}`); // 2, 3, 4, 5 return accumulator + currentValue }); console.log(`reduceArr: ${reduceArr}`); ``` ![](https://i.redd.it/9dm5u0f5mrr41.jpg) 出處:[JavaScript reduce function](https://ithelp.ithome.com.tw/articles/10243053) ### <i class="fa fa-chevron-circle-right" aria-hidden="true"></i> 物件 - 物件可以代表多個或複合值 - 實質上物件是一種==容器== - 物件內容稱為屬性(Property),是由名稱(鍵)與值組成 - 陣列是有序的,物件是無序的 物件宣告方式: ```javascript= // 大括號成對存在,用來表達物件的內容 // 建立物件與內容方式I let mobile = { brand: "Samsung", model: "S22", color: "White" }; // 建立物件與內容方式II let mobile2 = {}; // 空物件 mobile.brand = "Apple"; mobile.model = "iphone 13 pro"; mobile["color"] = "Gold"; console.log(mobile); console.log(mobile2); ``` 物件的運用 ```javascript= let course = { teacher: "阿奇", className: "網際網路應用", students: ["貝蒂", "羅利", "諾麗", "塔格", "海皮"], classRoom: "工206" }; ``` ## 6、迴圈 ### 迴圈基本操作 - 迴圈結構必須包含判別式及條件變數 - 初始迴圈變數 - 迴圈條件 - 遞增迴圈變數 ```javascript= //初始狀態;條件;遞增 for(let i = 0; i < 3; i++) { console.log(i); } ``` ### 迴圈與陣列物件 ```javascript= let classes = [ { className: "網際網路應用", class: "機械四甲", teacher: "丁丁", students: 40 }, { className: "MATLAB", class: "機械一甲", teacher: "丁丁", students: 92 }, { className: "工業大數據", teacher: "鍾文仁", students: 30 }]; ``` ## 7、函式 ### 參考 - [重新認識 JavaScript: Day 10 函式 Functions 的基本概念](https://ithelp.ithome.com.tw/articles/10191549) - JavaScript學習手冊 ### 函式寫法 - 基本函式 ```javascript= //宣告函式 函式名稱(){} function calculate() { alert("執行計算函式"); alert("請問你要計算什麼"); } // 執行函式 calculate(); ``` - 函式帶參數(引數) ```javascript= function calculate(num){ let total = num * 25; alert("計算後的數值為"+total); } calculate(6); ``` :::warning 引數只會在函式裡生存,就算在函式外有相同的名稱 ::: ```javascript= function f(x) { console.log("inside f: x = " + x); x = 5; console.log("inside f: x = " + x +"(after assignment)"); } let x = 3; console.log("before calling f: x = "+ x); f(x); console.log("after calling f: x = "+ x); ``` #### 預設引數 在ES6中有一個新功能,可以指派引數的預設值,一般來說,當我們沒有提供引數的數值時,我們會得到undefined值 ```javascript= function f(a, b="default", c = 3) { /*---- 初始:let a, b = "default", c = 3; f() => a; b = "default"; c = 3; f(5) => a = 5; b = "default"; c = 3; f(5,6) => a = 5; b = 6; c = 3; f(5,6,7) => a = 5; b = 6; c = 7; -----*/ console.log(a+"-"+b+"-"+c); } f(); f(5); f(5, 6); f(5, 6, 7); ``` - 函式返回結果 ```javascript= function getCalResult(num) { let returnValue = num * 12; return returnValue; } let getResult = getCalResult(12); alert("getResult Value:"+getResult); ``` ### 呼叫 V.S. 參考 在JavaScript中,函式是物件,因此如同其他的物件資料型態,可以將它到處傳遞與賦值 - 當我們在函式名稱後面加上括號例如calculate()時,JavaScript就知道我們要==呼叫==這個函數 - 當我們在函式名稱後面沒有加括號時,只是參考函式,與其他任何值一樣,==函式不會被呼叫== - 這點讓JavaScript有更多彈性,比如說可以將函式指派給一個變數,我們就可以用其他名稱來呼叫這個函式 ```javascript= function getGreeting() { console.log("hello world!"); } getGreeting(); getGreeting; //----參考---- console.log("將函式指派給refFunction這個變數"); const refFunction = getGreeting; refFunction(); //將函式放入物件中 let obj = {}; obj.f = getGreeting; console.log("執行物件內函式"); obj.f(); // 將函式放入陣列中 let arr = [1, 2, 3]; arr[2] = getGreeting; console.log("執行陣列內元素的函式"); arr[2](); ``` ### 提升 Hoisting 除了==var==這個關鍵字使得變數有提升機制外,函式也具有提升的機制 ```javascript= square(2); // 4 function square(number) { console.log(number * number); } ``` ### 函式運算式(Function Expressions) 透過 ==變數名稱 = function([參數]){ ... };== 的方式,將一個函式透過 = 指定給某個變數 ```javascript= var square = function (number) { console.log(number * number); }; ``` :::warning 函式運算式**不具提升作用** ::: ```javascript= square(2); // Uncaught TypeError: square is not a function var square = function (number) { console.log(number * number); }; ``` ## 8、DOM ### 什麼是DOM #### 參考資料 - [什麼是 DOM ?](https://yuwensaf.github.io/9c95b00e/) - [w3scools - HTML DOM Documents](https://www.w3schools.com/jsref/dom_obj_document.asp) ##### 這是一個html文件 ![](https://i.imgur.com/FqcrTFn.jpg) ##### 轉換成DOM ![](https://i.imgur.com/DmvuqBx.jpg) ### getElementById、querySelector、querySelectorAll 1. getElementById、querySelector可以選取到一個節點標籤 2. querySelectorAll可以選取到多個符合條件的節點資料,該資料會是一個**陣列資料** - HTML ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <link rel="stylesheet" href="css/style.css"> <title>Document</title> </head> <body> <h1 id="titleId" class="titleClass">title</h1> <h2>標題1</h2> <h2>標題2</h2> <h2>標題3</h2> <h2>標題4</h2> <h2>標題5</h2> <script src="js/all.js"></script> </body> </html> ``` - JavaScript ```javascript= /* 1.用Id取元素 → #Id 2.用Class取元素 → .Class名稱 3.用標籤取元素 → 標籤 */ var el = document.querySelector('.titleClass em'); el.textContent ='123'; // querySelectorAll 會回傳陣列資料 let titles = document.querySelectorAll('h2'); titles.forEach(function(el, idx) { el.textContent = `物件${idx+1}` }) ``` ### 屬性的設定與取得 #### setAttribute :::info element.setAttribute(name, value) ::: #### getAttribute :::info Element.getAttribute(attributeName) ::: ### 操控HTML #### innerHTML - 方法 : 組完字串後,傳進語法進行渲染。 - 優點 : **清空**在上,效能快。 - :star:缺點 : 資安危險,要確保來源,不然容易發生==xss==攻擊。 HTML ```htmlembedded= <!DOCTYPE html> <html> <body> <div id="main" class="container">google</div> </body> </html> ``` JavaScript ```javascript= // let mainEl = document.getElementById("main"); let mainEl= document.querySelector('#main'); // mainEl.innerHTML = "<a href='https://www.google.com/''>前往google網站</a>"; ``` 實務應用 - HTML ```htmlembedded= <!DOCTYPE html> <html> <body> <div id="main" class="container"> <ul id="list" class="faculty"> </ul> </div> </body> </html> ``` - JavaScript ```javascript= let facultyList = [ { name: "陳夏宗", title: "教授" }, { name: "許政行", title: "教授" }, { name: "康淵", title: "教授" }, { name: "鍾文仁", title: "教授" }, { name: "丁鏞", title: "教授" }, { name: "張耀仁", title: "教授" }, { name: "陳冠宇", title: "教授" }, { name: "范憶華", title: "教授" }, { name: "李有璋", title: "教授" }, { name: "翁輝竹", title: "教授" }, { name: "黃信行", title: "教授" }, { name: "吳政達", title: "教授" }, ]; let listLen = facultyList.length; let ulEl = document.querySelector(".faculty"); // 等同於document.querySelector("#list"); let htmlStr = ""; for(let i = 0; i < listLen; i++){ ulEl.innerHTML = "<li>"+facultyList[i].name+"<em>"+facultyList[i].title+"</em></li>"; } ``` - CSS ```css= .faculty li { margin: 10px; font-size: 24px; } .faculty li em { color: blue; } ``` #### [codepen完整程式](https://codepen.io/augustting/pen/gOvdPvQ) #### createElement - 方法 : 用 DOM 節點來處理。 - 優點 : 安全性高。 - 缺點 : 效能差。 :::info 需使用==appendChild==將Element加入父節點中 ::: HTML ```htmlembedded= <!DOCTYPE html> <html> <body> <div id="main" class="container"> <h4>工學院</h4> <ul id="depts"></ul> </div> </body> </html> ``` JavaScript ```javascript= // let mainEl = document.getElementById("main"); let mainEl= document.querySelector('#list'); // mainEl.innerHTML = "<a href='https://www.google.com/''>前往google網站</a>"; ``` #### [實務應用](https://codepen.io/augustting/pen/oNEEerx) - HTML ```htmlembedded= <html> <body> <div id="main" class="container"> <ul id="list"> <li><a href="https://www.google.com">Google</a></li> </ul> </div> </body> </html> ``` - JavaScript ```javascript= const deptList = [ { deptName: "機械工程", site: "https://mw.cycu.edu.tw" }, { deptName: "化學工程", site: "https://che.cycu.edu.tw/" }, { deptName: "醫學工程", site: "https://be.cycu.edu.tw/" }, { deptName: "土木工程", site: "https://ce.cycu.edu.tw/wSite/mp?mp=4200" }, { deptName: "環境工程", site: "https://bee.cycu.edu.tw/" } ]; let ulEl = document.querySelector("#depts"); ``` - CSS ```css= #depts li { font-size: 24px; } #depts li a { text-decoration: none; color: grey; } ``` #### [createElement完整範例](https://codepen.io/augustting/pen/oNEEerx) ## 9、event (事件) ### 參考文件 [w3schools - HTML DOM Events](https://www.w3schools.com/jsref/dom_obj_event.asp) ### event物件-元素資訊 ```htmlembedded= <!DOCTYPE html> <html> <body> <input type="button" value="Click" class="btn"> </body> </html> ``` ```javascript= let el = document.querySelector('.btn'); el.onclick = function(e) { console.log(e); alert("CYCU"); } ``` ### HTML元素綁定事件方式 1. 最古老的作法,在元素的屬性上直接給定事件 :::warning 不推薦使用,因為會有資安問題 ::: ```htmlembedded= <!DOCTYPE html> <html> <body> <input type="button" value="Click" class="btn" onclick="hello()"> <!--<input type="button" value="Click" class="btn" onclick="alert('CYCU')"> --> </body> </html> ``` ```javascript= /*let el = document.querySelector('.btn'); el.onclick = function(e) { console.log(e); alert("CYCU"); }*/ function hello() { alert("CYCU"); } ``` 2. 在JavaScript來進行事件綁定 ```htmlembedded= <!DOCTYPE html> <html> <body> <input type="button" value="Click" class="btn"> </body> </html> ``` ```javascript= // 方法 I let el = document.querySelector('.btn'); el.onclick = function(e) { console.log(e); alert("CYCU"); } // 方法 II addEventListener(事件名稱, 事件處理器(函式), 捕獲或冒泡的切換) // false → event bubbling // true → event capturing // 捕獲或冒泡的切換,預設是false (冒泡) //監聽 el.addEventListener('click',function(e){ alert('hello'); },false) ``` ### onclick and addEventListener差異 - `onclick`程式由上往下執行,事件會被底下的==覆蓋==掉 - `addEventListener`可以==疊加事件== ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <title>Document</title> </head> <body> <input type="button" class="btnSingle" value="single event"> <input type="button" class="btnMulti" value="multiple event"> </body> </html> <script src="js/all.js"></script> ``` ```javascript= var singleEl = document.querySelector('.btnSingle'); singleEl.onclick = function (){ alert('singile-1'); } singleEl.onclick = function (){ alert('singile-2'); } // 事件監聽 var multiEl = document.querySelector('.btnMulti'); multiEl.addEventListener('click',function(){ alert('Multi-1') },false) multiEl.addEventListener('click',function(){ alert('Multi-2') },false) ``` ### 事件傳播:捕捉(Capturing)與冒泡(Bubbling) - 冒泡 - 事件向外傳遞 - 捕捉 - 事件向內傳遞 ```htmlembedded= <!DOCTYPE html> <html lang="en"> <head> <link rel="stylesheet" href="css/style.css"> <title>Document</title> </head> <body class="body"> <div class="box"></div> </body> </html> <script src="js/all.js"></script> ``` ```javascript= var el = document.querySelector('.box'); el.addEventListener('click', function (e) { e.stopPropagation(); alert('box'); console.log(e); }); var elBody = document.querySelector('.body'); elBody.addEventListener('click', function (e) { e.stopPropagation(); alert('body'); console.log('body'); }); ``` ### 中止事件傳遞`stopPropagation()` ### 取消預設觸發行為`preventDefault()` ### `change`事件應用 ## 【補充】JavaScript 中 By Reference 和 By Value 的重要觀念 參考資料 - [談談 JavaScript 中 by reference 和 by value 的重要觀念](https://pjchender.blogspot.com/2016/03/javascriptby-referenceby-value.html) - [深入探討 JavaScript 中的參數傳遞:call by value 還是 reference?](https://blog.techbridge.cc/2018/06/23/javascript-call-by-value-or-reference/) @ TechBridge by Huli - [重新認識 JavaScript: Day 05 JavaScript 是「傳值」或「傳址」?](https://ithelp.ithome.com.tw/articles/10191057) @ iThome 鐵人賽 - 重新認識 JavaScript ### <i class="fa fa-square" aria-hidden="true"></i> By Value (類似於C語言中一般變數的使用) 當我們在建立 primitive type 的變數時(數字、字串、布林),假設我把a 指定成一個 primitive type 的時候,a 會在記憶體中存在一個自己的位置(假設叫做0x001)。這時候,當我指定另一個變數 b,它的值等同於 a 的時候,b 實際上會建立另一個獨立的記憶體位置(假設叫做0x002),接著再把 a 的值存在這個獨立的記憶體位置。也就是說, a 和 b 其實是**存在於兩個不同的記憶體位置**,因此彼此並不會乎相干擾影響,這種情況,我們就稱為==By Value== ,而這種情形會發生在 primitive type 的變數。 :::info 在 JavaScript 中 primitive type(Boolean, String, Number, null, undefined)都屬於 By Value。 ::: ![](https://i.imgur.com/oiNuDQC.png) > 圖片來源:[[Udemy] JavaScript: Understanding the Weird Parts](https://www.udemy.com/understand-javascript/) ### <i class="fa fa-square" aria-hidden="true"></i> By Reference (類似於C語言中的指標) 當我將變數 a 設立成一個Object(或function)時,這時候,一樣會在記憶體中給它一個位置(假設叫做0x001);但是當我建立一個變數 b,並且把變數 b 的值等同於 a 時,這時候並不會再給它一個新的位置,而是一樣指定到物件 a 的位置(即0x001),實際上是不會有新的東西被建立,`變數 a 和 b 都會被指稱到相同的位置(即0x001)`,因此,當 a 的值改變的時候 b 的值也會改變,因為它們實際上是指稱到相同的位置,這種情形我們就稱為==By Reference==,這樣情況會發生在 Object 和 Function 這種變數。 :::info 在 JavaScript 中 Objects(Object, Array, Function)都屬於 By Reference。 ::: ![](https://i.imgur.com/9hhFiL1.png) > 圖片來源:[[Udemy] JavaScript: Understanding the Weird Parts](https://www.udemy.com/understand-javascript/) ### <i class="fa fa-square" aria-hidden="true"></i> By Sharing