[Udemy]20 Web Projects With Vanilla JavaScript -Project#2

HTML&CSS&Javascript · 2020. 12. 8. 15:54

유데미에서 Brad Traversy의 20 Web Projects With Vanilla JavaScript

를 하나씩 직접 코딩해보면서 정리하기 위한 글이다.

 

 

데모 페이지

Movie Seat Booking (vanillawebprojects.com)

코드 링크

github.com/rshak8912/20-Web-Projects

 

 

 


index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="style.css">
    <title>Movie Seat Booking</title>
</head>
<body>
<div class="movie-container">
    <label>Pick a movie:</label>
    <select id="movie">
        <option value="10">어벤져스: 엔드게임($10)</option>
        <option value="12">조커($12)</option>
        <option value="8">토이스토리4($8)</option>
        <option value="9">라이언킹($9)</option>
    </select>
</div>
<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"></div>
        <div class="seat"></div>
        <div class="seat"></div>
        <div class="seat"></div>
        <div class="seat"></div>
    </div>
    <div class="row">
        <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 class="seat"></div>
    </div>
    <div class="row">
        <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 occupied"></div>
        <div class="seat occupied"></div>
    </div>
    <div class="row">
        <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 class="seat"></div>
    </div>
    <div class="row">
        <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 class="seat"></div>
    </div>
    <div class="row">
        <div class="seat"></div>
        <div class="seat"></div>
        <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 occupied"></div>
    </div>
</div>
<p class="text">You have selected <span id="count">0</span> seats for a price of
    $<span id="total">0</span></p>
<script src="script.js"></script>
</body>
</html>

 

 

style.css

@import url('https://fonts.googleapis.com/css?family=Lato&display=swap');

* {
    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;

}

.movie-container select {
    background-color: #ffffff;
    border: 0;
    border-radius: 5px;
    font-size: 14px;
    margin-left: 10px;
    padding: 5px 15px 5px 15px;
    -moz-appearance: none;
    -webkit-appearance: none;
    appearance: none;
}

.container {
    perspective: 700px;
    margin-bottom: 30px;
}
.seat {
    background-color: #444451;
    height: 12px;
    width: 15px;
    margin: 3px;
    border-top-left-radius: 10px;
    border-top-right-radius: 10px;
}

.seat.selected {
    background-color: #6feaf6;

}

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

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

}

.showcase {
    background: rgba(0, 0, 0, 0.1);
    padding: 5px 10px;
    border-radius: 5px;
    color: #777777;
    list-style-type: none;
    display: flex;
    justify-content: space-between;
}

.showcase li {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 10px;
}

.row {
    display: flex;
}

.screen {
    background-color: #ffffff;
    height: 70px;
    width: 100%;
    margin: 15px 0;
    transform: rotateX(-45deg);
    box-shadow: 0 3px 10px rgba(255, 255, 255, 0.7);

}

p.text {
    margin: 5px 0;
}

p.text span {
    color:#6feaf6;
}

 

 

script.js

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');

populateUI();

let ticketPrice = +movieSelect.value; // +붙이면 타입:number
// const ticketPrice = movieSelect.value 타입:string

function setMovieData(movieIndex, moviePrice) {
    localStorage.setItem('selectedMovieIndex', movieIndex);
    localStorage.setItem('selectedMoviePrice', moviePrice);
}

function updateSelectedCount() {
    const selectedSeats = document.querySelectorAll('.row .seat.selected');

    const seatsIndex = [...selectedSeats].map(seat => [...seats].indexOf(seat));

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

    const selectedSeatsCount = selectedSeats.length;

    count.innerText = selectedSeatsCount;
    total.innerText = selectedSeatsCount * ticketPrice;

    setMovieData(movieSelect.selectedIndex, movieSelect.value);
}
// get data from localstorage and popular UI
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;
    }
}

// Movie select event
movieSelect.addEventListener('change', e => {
    ticketPrice = +e.target.value;
    updateSelectedCount();
})

// Seat click event
container.addEventListener('click', e => {
    if (e.target.classList.contains('seat') &&
    !e.target.classList.contains('occupied')) {
        e.target.classList.toggle('selected');

        updateSelectedCount();
    }
});

// Initial count and total set
updateSelectedCount();

 

강의를 통해 배운점(생각 정리)

  • box-sizing: border-box 속성 : box-sizing은 박스의 크기를 화면에 표시하는 방식을 변경하는 속성으로  테두리가 있는 경우에는 테두리의 두께로 인해서 원하는 크기를 찾기가 어렵지만 box-sizing 속성을 border-box로 지정하면  테두리를 포함한 크기를 지정할 수 있기 때문에 예측하기가 더 쉽다.

 

  • align-items 속성: flex element의 수직 방향 정렬 방식을 설정한다. 이 속성은 한 줄만을 가지는 flex 박스에서는 효과가 없으며, 두 줄 이상을 가지는 flex 박스에서만 효과가 있다. 

 

  • justify-content 속성: flex element의 수평 방향 정렬 방식을 설정한다.

 

  • appearance 속성: 운영체제 및 브라우저에 기본적으로 설정되어 있는 테마를 기반으로 요소를 표현한다. 네이티브로 지원되는 모양들을 해제하거나 추가할때 이 속성을 이용할 수 있다. 그래서 appearance 속성을 사용한 위의 css 코드에서 
    -moz-appearance: none; /* Firefox */ 
    -webkit-appearance: none; /* Safari and Chrome */ 을 지원하기 위한 것이라고 볼 수 있겠다.

 

  • css의 not 선택자 사용법을 익힐 수 있었다. Element:not(selector)
    example) .seat:not(.occupied)

 

  • parse 메소드는 string 객체를 json 객체로 변환. (string -> json)

 

  • stringify 메소드는 json 객체를 String 객체로 변환. (json -> string)