學習到的知識點
HTML
下拉選單
span is inline-block
CSS
-webkit- 私有前綴
nth-of-type 與 nth-child
rotateX 旋轉技巧
perspactive 立體感
Javascript
ArrowFunction
e.target.classList.toggle
NodeList
addEventListener(“change”)
[… str] 展開運算子
Localstorage
setItem 跟 getItem
json.parse
簡介 DEMO
製作電影訂位的網頁,透過選取座位的方式,紀錄已幾選的位置與不同電影呈現不同的票價。
HTML
電影選單 製作出下拉式選單使用select
,在option
選項裡加上value
,之後可以做為讀取不同電影的不同價格。
1 2 3 4 5 6 7 8 9 <div class ="movie-container" > <label > Pick a movie:</label > <select id ="movie" > <option value ="10" > Avengers: Endgame ($10)</option > <option value ="12" > TENET ($12)</option > <option value ="8" > Toy Story 4 ($8)</option > <option value ="9" > The Lion King ($9)</option > </select > </div >
顯示座位狀態清單 座位顯示狀況說明(N/A meaning that the seat is open)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <ul class ="showcase" > <li > <div class ="seat" > </div > <small > N/A</small > </li > <li > <div class ="seat selected" > </div > <small > Selected</small > </li > <li > <div class ="seat occupied" > </div > <small > Occupied</small > </li > </ul >
座位區 利用 div 做出六排座位區
1 2 3 4 5 6 7 8 9 10 11 12 13 <div class ="container" > <div class ="screen" > </div > <div class ="row" > <div class ="seat occupied" > </div > <div class ="seat" > </div > <div class ="seat" > </div > <div class ="seat" > </div > <div class ="seat" > </div > <div class ="seat" > </div > <div class ="seat" > </div > <div class ="seat" > </div > </div > </div >
顯示已選取幾個座位與價格 span 是屬於 inline-block,所以可以在同一行對數字進行動態調整。
1 2 3 <p class ="text" > You have selected <span id ="count" > 0 </span > seats for a price of $<span id ="total" > 0</span > </p >
CSS Javascript
引入字體 1 @import url("https://fonts.googleapis.com/css2?family=Lato&display=swap" );
將整體元素置中 利用flex
屬性使元素置中畫面中央,先使元素column
(垂直)排列,再使用flex-direction: column
與align-items:center
,主軸副軸置中,視窗高度為100vh
。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 * { box-sizing : border-box; }body { background-color : #242333 ; color : #fff ; display : flex; flex-direction : column; align-items : center; justify-content : center; height : 100vh ; font-family : "Lato" , sans-serif; margin : 0 ; }.movie-container { margin : 20px 0 ; }
調整下拉式選單的選項 -webkit-appearance
,使用系統預設的外觀。
-webkit-appearance: none
,去除瀏覽器的預設樣式。
webkit 是一種瀏覽器的私有前綴,是瀏覽器對新 css 實現的一個提前支持。 -webkit 是指 webkit 內核的瀏覽器(Safari 和 Google),moz 是 Firefox 內核的瀏覽器。。
為什麼會出現 webkit 跟 moz 這種私有前綴?
因為制定 HTML 和 CSS 標準的組織 W3C 動作較慢,通常,W3C 組織成員提出新屬性,比如說border-radius
,但要審核一個屬性通過需要很多審查,瀏覽器商不想等這麼久,就會在瀏覽器中先加入支持。
但是,避免 W3C 公布標準時有變更,就會加入一個私有前綴,比如-webkit-border-radius,通過這種方式來提前支持屬性。
1 2 3 4 5 6 7 8 9 10 11 .movie-container select { background-color : #fff ; border : 0 ; border-radius : 5px ; font-size : 14px ; margin-left : 10px ; padding : 5px 15px 5px 15px ; -moz-appearance: none; -webkit-appearance: none; appearance: none; }
調整座位元素與位置 在每一組 seat 元素的父元素須加上.row 屬性,才能夠每一排都套用到 flex 屬性。
1 2 3 4 5 6 7 8 9 10 11 12 .seat { background-color : #444451 ; height : 12px ; width : 15px ; margin : 3px ; border-top-left-radius : 10px ; border-top-right-radius : 10px ; }.row { display : flex; }
不同狀況(Selected,Occupied),給予不同顏色。
1 2 3 4 5 6 7 8 .seat .selected { background-color : #6feaf6 ; }.seat .occupied { background-color : #fff ; }
將座位與其他座位空出距離
nth-of-type 與 nth-child() 有什麼不同?
當選取的元素中間有混雜其他元素,像是<hr>
,在 nth-child 會將其他元素一起做計算,但 nth-of-type 只會計算選取的元素,例如選擇<p>
,就只會選<p>
。
1 2 3 4 5 6 7 .seat :nth-of-type (2 ) { margin-right : 18px ; }.seat :nth-last-of-type (2 ) { margin-left : 18px ; }
滑鼠移動到座位的動畫 不是.occupied
的就放大 1.2 倍。
.showcase
裡的三個說明位置。scale 不需要變大,使用 default 的 1 倍就好。cursor 也是使用 default。
1 2 3 4 5 6 7 8 9 .seat :not (.occupied ):hover { cursor : pointer; transform : scale (1.2 ); }.showcase .seat :not (.occupied ):hover { cursor : default; transform : scale (1 ); }
showcase 的排列 使用 flex 屬性自動水平排列,使用justify-content:space-between
,空間都留在物件之間。
(space-evenly,平均分配空間。space-around,開頭留一份空間,物件留兩份。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 .showcase { background-color : rgba (0 , 0 , 0 , 0.1 ); padding : 5px 10px ; border-radius : 5px ; color : #777 ; list-style-type : none; display : flex; justify-content : space-between; }.showcase li { display : flex; align-items : center; justify-content : center; margin : 0 10px ; }.showcase li small { margin-left : 2px ; }
電影螢幕 rotateX MDN
選轉技巧
1 2 3 4 5 6 7 8 .screen { background-color : #fff ; height : 70px ; width : 100% ; margin : 15px 0 ; transform : rotateX (-45deg ); box-shadow : 0 3px 10px rgba (255 , 255 , 255 , 0.7 ); }
perspactive MDN
perspactive 立體感,數值越高越立體。
1 2 3 4 .container { perspective : 1000px ; margin-bottom : 30px ; }
計算點選幾個座位與價格 1 2 3 4 5 6 7 8 p .text { margin : 5px 0 ; }p .text span { color : #6feaf6 ; }
Javascript 讀取 Container (Screen & Seats)的元素
宣告變數,如果是之後會用到的值,不能使用 const。
ticketPrice 不能用const
宣告,因為 value 會改變,改用let
。
parseInt
為轉換型別,字串到數字,也可以使用+
。
使用 ES6 語法 ArrowFunctoin 改寫
1 2 3 container.addEventListener ("click" , function (e ) {}) container.addEventListener ("click" , (e ) => {});
container.addEventListener
,點擊後,可以讀取到.Container
裡面的元素。
1 2 3 4 5 6 7 8 9 10 11 12 const container = document .querySelector (".container" );const seats = document .querySelectorAll (".row .seat:not(.occupied)" );const count = document .getElementById ("count" );const total = document .getElementById ("total" );const movieSelect = document .getElementById ("movie" );let ticketPrice = parseInt (movieSelect.value ); container.addEventListener ("click" , (e ) => { console .log (e.target ); });
當空位被點擊時,會轉換成被選取的顏色
當選取到空位且不是.occupied
時,加上.selected
的 class 樣式。
e.target.classList.toggle
,可以打開或是關閉(像是電燈開關),處理.selected
是否加上去。
classList.其他方法 ,包括 add,remove。
1 2 3 4 5 6 7 8 9 10 const ticketPrice = parseInt (movieSelect.value ); container.addEventListener ("click" , (e ) => { console .log (e.target ); if ( e.target .classList .contains ("seat" ) && !e.target .classList .contains ("occupied" ) ) { e.target .classList .toggle ("selected" ); } });
加入 Function 之後,動態調整座位數與價錢。
NodeList 是什麼?
NodeList 會經由 querySelectorAll(),childNodes 產生,是陣列的一種,無法使用陣列型別的 method,但可以索引存取內容。
1 2 3 4 5 6 const selectedSeats = document .querySelectorAll (".row .seat.selected" );const selectedSeatsCount = selectedSeats.length ; count.innerText = selectedSeatsCount;
完整 Function
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 function updateSelectedCount ( ) { const selectedSeats = document .querySelectorAll (".row .seat.selected" ); const selectedSeatsCount = selectedSeats.length ; count.innerText = selectedSeatsCount; total.innerText = selectedSeatsCount * ticketPrice; } container.addEventListener ("click" , (e ) => { if ( e.target .classList .contains ("seat" ) && !e.target .classList .contains ("occupied" ) ) { e.target .classList .toggle ("selected" ); updateSelectedCount (); } });
更換電影時,可以更換價格 重新認識 JavaScript: Day 16 那些你知道與不知道的事件們
change 是屬於表單事件中的 change 事件,點擊項目後才觸發。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function updateSelectedCount ( ) { const selectedSeats = document .querySelectorAll (".row .seat.selected" ); const selectedSeatsCount = selectedSeats.length ; count.innerText = selectedSeatsCount; total.innerText = selectedSeatsCount * ticketPrice; } movieSelect.addEventListener ("change" , (e ) => { ticketPrice = parseInt (e.target .value ); updateSelectedCount (); });
儲存被點選的座位位置
[…] 展開運算子。
在 ES6 中,新增了一個 “…” 的關鍵字,這個關鍵字在不同時間點有不同的效果,有些時候它會被當作展開運算子(spread operator 使用,有時候則是被當作其餘運算子(rest operator)使用。
展開運算子,是把許多的參數轉換成一個陣列,而展開運算子則是可以把陣列中的元素取出。
1 2 3 4 5 6 7 8 9 10 11 12 13 const arr1 = [1 ,2 ,3 ]const arr2 = [...arr1,4 ,5 ]console .log (arr2)const arr1 = [1 ,2 ,3 ]const arr2 = [...arr1,4 ,5 ]const arr3 = arr2.map (function (item ){ return item*2 ; })console .log (arr3)
Spread syntax MDN
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function updateSelectedCount ( ) { const selectedSeats = document .querySelectorAll (".row .seat.selected" ); const seatsIndex = [...selectedSeats].map ((seat ) => { return [...seats].indexOf (seat); }); console .log (seatsIndex); const selectedSeatsCount = selectedSeats.length ; count.innerText = selectedSeatsCount;
加入 LocalStorage 可以在 Application 裡面查看儲存的座位陣列。
Js 的setItem
語法,可以將資料寫進瀏覽器裡。setItem
的第一個值是 key 的屬性名,第二個值就是相對應的 value。
1 2 localStorage .setItem ("selectedSeats" , JSON .stringify (seatsIndex));
可以在 Application 裡面查看,儲存的 selectedMovieIndex,selectedMoviePrice 陣列。
1 2 3 4 5 function setMovieData (movieIndex, moviePrice ) { localStorage .setItem ("selectedMovieIndex" , movieIndex); localStorage .setItem ("selectedMoviePrice" , moviePrice); }
從 Localhost 取資料,使得點選過後的位置可以填充 UI
回傳-1 為,陣列內沒有這個元素時會回傳索引值。
1 2 3 const arr2 = [1 ,2 ,3 ,4 ,5 ]console .log (arr2.indexOf (100 ))
Json.parse
將字串轉換成陣列或物件的用法。
把資料存進瀏覽器後,要取出來的話要用getItem
語法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 function populateUI ( ) { const selectedSeats = JSON .parse (localStorage .getItem ("selectedSeats" )); if (selectedSeats ! null && selectedSeats.length > 0 ) { seats.forEach ((seat, index ) => { if (selectedSeats.indexOf (index) > -1 ) { seat.classList .add ("selected" ); } }); } const selectedMovieIndex = localStorage .getItem ("selectedMovieIndex" ); if (selectedMovieIndex ! null ) { movieSelect.selectedIndex = selectedMovieIndex; } }
最後在整個 JS 最下方呼叫一次 updateSelectedCount() 函式,這樣重整網頁時,不需經過事件觸發,直接更新下方文字敘述。
參考文章 CSS webkit-appearance:none
appearance
CSS3 選擇器「:nth-child()」與「:nth-of-type()」用法大不同
JS NodeList 與 Array 差異
重新認識 JavaScript: Day 16 那些你知道與不知道的事件們
【ES6 小筆記】Array.map() - 處理陣列最佳選擇
Spread syntax MDN
JSON.parse 函式
JavaScript ES6 中的展開運算子(spread operator)和其餘運算子(rest operator)