之前一直沒有寫相關的作業心得筆記,一方面是覺得很難度不高,一方面覺得操作幾次就很熟練了,所以就很懶惰...但進入到學期2-2後,光是S2的製作電影清單,突然感覺難度一下提升超級多,很多內容不是一次兩次就能夠記起來且熟悉,而且函式的拆解跟邏輯複雜度上也提升相當多,演算法也來到了百行之多,這時才發現真的很難一下吸收,所以決定開始來寫學習、作業、技術相關筆記來加深自己的記憶及熟練度。
任務
這次的任務是要把S2製作的My Movie List做進化,且要滿足以下幾個功能
- 要新增一組功能按鈕,可以將畫面呈現為列表模式及卡片模式。
- 畫面能夠順利在 list 和 card 模式間來回切換。
- 不能影響到原有功能(尤其是 More、收藏按鈕、分頁功能要正常運作)
- 由於程式碼篇幅頗長,請運用 description 欄位說明新增的功能,幫助助教能有效注意到重點。在程式碼中也請運用註解來提示助教!
驗收重點
驗收重點現階段期待你做到⋯⋯開發框架/函式庫(略)程式邏輯與 Coding Style以你目前的程度,我們期待你在「成功做出功能」以後,認真地花點時間優化程式碼品質,強化邏輯清晰度、提高可讀性、減少重複,畢竟我們的程式碼體量愈來愈大了,以下我們列出可能的方向,請你這次務必要「一邊實作一邊刻意優化」:
- 程式邏輯是否清晰
- 每個函式都有明確的職責,一個函式專心做一件事,例如例如 card 模式渲染、 list 模式渲染、判定模式等
- 避免不必要的變數
- 減少暴露在 global 區域的程式碼,也就是說,若你在這份作業中新增了功能,這些功能的程式碼必須被封裝在函式裡。
- 宣告變數時需注意
- 使用 let & const 宣告變數 (不要使用 var)
- 不會變動的內容用 const,要變動的內容才用 let
- 清晰易懂的變數命名
- 變數用名詞、函式用動詞、陣列使用複數型
- 名稱與內涵盡力一致 (不要嫌單字太多、講清楚最重要)
- 新增顯示模式的切換功能後,原有的搜尋功能仍能正常運作 (如:按下 search 能啟動搜尋、執行新的搜尋時,有先清空頁面再呈現搜尋結果)
- 變數資料型態保持前後一致
- 命名是否有意義、可讀性
- 適當運用註解幫助他人快速理解程式碼
- 盡量避免多餘的程式碼 - 尤其是測試過程中的 console.log
- 遵守 JavaScript Standard style (建議安裝自動檢查套件)
視覺與使用者動線
- 切換顯示模式時,內容能停留在原本的頁面,不會跳回第一頁
- 若使用搜尋後,再切換顯示模式,仍然能正常顯示搜尋結果
- 視覺上能讓使用者知道現在選的是第幾頁、哪個顯示模式、哪些項目有加入收藏
行有餘力:優化品質、擴充規格
除了上面的 coding style 驗收重點以外,也可以嘗試思考「擴充性」,例如:
- 想一想:你是怎麼切換清單/卡片模式的,有沒有能優化的空間?
- 進一步想想看,如果未來還要加入第三種、第四種顯示情境,你目前的邏輯設計有擴充空間嗎?會不會到時候要打掉重練?
另外,也可以思考一下目前的邏輯如何查找 DOM 元素內容,有沒有更有效率的方式?
- 是否能穩定的切換頁籤而不會影響畫面(通常會和資料流有沒有處理好有關)
- 嘗試對 button 做一些 hover 處理以提升用戶體驗
Step 1:
第一步老樣子,先從HTML下手吧!先將Font Awesome按鈕的樣式置入頁面中,並且調整至適當的位置。
說明:
要先將Font Awesome的JS先置入head裏頭。之後在Font Awesome裡尋找相關icon的class碼,安排它在Html的位置,也要適時的調整Row或col(Boostrap開發模式),最後別忘了給他們的父層,以及各自都加上屬於自己的id。
Step 2:
再來製作出清單模式在畫面上應該顯示的樣式。
只要先寫出一列的樣板模式即可,因為之後要用JS把設定好的HTML格式資料至入到畫面上。
Step 3:
改造function。
首先我們可以複製renderMovieList這個函式,並將它命名為renderMoviesList2之後方便做測試,基本上函式內的邏輯都一樣,只要將新寫的HTML格式資料帶到到rawHTML即可。所以去HTML把你剛剛寫好的格式放進rawHTML吧,記得要把電影名稱或相關要顯示的資料,替換成正確的參數。 之後可以將最下方renderMovieList改成renderMovieList2,來看看是不是正確顯示在螢幕上。
Step 4:
新增監聽器。
在Step 1置入icon的時候,就將各自及父層都設定好了id。
所以先回到上頭設定常數,將各自的位置選擇出來。
然後在父層掛上監聽器。
這邊我設定的父層為btnShowStyle
- 因為在按鈕上有設置a標籤,所以要先取消超連結本上的預設動作。
- 加入判斷式,確定是選擇到哪一個按鈕。
- 接著變更圖案本身的顏色樣式,因為是使用boostrap系統,所以要將各自的classList做新增或刪除。(使用狀態下為藍色primary,非使用狀態下為灰色secondary)
- 最後面的renderMovieList記得改成renderMovieList2
最後來檢查按鈕是否有成功,此時按下按鈕,按鈕要會變色,且會變換顯示的模式
Step 5:
優化顯示模式的語法。
由於現在將兩個不同的顯示方式分成兩個函式來寫,會增加後面管理程式碼的困擾,因為只要碰到任何跟渲染畫面"renderMovieList"的時候,都要去判斷要用renderMovieList或是renderMovieList2,包含查找功能,換頁功能都有用到renderMovieList這個函式,為了不去更改原有的程式碼,所以決定把這兩個函式合併。
先將renderMovieList2內的程式碼先搬到renderMovieList後面,
接著就是加入判斷式, 這邊思考後,我的想法是先在最上頭新增一個變數 showStyle作為信號開關,
接著在renderMovieList加上判斷式,若showStyle等於0時,使用cards的顯示方式,否則就顯示清單模式。
接著回到顯示模式監聽器,加入當選則卡片時showStyle重新賦值為0,選擇清單時,showStyle重新賦值為1。
最後檢查一遍把確定所有的renderMovieList不要有出現之前寫的renderMovieList2。
現在頁面已經可以順利切換了,而且也優化了程式碼,未來若要新增第三種第四種顯示方式,只要在renderMovieList內新增新的判斷及格式,還有在樣式顯示監聽器內加入新的判斷就可以了。
目前原有的功能也要都能夠正常運作,只要選定一種顯示方式,不管用搜尋、換頁都會保持原有的顯示方式。
但是發現一個小問題,當我在卡片狀態下然後在第三頁時,當我按下變換樣式顯示頁面內容會回到第一頁,應該要顯示當頁才對。
這時候回頭檢視所有的renderMovieList帶入的引數發現,全部都帶入了getMoviesByPage(1),所以不管搜尋、換頁,只要碰到getMoviesByPage(1)帶入的是1時,全都會回到第一頁。
Step 6:
修正所有的getMoviesByPage(1),除了搜尋監聽器內的getMoviesByPage(1)。
回到最上方設定一個新的變數page 且賦予值 1。
再來將所有的getMoviesByPage的引數帶入page
getMoviesByPage(page)
這邊要注意,除了搜尋監聽器內的getMoviesByPage(1),不能修改。 然後在回到之前頁碼生成的函式內,將原本的定義page為變數的let拿掉。
現在只要切換頁面就會給page重新賦值,page被確定下來,就算換樣式,也還是會保持在原本該顯示的頁數了。 除了搜尋監聽器內的getMoviesByPage(1)不能改原因
因為現在page在globle的狀態,假設一開始先切換到第三頁,page值此時會被賦予3,搜尋監聽器內的getMoviesByPage()引數如果是page的情況下''renderMovieList(getMoviesByPage(page))'',這時候搜尋任何關鍵字,搜尋結果是少於三頁的情況下,會發現此刻畫面會變一片空白沒有資料,那是因為我們變成渲染頁數變成第三頁,但是結果少於三頁,所以當然就沒有任何電影顯示出來。
所以再次強調
除了搜尋監聽器內的getMoviesByPage(1),不能修改。
除了搜尋監聽器內的getMoviesByPage(1),不能修改。
除了搜尋監聽器內的getMoviesByPage(1),不能修改。
已完成任務 (已完成的畫刪除線) - 要新增一組功能按鈕,可以將畫面呈現為列表模式及卡片模式。
- 畫面能夠順利在 list 和 card 模式間來回切換。
- 不能影響到原有功能(尤其是 More、收藏按鈕、分頁功能要正常運作)
- 由於程式碼篇幅頗長,請運用 description 欄位說明新增的功能,幫助助教能有效注意到重點。在程式碼中也請運用註解來提示助教!
已完成的視覺與使用者動線驗收重點 (已完成的畫刪除線) - 切換顯示模式時,內容能停留在原本的頁面,不會跳回第一頁
- 若使用搜尋後,再切換顯示模式,仍然能正常顯示搜尋結果
- 視覺上能讓使用者知道現在選的是第幾頁、哪個顯示模式、哪些項目有加入收藏
看來只剩下最後兩項了,加把勁!
Step 7:
讓使用者知道現在選的是第幾頁。
隨著改變頁數要改變頁碼的顯示樣式,這代表每當我選了一次頁碼,我勢必要重新渲染一次頁碼,所以表示一定會是從這兩個部分下手
- renderPaginator(在正確的頁數上改變樣式)
- Paginator的監聽器(換頁數就重新render)
我們先到Paginator監聽器來,這邊比較容易修改,我們只要在最後加上
新增data運算子,因為每次點選換頁按鈕都得重新渲染頁面按鈕,更新顯示正在哪一頁上,所以要看是依filteredMovies來重新渲染頁碼或是movies來重新渲染頁碼,如果搜尋情況下filteredMovies會有資料,將會依搜尋結果的情況下去產生頁碼,若否則會依照原本movies的資料長度去產生頁碼。
再來我們接著修改函式renderPaginator(在正確的頁數上改變樣式)
函式當中,一旦我們置入所有頁碼的HTML格式資料後,我們就可以立刻選取到各li子元素,而因為globle的page會被更新,此時便可以依照global的page去指出是哪一個li子元素裡的a元素標籤應該要被修改樣式。
paginator.children[0]為第一頁的li標籤
paginator.children[0].firstElementChild為li裡面的a標籤
paginator.children[0].firstElementChild.classList.add('bg-primary', 'text-white')
a標籤新增名稱至class內(boostrap改變樣式)
最後再回到搜尋監聽器上,當搜尋為空白的時候,我們要顯示全部的電影,但別忘了要記得清空filteredMovies,因為filteredMovies現在已經移出到globle當中,也有很多函式都有使用到他,連續操作搜尋下,萬一filteredMovies保留之前的搜尋結果,將會出現顯示錯誤電影資料。
最後再補上一個執行產生頁碼函式。
這樣讓使用者知道現在選的是第幾頁就大功告成啦!
Step 8:
顯示哪些項目有加入收藏。
這大概是所有題目內,我覺得最困擾的一個題目了,邏輯性比較複雜。
我思考的邏輯是,先製作一個新的function: 一開始先設定幾個常數:
- 選出所有class為card-title的元素(應該有12個,因為每頁顯示12筆),並放入panelMovies,之後就可以使用panelMovies[i].innerText來取得畫面上的電影名稱。
- 將我最愛的電影從locoalStorage取出並從JSON轉成Object,並放入favoriteMoviesList
- 將favoriteMoviesList使用map製造出一個新的陣列,條件是每一筆電影資料的電影名稱,並放入favoriteMoviesTitle
- 重複畫面上總共有幾部電影,就重複檢查多少次,for迴圈
- 加入判斷式,若favoriteMoviesTitle裡面含有畫面上第i部的電影名稱,修改"新增至我的最愛按鈕"的顯示樣式。
補充說明:
- 第140行,當選擇到元素標籤時,可以使用setAttribute(’string’,’value’)來設定新的標籤屬性,兩個引述都是字串,屬性無值時為空字串。
- 對陣列使用map(),會將陣列中每一個項目,依照條件,產生一組新的陣列
MDN範例說明
大功告成,完成了所有作業要求了!
最後再檢查一遍作業的批改標準吧!
詳細的檢查一遍,操作看看,有沒有發現其他的小錯誤,若沒有大致上應該沒有問題啦!