---
# System prepended metadata

title: JS練習_電影院訂位頁面
tags: [jQuery, javascript]

---

# JS練習_電影院訂位頁面 
製作一個電影訂位的網頁，可以透過選取的座位數以及不同的電影，呈現不同的票價。

最終畫面呈現：
![](https://i.imgur.com/pK4oigy.png)

[成果網頁](https://chrislinlin.github.io/my-projects/mentor_mission/mission_22/index.html?)


## 成品功能
- 可以選擇不同電影
- 可以選擇座位
- 加總選取座位配合搭配電影的票價獲得消費總額
- 頁面重新整理時，資料保有在原始頁面

## HTML
- 用select and option做出下拉式選單，並在option後帶入代表票價的value
```htmlembedded=
    <div class="movie-container">
      <label>Pick a movie:</label>
      <select id="movie">
        <option value="10">Avengers: Endgame ($10)</option>
        <option value="12">Joker ($12)</option>
        <option value="8">Toy Story 4 ($8)</option>
        <option value="9">The Lion King ($9)</option>
      </select>
    </div>
```
- 用ul 及 li做出座位狀態表，再利用容器div給予屬性裡面包覆很多小容器代表座位。
```htmlembedded=
    <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 class="container">
      <div class="screen"></div>
      <div class="row">
        <div class="seat"></div>
        <div class="seat"></div>
        <div class="seat"></div>
        <div class="seat occupied"></div>
        <div class="seat occupied"></div>
        <div class="seat"></div>
        <div class="seat"></div>
        <div class="seat"></div>
      </div>
```
- 利用標籤p 做出選取的座位數及票價的文字呈現，並利用span標籤讓帶入JS時可以更方便選取。
```htmlembedded=
    <p class="text">
      You have selected <span id="count">0</span> seats for a price of $<span
        id="total"
        >0</span
      >
    </p>
```

## CSS
網頁整體以flex為主，只要設定致中，版面設置滿版

- `appearance`
  - 由於不同瀏覽器的支援不同，針對不同瀏覽器的預設樣式修正。
  - `macOS`使用的`safari`需要`-webkit-appearance: none;`來防止預設的瀏覽器樣式。
```css=
.movie-container select {
  -moz-appearance: none;
  -webkit-appearance: none;
  appearance: none;
}
```
先設定好選擇好的座位及特定的座位的css，就算一開始html沒有在標籤內填入這些屬性也可之後ＪＳ帶入時，直接更改classname就可套用設定好的css。
```css=
.seat.selected {
  background-color: #6feaf6;                /*已選擇座位顏色*/
}

.seat.occupied {
  background-color: #fff;                   
}

.seat:nth-of-type(2) {                      /*利用特定位置選擇器做出走道*/
  margin-right: 18px;
}

.seat:nth-last-of-type(2) {                 /*利用特定位置選擇器做出走道*/
  margin-left: 18px;
}

.seat:not(.occupied):hover {                /*選擇器 => 未被選擇座位碰觸時放大*/
  cursor: pointer;
  transform: scale(1.2);
}

.showcase .seat:not(.occupied):hover {      /*選擇器 => 上方示意區域取消效果*/
  cursor: default;
  transform: scale(1);
}
```
電影螢幕透過css 3D透視效果及搭配本身螢幕容器設定`transform:rotate`屬性及`box-shadow`屬性做出類似電影院的效果

```css=
.container {
  perspective: 1000px;                /*3D效果*/
  margin-bottom: 30px;
}

.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);
}
```

## JS
1. 宣告變數，要是變數之後還會改變，就會利用`let`
```.js
let ticketPrice = +$('#movie').val();//jquery沒有'.value" 只有val()
let seats = $('.row .seat:not(.occupied)');
```

2. 用click事件呼叫callback function，且利用`toggle`屬性，將參數未有的標籤加上，有的標籤去除。
- 用click事件方式呼叫callback function
- 用.target得到元素並用`toggle()` 判斷在函式中判斷參數是否含有某class。
[target小科普]()
- 用if條件判斷符合，就可以在使用toggle()加入selected屬性使他變色
- 最後呼叫一次**updateSelectedCount()** 函式，讓我們每產生點擊事件一次，即可更新下方文字敘述。



```.js=
 $(document).ready(function(){
     $('.container').click(function(e){
         if(e.target.classList.contains('seat') && !e.target.classList.contains('occupied')){
             e.target.classList.toggle('selected'); //因為用toggle 會把沒有的變成有，若有的就會變沒有

             updateSelectedCount();
         }
     }); 
 });
 ```

3. 使用者可以自由選擇座位，但已選的不能選

- 編寫步驟如下：
  - 把選取的座位放到arr
  - 用map()方法更新arr
  - 在return到新的arr

- 利用`...`運算來將我們選取的位置傳入`[]`成為陣列`array`，
- 利用`map()`這個方法處理陣列中的元素，返回我們選取的位置在陣列中的索引值(在陣列中哪個位置，從零開始，利用`indexOf()`方法)。

- 將取得的索引值存入`localStorage`，給定兩個參數（keyName, keyValue)，
第一個參數作用是給定名稱，稱為`keyName`(鍵名) => 下面這個例子叫做`selectedSeats`
第二個參數作用是給定值，稱為`keyValue`(鍵值)也就是這個`keyName`對應的值。
但`keyName`和`keyValue`都必須是字串型別，所以用`JSON.stringify()`將陣列轉成字串型別。
[LocalStorage 筆記傳送門](https://hackmd.io/@chrislinlin/js_localstorage)

```.js=

 function updateSelectedCount(){
    $(document).ready(function(){
        const selectSeats = $('.row .seat.selected');

        const selectedSeats = $('.row .seat.selected');
        const seatsIndex = [...selectedSeats].map(function(seat){
            return [...seats].indexOf(seat);
        })

        localStorage.setItem('selectedSeats', JSON.stringify(seatsIndex));

        const selectedSeatsCount = selectSeats.length;

        $('#count').text(selectedSeatsCount);
        $('#total').text(selectedSeatsCount * ticketPrice) ;
    });
```
4. 使用者可以選擇電影，帶入不同的價格


```.js=

$('#movie').change(function(e){
    ticketPrice = $(e.target).val();
    setMovieData(e.target.selectedIndex, $(e.target).val()); //這個selectedIndex在哪
    updateSelectedCount();
})
```

5. 每選一個位置，位置圖下有數量跟座位數的正比關係
```.js=

 function updateSelectedCount(){
    $(document).ready(function(){
        const selectSeats = $('.row .seat.selected');

        const selectedSeats = $('.row .seat.selected');
        const seatsIndex = [...selectedSeats].map(function(seat){
            return [...seats].indexOf(seat);
        })

        localStorage.setItem('selectedSeats', JSON.stringify(seatsIndex));

        const selectedSeatsCount = selectSeats.length;

        $('#count').text(selectedSeatsCount);
        $('#total').text(selectedSeatsCount * ticketPrice) ;
    });
}
```


6. 使用local Storage儲存已選的座位，並將其取出後附加屬性

為了使頁面重整後依然能夠保有選取的位置，在每次選取時將內容儲存至瀏覽器的儲存空間，需要時再取出套用其他屬性。
-  使用JSON.parse把JSON資料轉換array回來，使用getItem到我們要使用的key(`selectedSeats`)取得value並且指派給變數selectedSeats
-  使用兩個判斷式
   - 第一個判斷式：條件為`selectedSeats`不為空值，且`localStorage`至少需要一個值。
   - 通過第一個判斷式會透過`each()`方法，檢查`selectedSeats`索引值是否存在其中。
   - 第二個判斷式：要是透過`indexOf()`確認索引值存在其中，indexOf會大於-1
   - 通過第二個判斷式就會加上屬性`selected`


```.js=
    const selectedSeats = JSON.parse(window.localStorage.getItem('selectedSeats'));

    //將localStorage的資料加入 selected屬性
    if(selectedSeats !== null && selectedSeats.length >0){
        seats.each(function(index, seat){
            //indexOf> -1代表該資料在arr 中
            if(selectedSeats.indexOf(index)>-1){
                seat.classList.add('selected');
            }
        });
    }// 在jquery裡 foreach的方式是 .each(function(index,element))和JS相反，js是foreach(function(element, index))

    const selectedMovieIndex =window.localStorage.getItem('selectedMovieIndex');
    if(selectedMovieIndex !== null){
        $('#movie').selectedIndex = selectedMovieIndex;
    };
}
```

###### tags: `javascript` `jQuery`
