###### tags: `學期 2-1` # 【選修】作業延伸練習 ## 新單元上架:【選修】作業延伸練習 Hi {{user_nickname}}, 第三週的學習狀況還好嗎~是不是覺得 DOM 又是另一個大魔王呀 XD 在 DOM 的學習中,很常因為抽象的概念,讓同學完成作業後會有「結果是正確的沒錯,但我不太知道其中發生了什麼事情?」的困擾。 AC 團隊特別搜集了幾個,同學們在「寫完作業後」還是會有的疑惑,推薦**已經完成本週作業的同學**,可以挑戰回答延伸練習的問題!但若還在進度學習的同學,還是先以主課程內容優先喔,之後有餘裕再回來補上【選修】的內容就好! #### 新單元上架,完成 Week 3 作業後,可以試試回答延伸練習的問題喔! 觀看新單元 👉 [【選修】作業延伸練習](https://lighthouse.alphacamp.co/courses/98/units/28243) <div style="width:100%"><img style="max-width:1000px; width:100%;" src="https://assets-lighthouse.alphacamp.co/uploads/image/file/21381/banner.001.png"></div> ## week 1 >**學習成果與目標** >・加強呼叫函式、組合函式練習 >・提升程式邏輯裡解練習 恭喜你完成 Week 1 作業來到這個單元,有沒有對函式的使用更加上手了呢? 函式的應用非常廣泛多元,在開始學習時,需要花一點時間梳理呼叫函式的邏輯,除了作業以外,我們也準備了幾個綜合練習題,讓你練習看看,自己是否有真的了解背後的觀念。 一起來練習回答以下題目吧! #### Exercise 1:用加減乘法練習組合函式、呼叫函式 在起手式程式碼中,有加法、減法、乘法三個已被宣告的函式 <pre class="prettyprint"> function addition (x, y) { return x + y } function subtraction (x , y) { return x - y } function multiplication (x, y) { let result = 0; for (let i = 0; i < y; i++) { result = addition(result, x) } return result } // write your code here </pre> **Q:請運用這三個函式,計算出下列數學式** 1. 3 + (5 - 1) 2. 5 - (3 * 4) 3. (4 - 2) * (1 + 3) 4. ((5 - 7) + 3) * 2 提示:可以使用 console.log 印出計算結果,以確認計算結果是否正確 <small>將你的答案分享到留言區與同學交流</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#exercise1" aria-expanded="false" aria-controls="exercise1">參考答案</a> <div class="collapse" id="exercise1"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> <pre class="prettyprint"> function addition (x, y) { return x + y } function subtraction (x , y) { return x - y } function multiplication (x, y) { let result = 0; for (let i = 0; i < y; i++) { result = addition(result, x) } return result } // write your code here // 3 + (5 - 1) = 7 console.log(addition(3, subtraction(5, 1))) // 5 - (3 * 4) = -7 console.log(subtraction(5,multiplication(3, 4))) // (4 - 2) * (1 + 3) = 8 console.log(multiplication(subtraction(4, 2), addition(1, 3))) // ((5 - 7) + 3) * 2 = 2 console.log(multiplication(addition(subtraction(5,7), 3) ,2)) </pre> </div> </div> #### Exercise 2:理解雙迴圈的邏輯 在前面的作業,我們有推薦大家可以使用推薦一個視覺化工具:http://pythontutor.com/javascript.html 來輔助自己釐清程式邏輯,下面的練習就是來讓同學刻意去意識迴圈的邏輯。 **Q:先閱讀起手式程式碼,並試著理解雙迴圈邏輯。請試著在起手式程式碼中加入 console.log 讓程式印出相同的指定訊息。** #### 起手式程式碼 <pre class="prettyprint"> const players = [ { name: 'Ellen', email: 'ellen@example.com', ticket: 'BB1750' }, { name: 'Walter', email: 'walter@example.com', ticket: 'EI5724' }, { name: 'Walter', email: 'walter@example.com', ticket: 'EI5724' }, { name: 'Tim', email: 'tim@example.com', ticket: 'CK4592' }, { name: 'Kevin', email: 'kevin@example.com', ticket: 'TT1804' } ] const blackList = [ { name: 'Tim', email: 'tim@example.com', ticket: 'CK4592' }, { name: 'Walter', email: 'walter@example.com', ticket: 'EI5724' } ] for (let i = players.length - 1; i >= 0; i--) { for (let j = 0; j < blackList.length; j++) { if (players[i].email === blackList[j].email) { players.splice(i, 1) } } } console.log(players) </pre> #### 指定訊息 <pre class="prettyprint"> ===== i 是 4 的外層迴圈開始 ===== ----- j 是 0 的內層迴圈開始 ----- players[i].email 是 kevin@example.com,blackList[j].email 是 tim@example.com ----- j 是 0 的內層迴圈結束 ----- ----- j 是 1 的內層迴圈開始 ----- players[i].email 是 kevin@example.com,blackList[j].email 是 walter@example.com ----- j 是 1 的內層迴圈結束 ----- ===== i 是 4 的外層迴圈結束 ===== ===== i 是 3 的外層迴圈開始 ===== ----- j 是 0 的內層迴圈開始 ----- players[i].email 是 tim@example.com,blackList[j].email 是 tim@example.com 發現黑名單,故刪除。刪除後的 players[i].email 是 kevin@example.com ----- j 是 0 的內層迴圈結束 ----- ----- j 是 1 的內層迴圈開始 ----- players[i].email 是 kevin@example.com,blackList[j].email 是 walter@example.com ----- j 是 1 的內層迴圈結束 ----- ===== i 是 3 的外層迴圈結束 ===== ===== i 是 2 的外層迴圈開始 ===== ----- j 是 0 的內層迴圈開始 ----- players[i].email 是 walter@example.com,blackList[j].email 是 tim@example.com ----- j 是 0 的內層迴圈結束 ----- ----- j 是 1 的內層迴圈開始 ----- players[i].email 是 walter@example.com,blackList[j].email 是 walter@example.com 發現黑名單,故刪除。刪除後的 players[i].email 是 kevin@example.com ----- j 是 1 的內層迴圈結束 ----- ===== i 是 2 的外層迴圈結束 ===== ===== i 是 1 的外層迴圈開始 ===== ----- j 是 0 的內層迴圈開始 ----- players[i].email 是 walter@example.com,blackList[j].email 是 tim@example.com ----- j 是 0 的內層迴圈結束 ----- ----- j 是 1 的內層迴圈開始 ----- players[i].email 是 walter@example.com,blackList[j].email 是 walter@example.com 發現黑名單,故刪除。刪除後的 players[i].email 是 kevin@example.com ----- j 是 1 的內層迴圈結束 ----- ===== i 是 1 的外層迴圈結束 ===== ===== i 是 0 的外層迴圈開始 ===== ----- j 是 0 的內層迴圈開始 ----- players[i].email 是 ellen@example.com,blackList[j].email 是 tim@example.com ----- j 是 0 的內層迴圈結束 ----- ----- j 是 1 的內層迴圈開始 ----- players[i].email 是 ellen@example.com,blackList[j].email 是 walter@example.com ----- j 是 1 的內層迴圈結束 ----- ===== i 是 0 的外層迴圈結束 ===== </pre> <small>將你的答案分享到留言區與同學交流</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#exercise2" aria-expanded="false" aria-controls="exercise2">參考答案</a> <div class="collapse" id="exercise2"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> <pre class = "prettyprint"> for (let i = players.length - 1; i >= 0; i--) { console.log(`===== i 是 ${i} 的外層迴圈開始 =====`) for (let j = 0; j < blackList.length; j++) { console.log(`----- j 是 ${j} 的內層迴圈開始 -----`) console.log(`players[i].email 是 ${players[i].email},blackList[j].email 是 ${blackList[j].email}`) if (players[i].email === blackList[j].email) { players.splice(i, 1) console.log(`發現黑名單,故刪除。刪除後的 players[i].email 是 ${players[i].email}`) } console.log(`----- j 是 ${j} 的內層迴圈結束 -----`) } console.log(`===== i 是 ${i} 的外層迴圈結束 =====\n`) } </pre> </div> </div> #### Exercise 3:隨機產生數字 Math.random() 的用法 在第一週的作業中,我們運用 Math.random() 的函式來進行隨機抽獎,為了要抽出符合題目產生號碼,我們會用 Math.floor() 來搭配 Math.random()。 然而,不論是用 Math.floor() 還是 Math.ceil() 都可以產生我們所需的數值區間,以隨機產生 0~9 的整數舉例: <pre class = "pretty print"> // 以 Math.floor() 來寫 0 ≤ Math.random() < 1 0 ≤ Math.random() * 10 < 10 0 ≤ Math.floor(Math.random() * 10) ≤ 9 // 以 Math.ceil() 來寫 0 ≤ Math.random() < 1 0 ≤ Math.random() * 9 < 9 0 ≤ Math.ceil(Math.random() * 9) ≤ 9 </pre> **Q:既然兩種寫法都可以產出 0~9 的整數,為什麼不使用 Math.ceil() 來產生數字呢?請試著用文字寫下你的想法。** 提示:用 MDN 複習三個函式的用法,並試想產生每個數字的情境吧! - [Math.random()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random) - [Math.floor()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor) - [Math.ceil()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil) <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#exercise3" aria-expanded="false" aria-controls="exercise3">參考答案</a> <div class="collapse" id="exercise3"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> 以隨機產出 0~9 的整數為例,因為兩者產生出 0 的機率不同 <br>Math.ceil(Math.random() * 9) 只有在 Math.random() 剛好回傳 0 的時候,才會回傳 0,所以產生出 0 的機率非常非常非常小 <br>可以參考討論串 Random Number, Math.floor(...) vs Math.ceil(...): <p><a href="https://stackoverflow.com/questions/15830658/random-number-math-floor-vs-math-ceil" target="_blank">https://stackoverflow.com/questions/15830658/random-number-math-floor-vs-math-ceil</a> </p> </div> </div> ## week 2 >**學習成果與目標** >・理解如何活用 HTML/CSS 呈現相同畫面 >・能觀察出 HTML 的標籤屬性,並能判斷如何設定屬性達到自己的需求 恭喜你完成 Week 2 作業來到這個單元,通過作業你是否更加掌握 HTML/CSS 的應用了呢? 了解 HTML/CSS 的特性能夠幫助我們更自在地活用它們,讓我們通過以下的題目一起來探索吧! #### Exercise 1: 元素層級堆疊 我們在 [U:認識 Position](https://lighthouse.alphacamp.co/courses/98/units/26010) 學會運用 z-index 來賦予圖層「深度」,而 HTML/CSS 中很多呈現都有不少異曲同工之妙的作法。 ##### Q1: 試試看複製起手式程式碼,你是否能「只更改 CSS,不動任何 HTML」調整這些色塊的堆疊方式呢? <div style="width:100%"> <a href="https://assets-lighthouse.alphacamp.co/uploads/image/file/21826/F2-1________w2Q1.png" target="_blank"><img style="max-width:700px;width:100%;" src="https://assets-lighthouse.alphacamp.co/uploads/image/file/21826/F2-1________w2Q1.png"></a></div> ##### HTML 起手式程式碼 <pre class="prettyprint"> &lt;div class="parent"> &lt;div class="red">&lt;/div> &lt;div class="green">&lt;/div> &lt;div class="blue">&lt;/div> &lt;/div> </pre> ##### CSS 起手式程式碼 <pre class="prettyprint"> div { height: 100px; width: 100px; } .parent { position: relative; } .red { position: absolute; top: 30px; left: 30px; background-color: red; z } .green { position: absolute; top: 20px; left: 20px; background-color: green; } .blue { position: absolute; top: 10px; left: 10px; background: blue; } </pre> <small>你是如何思考的呢?將你的思考過程分享到留言區與同學交流吧!</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#w2-exercise1" aria-expanded="false" aria-controls="w2-exercise1">參考答案</a> <div class="collapse" id="w2-exercise1"> 只要改變 HTML 標籤順序,就可以讓元素依照設計稿的方式排列喔! <div class ="prettyprint"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> <p>&lt;div class="parent"></p> <p>&lt;div class="blue">&lt;/div></p> <p>&lt;div class="green">&lt;/div></p> <p> &lt;div class="red">&lt;/div></p> <p> &lt;/div> </div> </div> </div> #### Exercise 2: 瞭解預設屬性的概念 ##### Q2: 將起手式程式碼放入自己的 codepen 中,將 CSS 中的註解解除,觀察頁面的變化,思考看看為什麼會有這種變化呢? #### HTML 起手式程式碼 <pre class ="prettyprint"> &lt;div> &lt;h1>我是 h1 標題&lt;/h1> &lt;/div> </pre> #### CSS 起手式程式碼 <pre class ="prettyprint"> div { border: 1px solid red; } h1 { border: 1px solid blue; /* margin: 0; */ } </pre> <small>你是如何思考的呢?將你的思考過程分享到留言區與同學交流吧!</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#w2-exercise2" aria-expanded="false" aria-controls="w2-exercise2">參考答案</a> <div class="collapse" id="w2-exercise2"> 因為 h1 標籤有預設的 margin 屬性,可以透過 margin: 0; 設定,將預設屬性覆蓋掉。 關於標籤的預設樣式,可以參考 <p><a href="https://www.w3schools.com/cssref/css_default_values.asp" target="_blank">CSS Default Values Reference</a> </p> </div> ## week 3 >**學習成果與目標** >・針對同學在作業中常見的問題延伸練習 恭喜你完成 Week 3 作業來到這個單元,想必過程中也遇到了不少挑戰,首先鼓勵自己又克服了一個關卡,又學會新東西了! 在 DOM 的學習中,很常因為抽象的概念,讓同學完成作業後會有「結果是正確的沒錯,但我不太知道其中發生了什麼事情?」的困擾,此時,除了繳交作業同時在 Description 區塊提問以外;我們也準備了幾個常見的問題,讓你練習看看,自己是否有真正了解背後的觀念。 一起來練習回答以下題目吧! #### Exercise 1:了解 innerHTML、innerText、textContent 的不同 在 [A15: 找出得分王](https://lighthouse.alphacamp.co/courses/98/assignments/2975)中,使用 innerHTML、innerText、textContent 都可以取出得分欄的分數,完成題目要求的功能,例如: **用 innerHTML** <pre class="prettyprint"> const playerRecords = document.querySelectorAll('.table tbody tr') const thumbsUpIconHTML = '&lt;i class="fa fa-thumbs-up">&lt;/i>' for (let i = 0; i < playerRecords.length; i++) { const score = parseInt(playerRecords[i].children[1].innerHTML, 10) if (score >= 20) { playerRecords[i].children[0].innerHTML += thumbsUpIconHTML } } </pre> **用 innerText** <pre class="prettyprint"> const playerRecords = document.querySelectorAll('.table tbody tr') const thumbsUpIconHTML = '&lt;i class="fa fa-thumbs-up">&lt;/i>' for (let i = 0; i < playerRecords.length; i++) { const score = parseInt(playerRecords[i].children[1].innerText, 10) if (score >= 20) { playerRecords[i].children[0].innerHTML += thumbsUpIconHTML } } </pre> **用 textContent** <pre class="prettyprint"> const playerRecords = document.querySelectorAll('.table tbody tr') const thumbsUpIconHTML = '&lt;i class="fa fa-thumbs-up">&lt;/i>' for (let i = 0; i < playerRecords.length; i++) { const score = parseInt(playerRecords[i].children[1].textContent, 10) if (score >= 20) { playerRecords[i].children[0].innerHTML += thumbsUpIconHTML } } </pre> **Q:到底用哪個比較好,原因是什麼?** <small>將你的答案分享到留言區與同學交流</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#exercise1" aria-expanded="false" aria-controls="exercise1">參考答案</a> <div class="collapse" id="exercise1"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> 目前的使用情境,使用 textContent 比較好。 <p>詳細請參考下一個單元【選修】作業延伸練習:參考答案說明</p> </div> </div> #### Exercise 2:了解使用 document.createElement 可能會踩到的坑,以及對「如何提升程式碼的效能」有初步的認識 在 [A15: 找出得分王](https://lighthouse.alphacamp.co/courses/98/assignments/2975)中,如果是使用 document.createElement 創造出讚的 icon,像這樣: <pre class="prettyprint"> const thumbsUpIcon = document.createElement('i') thumbsUpIcon.classList.add('fa', 'fa-thumbs-up') </pre> **Q:這段程式碼應該要擺在哪個地方(A、B or C):** <pre class="prettyprint"> const playerRecords = document.querySelectorAll('.table tbody tr') // A for (let i = 0; i < playerRecords.length; i++) { const score = parseInt(playerRecords[i].children[1].textContent, 10) // B if (score >= 20) { // C playerRecords[i].children[0].appendChild(thumbsUpIcon) } } </pre> <small>將你的答案分享到留言區與同學交流</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#exercise2" aria-expanded="false" aria-controls="exercise2">參考答案</a> <div class="collapse" id="exercise2"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> 擺在 B 和 C 都可以達成「在得分數 ≥ 20 的球員名字旁加上讚的符號」的目標,而擺在 C 的效能會比較好。 <p>詳細請參考下一個單元【選修】作業延伸練習:參考答案說明</p> </div> </div> #### Exercise 3:迭代的對象是陣列中的元素時,建議不要使用的迴圈方法 在 [A17: 彩子的計分版](https://lighthouse.alphacamp.co/courses/98/assignments/2977)中,如果把 [Model Answer]() 迭代 players 陣列的方式,從 forEach 改成 for…in,像這樣: <pre class="prettyprint"> let players = [ { name: "櫻木花道", pts: 0, reb: 0, ast: 0, stl: 0, blk: 2 }, { name: "流川楓", pts: 30, reb: 6, ast: 3, stl: 3, blk: 0 }, { name: "赤木剛憲", pts: 16, reb: 10, ast: 0, stl: 0, blk: 5 }, { name: "宮城良田", pts: 6, reb: 0, ast: 7, stl: 6, blk: 0 }, { name: "三井壽", pts: 21, reb: 4, ast: 3, stl: 0, blk: 0 } ]; const dataPanel = document.querySelector("#data-panel"); function displayPlayerList(data) { let htmlContent = ""; for (let index in data) { // 改這行 const player = data[index] // 改這行 htmlContent += "&lt;tr>"; for (let key in player) { if (key === "name") { htmlContent += `&lt;td>${player[key]}&lt;/td>`; } else { htmlContent += ` &lt;td>&lt;span style="font-size: 25px">${player[key]}&lt;/span> &lt;i class="fa fa-plus-circle up" aria-hidden="true">&lt;/i> &lt;i class="fa fa-minus-circle down" aria-hidden="true">&lt;/i> &lt;/td> `; } }; // 改這行 htmlContent += "&lt;/tr>"; }; dataPanel.innerHTML = htmlContent; } displayPlayerList(players); </pre> 目前的情況下的確可以成功產生出按鈕。但增加 players 陣列的屬性之後,例如,在呼叫 displayPlayerList(players); 的前一行加上這段程式碼: <pre class="prettyprint"> players.broken = "broken" displayPlayerList(players); </pre> **Q1:會發生什麼事?** **Q2:為什麼會產生這種狀況?** <small>將你的答案分享到留言區與同學交流</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#exercise3" aria-expanded="false" aria-controls="exercise3">參考答案</a> <div class="collapse" id="exercise3"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> Q1 :表格最下方多出一行奇怪的東西 <p>Q2 :這是因為,for … in 在迭代陣列時,除了迭代陣列的 index,也會迭代陣列的其他屬性。</p> <p>詳細請參考下一個單元【選修】作業延伸練習:參考答案說明</p> </div> </div> --- # 【選修】Week 3 作業延伸練習:參考答案說明 請先回答上一個單元的練習題後,再來閱讀參考答案。 #### Exercise 1:了解 innerHTML、innerText、textContent 的不同 **參考答案:目前的使用情境,使用 textContent 比較好。** ##### 1.為什麼不用 innerHTML: 只是要取出字串的話,殺雞焉用牛刀,不需要出動到 innerHTML。因為 innerHTML 會連 HTML 的格式也一起取出。如果哪天網頁改版,分數欄位加上了一些 HTML 格式,例如題目的 HTML 結構,得分欄從 `<td>0</td>` 改成 `<td><div>0</div></td>`,像這樣: <pre class="prettyprint"> &lt;div class="container"> &lt;h1 class="my-5">湘北籃球隊計分板&lt;/h1> &lt;ol> &lt;li>找出&lt;strong>得分數&lt;/strong>大於等於 20 分的球員,在他們的名字旁加上 &lt;i class="fa fa-thumbs-up"> &lt;/i> 符號&lt;/li> &lt;li>你只能使用 JavaScript&lt;/li> &lt;li>已設定 Font Awesome,只需要創造元素並套用 &lt;code>class="fa fa-thumbs-up"&lt;/code>&lt;/li> &lt;/ol> &lt;table class="scoreboard table table-bordered table-hover text-center"> &lt;thead> &lt;tr> &lt;th>球員&lt;/th> &lt;th>得分&lt;/th> &lt;th>籃板&lt;/th> &lt;th>助攻&lt;/th> &lt;th>抄截&lt;/th> &lt;th>阻攻&lt;/th> &lt;/tr> &lt;/thead> &lt;tbody> &lt;tr> &lt;td>櫻木花道&lt;/td> &lt;td>&lt;div>0&lt;/div>&lt;/td> &lt;td>14&lt;/td> &lt;td>0&lt;/td> &lt;td>0&lt;/td> &lt;td>2&lt;/td> &lt;/tr> &lt;tr> &lt;td>流川楓&lt;/td> &lt;td>&lt;div>30&lt;/div>&lt;/td> &lt;td>6&lt;/td> &lt;td>3&lt;/td> &lt;td>3&lt;/td> &lt;td>0&lt;/td> &lt;/tr> &lt;tr> &lt;td>赤木剛憲&lt;/td> &lt;td>&lt;div>16&lt;/div>&lt;/td> &lt;td>10&lt;/td> &lt;td>0&lt;/td> &lt;td>0&lt;/td> &lt;td>5&lt;/td> &lt;/tr> &lt;tr> &lt;td>宮城良田&lt;/td> &lt;td>&lt;div>6&lt;/div>&lt;/td> &lt;td>0&lt;/td> &lt;td>7&lt;/td> &lt;td>6&lt;/td> &lt;td>0&lt;/td> &lt;/tr> &lt;tr> &lt;td>三井壽&lt;/td> &lt;td>div>21&lt;/div>&lt;/td> &lt;td>4&lt;/td> &lt;td>3&lt;/td> &lt;td>0&lt;/td> &lt;td>0&lt;/td> &lt;/tr> &lt;/tbody> &lt;/table> &lt;/div> </pre> 那麼 innerHTML 版本的做法就會失效,無法在得分數 ≥ 20 的球員名字旁加上讚的符號。 這是因為 `playerRecords[i].children[1].innerHTML` 取出的值會變成字串,例如 `“<div>0</div>"`,於是經過 `parseInt` 的計算之後,最終 `score` 變數裡儲存的值會是 `NaN`,導致 `score >= 20` 的值是 `false`。可以用 console.log 觀察到這個現象,像這樣: <pre class="prettyprint"> // 用 innerHTML const playerRecords = document.querySelectorAll('.table tbody tr') const thumbsUpIconHTML = '&lt;i class="fa fa-thumbs-up">&lt;/i>' for (let i = 0; i < playerRecords.length; i++) { const score = parseInt(playerRecords[i].children[1].innerHTML, 10) console.log("innerHTML: ", playerRecords[i].children[1].innerHTML) console.log("score: ", score) console.log("score >= 20: ", score >= 20) console.log("=====我是分隔線=====") if (score >= 20) { playerRecords[i].children[0].innerHTML += thumbsUpIconHTML } } </pre> 印出來的內容: <div style="width:100%"> <a href="https://assets-lighthouse.alphacamp.co/uploads/image/file/21566/Screen_Shot_2022-06-25_at_18.12.22.png" target="_blank"><img style="max-width:700px;width:100%;" src="https://assets-lighthouse.alphacamp.co/uploads/image/file/21566/Screen_Shot_2022-06-25_at_18.12.22.png"></a></div> 若是使用 innerText 或 textContent,即使多了 div 標籤,依然可以在得分數 ≥ 20 的球員名字旁加上讚的符號。 ##### 2. 為什麼不用 innerText 如果使用 innerText 和 textContent 程式都可以順利運作的話,建議優先使用 textContent,避免產生[回流](https://developer.mozilla.org/zh-TW/docs/Glossary/Reflow),可以節省瀏覽器的效能。 因為 innerText 在某種程度上會計算 CSS 樣式(可以參考:[JavaScript學習筆記--innerText 與 textContent](https://uu9924079.medium.com/javascript%E5%AD%B8%E7%BF%92%E7%AD%86%E8%A8%98-innertext-%E8%88%87-textcontent-755b8b93f13b))瀏覽器為了確保取得的 CSS 樣式是最新的,於是引發了回流。 「回流」在現階段是一個比較進階的概念,目前不太能理解也沒關係,可以先對「如果使用 innerText 和 textContent 程式都可以順利運作的話,建議優先使用 textContent」這件事有個印象就好。 #### Exercise 2:了解使用 document.createElement 可能會踩到的坑,以及對「如何提升程式碼的效能」有初步的認識 **參考答案:擺在 B 和 C 都可以達成「在得分數 ≥ 20 的球員名字旁加上讚的符號」的目標,而擺在 C 的效能會比較好。** ##### 1.為什麼不能放在 A 執行後會發現,只有三井壽名字旁有讚。實際上發生的事情是,因為從頭到尾只有產生一個讚的 icon,icon 會先放到流川楓名字旁,最後又被移動到三井壽名字旁。我們可以用 console.log 觀察到觀察 icon 被移動的過程,像這樣: <pre class="prettyprint"> const playerRecords = document.querySelectorAll('.table tbody tr') // A const thumbsUpIcon = document.createElement('i') thumbsUpIcon.classList.add('fa', 'fa-thumbs-up') for (let i = 0; i < playerRecords.length; i++) { console.log(`====== for 回圈第 ${i} 圈開始 ======`) const score = parseInt(playerRecords[i].children[1].textContent, 10) // B if (score >= 20) { // C playerRecords[i].children[0].appendChild(thumbsUpIcon) } console.log(playerRecords[1].children[0].innerHTML) console.log(playerRecords[4].children[0].innerHTML) console.log(`====== for 回圈第 ${i} 圈結束 ======`) } </pre> 印出來的內容: <div style="width:100%"> <a href="https://assets-lighthouse.alphacamp.co/uploads/image/file/21567/Screen_Shot_2022-06-25_at_18.09.34.png" target="_blank"><img style="max-width:700px;width:100%;" src="https://assets-lighthouse.alphacamp.co/uploads/image/file/21567/Screen_Shot_2022-06-25_at_18.09.34.png"></a></div> ##### 2. 為什麼放在 C 比放在 B 好 放在 B: <pre class="prettyprint"> const playerRecords = document.querySelectorAll('.table tbody tr') // A for (let i = 0; i < playerRecords.length; i++) { const score = parseInt(playerRecords[i].children[1].textContent, 10) // B const thumbsUpIcon = document.createElement('i') thumbsUpIcon.classList.add('fa', 'fa-thumbs-up') if (score >= 20) { // C playerRecords[i].children[0].appendChild(thumbsUpIcon) } } </pre> 會變成每次 for 回圈一定會宣告一個新的 thumbsUpIcon 變數,但如果球員的得分數不符合「得分數 ≥ 20」,這個變數就用不到。變成讓電腦花費時間和記憶體,去宣告一個不會用到的東西。 放在 C: <pre class="prettyprint"> const playerRecords = document.querySelectorAll('.table tbody tr') // A for (let i = 0; i < playerRecords.length; i++) { const score = parseInt(playerRecords[i].children[1].textContent, 10) // B if (score >= 20) { // C const thumbsUpIcon = document.createElement('i') thumbsUpIcon.classList.add('fa', 'fa-thumbs-up') playerRecords[i].children[0].appendChild(thumbsUpIcon) } } </pre> 則能保證,電腦花費時間和記憶體宣告的 thumbsUpIcon 變數,一定會被使用到。 #### Exercise 3:迭代的對象是陣列中的元素時,建議不要使用的迴圈方法 **參考答案:表格最下方多出一行奇怪的東西。這是因為,for … in 在迭代陣列時,除了迭代陣列的 index,也會迭代陣列的其他屬性。** ##### 1.表格最下方多出一行奇怪的東西,如圖 <div style="width:100%"> <a href="https://assets-lighthouse.alphacamp.co/uploads/image/file/21568/Screen_Shot_2022-06-25_at_22.33.34.png" target="_blank"><img style="max-width:700px;width:100%;" src="https://assets-lighthouse.alphacamp.co/uploads/image/file/21568/Screen_Shot_2022-06-25_at_22.33.34.png"></a></div> ##### 2. 為什麼會產生這種狀況? 這是因為,for … in 在迭代陣列時,除了迭代陣列的 index,也會迭代陣列的其他屬性。因為 <pre class="prettyprint"> players.broken = "broken" </pre> 這一行幫 players 陣列新增了名叫「broken」的屬性,屬性的值是 "broken” 這個字串,因此也會迭代到這個屬性,並在內層的 for…in 回圈迭代 "broken” 這個字串(字串第 0 個位置是字母 b,字串第 1 個位置是字母 r ⋯⋯依此類推),最終產生我們看到的畫面。 我們可以使用 console.log 觀察到這個現象: <pre class="prettyprint"> let players = [ { name: "櫻木花道", pts: 0, reb: 0, ast: 0, stl: 0, blk: 2 }, { name: "流川楓", pts: 30, reb: 6, ast: 3, stl: 3, blk: 0 }, { name: "赤木剛憲", pts: 16, reb: 10, ast: 0, stl: 0, blk: 5 }, { name: "宮城良田", pts: 6, reb: 0, ast: 7, stl: 6, blk: 0 }, { name: "三井壽", pts: 21, reb: 4, ast: 3, stl: 0, blk: 0 } ]; const dataPanel = document.querySelector("#data-panel"); function displayPlayerList(data) { let htmlContent = ""; for (let index in data) { console.log(`===== 外層 for in 回圈 index ${index} 開始 =====`) const player = data[index] console.log("player: ", player) htmlContent += "&lt;tr>"; for (let key in player) { console.log(`----- 內層 for in 回圈 key ${key} 開始 -----`) console.log("player[key]: ", player[key]) if (key === "name") { htmlContent += `&lt;td>${player[key]}&lt;/td>`; } else { htmlContent += ` &lt;td>&lt;span style="font-size: 25px">${player[key]}&lt;/span> &lt;i class="fa fa-plus-circle up" aria-hidden="true">&lt;/i> &lt;i class="fa fa-minus-circle down" aria-hidden="true">&lt;/i> &lt;/td> `; } console.log(`----- 內層 for in 回圈 key ${key} 結束 -----`) }; htmlContent += "&lt;/tr>"; console.log(`===== 外層 for in 回圈 index ${index} 結束 =====`) }; dataPanel.innerHTML = htmlContent; } players.broken = "broken" displayPlayerList(players); </pre> 在新增 broken 屬性後,for…in 回圈會額外印出的資訊如下: <div style="width:100%"> <a href="https://assets-lighthouse.alphacamp.co/uploads/image/file/21569/Screen_Shot_2022-06-25_at_22.45.55.png" target="_blank"><img style="max-width:700px;width:100%;" src="https://assets-lighthouse.alphacamp.co/uploads/image/file/21569/Screen_Shot_2022-06-25_at_22.45.55.png"></a></div> 參考文章:MDN 文件在 for…in 的文件中有個小節 [Array iteration and for...in](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in#array_iteration_and_for...in) 建議,如果我們需要處理的只有陣列裡的元素(也就是,用整數 index 就可以存取的那些元素,例如 `array[0]`, `array[1]` 等等),沒有要處理陣列的其他屬性時,不要使用 for…in 來迭代,而是改成使用 forEach, for…of 或傳統的 for 迴圈(也就是 `for (let i = 0; i < array.length; i++) {}` 這種格式的迴圈) --- # 【選修】Week 4 作業延伸練習 >**學習成果與目標** >・針對同學在作業中常見的問題延伸練習 恭喜你完成 Week 4 作業來到這個單元,能夠操作 DOM、設置事件,讓網頁開始能夠「動起來」是不是覺得又能夠實踐更多功能、更有成就感了! 如果想要幫助自己更熟練、或是繼續延伸學習,來練習回答以下題目吧: #### Exercise 1:My Favorite Movies:按讚與刪除 延伸練習 繼作業 My Favorite Movies:按讚與刪除 功能之後,畫面又新增了「超級讚」的綠色按鈕,按一下可以幫電影的 Rating 加十分,像這樣: <div style="width:100%"> <a href="https://assets-lighthouse.alphacamp.co/uploads/image/file/21693/ezgif-1-1f91f6f8c6.gif" target="_blank"><img style="max-width:700px;width:100%;" src="https://assets-lighthouse.alphacamp.co/uploads/image/file/21693/ezgif-1-1f91f6f8c6.gif"></a></div> **Q: 除了原本的按讚與刪除功能,請加碼實作「按綠色按鈕加十分」的功能** 起手式程式碼:https://codepen.io/rossignol/pen/ExEjywQ (請接續完成 JavaScript 的部分) <small>將你的答案分享到留言區與同學交流</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#exercise1" aria-expanded="false" aria-controls="exercise1">參考答案</a> <div class="collapse" id="exercise1"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> CodePen:https://codepen.io/rossignol/pen/abYOZwq </div> </div> #### Exercise 2:練習思考網頁改版時,程式碼的易維護性 觀察這份 [CodePen](https://codepen.io/rossignol/pen/xxWGjmK): - 觀察紅色按鈕的刪除電影功能是否正常運作 - 老闆說要新增電影收藏功能,於是這份 CodePen 經歷了網頁改版:新增了 class name 是 btn-danger 的紅色愛心按鈕(請把第 20~50 行變成註解,把第 53~84 行的註解取消,以模擬網頁改版) 結果,網頁改版後,點擊紅色愛心按鈕時,電影會被刪除。導致我們需要回過頭去修改刪除電影的邏輯,以免按下愛心按鈕時發生不符合需求的情況。 **Q:一開始在撰寫刪除電影的邏輯時,有什麼比對刪除按鈕的寫法,可以不受這次的網頁改版影響?** <small>將你的答案分享到留言區與同學交流</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#exercise2" aria-expanded="false" aria-controls="exercise2">參考答案</a> <div class="collapse" id="exercise2"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> 分析問題成因:為什麼按紅色愛心按鈕,居然會把電影刪掉? <p>該份 CodePen 的 class name:</p> <li>btn 是 bootstrap 用來控制按鈕樣式的</li> <li>btn-sm 是 bootstrap 用來控制尺寸的</li> <li>btn-danger 是 bootstrap 用來控制按鈕顏色的</li> <p>該份 CodePen 使用 btn-danger 這個 class name 比對刪除按鈕。而網頁改版後,增加了 classname 一樣是 btn-danger 的 bootstrap 按鈕,於是紅色愛心按鈕也會比對成功,執行刪除電影的邏輯。</p> <p>回到原本的問題:一開始在撰寫刪除電影的邏輯時,有什麼比對刪除按鈕的寫法,可以不受這次的網頁改版影響?</p> <p>不要使用 bootstrap 的 class name 比對元素,而是在 HTML 中自創專屬的 class name 用於比對元素。那麼程式就不會在網頁改版,新增其他 bootstrap 按鈕後,發生奇怪的事情。像這樣:[CodePen](https://codepen.io/rossignol/pen/xxWGjex),網頁改版後,按下紅色愛心按鈕,也不會意外刪掉電影了。</p> </div> </div> #### Exercise 3:注意監聽事件時,若忘記宣告參數,可能會不小心使用到已棄用的全域變數 [Window.event](https://developer.mozilla.org/en-US/docs/Web/API/Window/event) 觀察[這份 CodePen](https://codepen.io/rossignol/pen/VwXLxVa): - 觀察紅色按鈕的刪除電影功能是否正常運作 - 把第 53 行變成註解,把第 54 行的註解取消,再次觀察紅色按鈕的刪除電影功能是否正常運作 **Q:為什麼把參數 event 刪掉後,刪除的功能沒有壞掉,也沒有跳出「沒有宣告 event 變數」之類的錯誤訊息呢?** <small>將你的答案分享到留言區與同學交流</small> <a class="btn btn-secondary" role="button" data-toggle="collapse" href="#exercise3" aria-expanded="false" aria-controls="exercise3">參考答案</a> <div class="collapse" id="exercise3"> <div style="padding: 19px; margin-bottom: 20px; background-color: #f5f5f5; border: 1px solid #e3e3e3; border-radius: 4px;"> 因為剛好有個已經被棄用的全域變數,變數名稱也叫做 event:Window.event,而且變數裡儲存的東西,功能和本來的 event 參數相同。因此在這些機緣巧合之下,刪掉 event 參數之後,會變成使用全域變數 event,於是功能一切都正常。 <p>為了避免使用到已棄用的全域變數 event,要記得在 event handler 加上參數。也就是,要寫成第 53 行那樣,不要寫成第 54 行那樣</p> </div> </div>