ItIron2022
Javascript
要準備面試前總是需要複習各種基本概念,就像以前高中考試前背單字一樣,既然都是一樣的行為,那麼以前常用的單字卡應該也能用在程式學習上吧? 這幾天我們就來一起打造一個記憶卡app,雖說會有些複雜,但許多應用到的技巧都在之前的練習中提過,不用太過於擔心!
我們這次要做的是一個模擬flash card的頁面,可以手動新增想練習的字卡並有著基本的翻頁動畫,如同下圖所示。
Step1: 專案結構
同樣請你先準備好三個基本的檔案,index.html, style.css & script.js檔案,並先在index.html & style.css中寫入以下的內容,量有點多但不用擔心,我會額外做需要的說明。
另外有些人可能會發現html/css的有些寫法相當奇怪,you got me. 其實這個專案是我剛開始學程式不久後寫的東西,現在回頭看確實很粗糙,但為了快速示範我就直接拿以前寫的東西做基底,沒有再額外做一些優化,暫時先把重點放js的處理就好了,css/html的部分就挑需要的看,暫時放我一馬吧。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Flash Cards</title>
<link rel="stylesheet" href="./style.css" />
<script
src="https://kit.fontawesome.com/fb6ef03ea0.js"
crossorigin="anonymous"
defer
></script>
<!-- <script src="./script.js" defer></script> -->
</head>
<body>
<!-- clear btn -->
<button id="clear" class="clear btn">
<i class="fas fa-trash"></i> Clear Cards
</button>
<!-- title -->
<h1>
Memory Cards
<button id="show" class="btn btn-small">
<i class="fas fa-plus"></i> Add new cards
</button>
</h1>
<!-- card-container -->
<div id="cards-container" class="cards">
<div class="card active">
<div class="inner-card">
<div class="inner-card-front">
<p>What is Javascript?</p>
</div>
<div class="inner-card-back">
<p>A programming language</p>
</div>
</div>
</div>
</div>
<!-- navigations -->
<div class="navigation">
<button id="prev" class="nav-button">
<i class="fas fa-arrow-left"></i>
</button>
<p id="current">1/1</p>
<button id="next" class="nav-button">
<i class="fas fa-arrow-right"></i>
</button>
</div>
<!-- add card -->
<div class="add-container" id="add-container">
<h1>
Add New Card
<button class="btn btn-small btn-ghost" id="hide">
<i class="fas fa-times"></i>
</button>
</h1>
<div class="form-group">
<label for="question">Question</label>
<textarea
name="question"
id="question"
placeholder="Enter question"
></textarea>
</div>
<div class="form-group">
<label for="answer">Answer</label>
<textarea
name="answer"
id="answer"
placeholder="Enter answer"
></textarea>
</div>
<button id="add-card" class="btn">
<i class="fas fa-plus"></i> Add Card
</button>
</div>
</body>
</html>
@import url("https://fonts.googleapis.com/css?family=Lato&display=swap");
* {
box-sizing: border-box;
}
body {
padding: 0;
background: #3e92cc;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
overflow: hidden;
font-family: "Lato", sans-serif;
}
h1 {
position: relative;
}
h1 button {
position: absolute;
right: 0;
transform: translate(120%, -50%);
z-index: 2;
}
.btn {
border-radius: 3px;
font-size: 14px;
margin-top: 20px;
padding: 10px 15px;
text-transform: uppercase;
background: #27ae60;
color: #fff;
border: none;
font-weight: bold;
cursor: pointer;
}
.btn:hover {
letter-spacing: 1px;
transition: all 0.4s ease 0s;
background: #434343;
}
.btn-small {
font-size: 12px;
padding: 5px 10px;
}
.btn-ghost {
border: 0;
background: transparent;
}
.clear {
position: absolute;
bottom: 30px;
left: 30px;
color: #fff;
background-color: #ff6666;
}
.cards {
perspective: 1000px;
position: relative;
height: 300px;
width: 500px;
max-width: 100%;
overflow: hidden;
}
.cards .card {
position: absolute;
opacity: 0;
font-size: 1.5em;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: translateX(50%) rotateY(-10deg);
transition: transform 0.4s ease, opacity 0.4s ease;
}
.cards .card .inner-card {
box-shadow: 0 1px 10px rgba(0, 0, 0, 0.3);
border-radius: 4px;
height: 100%;
width: 100%;
position: relative;
transform-style: preserve-3d;
transition: transform 0.4s ease;
}
.cards .card .inner-card .inner-card-front,
.cards .card .inner-card .inner-card-back {
backface-visibility: hidden;
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
background: #fff;
border-radius: 16px;
word-break: break-word;
padding: 20px;
}
.card.active {
opacity: 1;
cursor: pointer;
z-index: 10;
transform: translateX(0%) rotateY(0deg);
}
.card.left {
transform: translateX(-50%) rotateY(+10deg);
}
.card.show-answer .inner-card {
transform: rotateX(180deg);
}
.card .inner-card-front {
transform: rotateX(0deg);
}
.card .inner-card-back {
transform: rotateX(180deg);
}
.navigation {
display: flex;
margin: 20px 0;
}
.navigation .nav-button {
border: none;
background-color: transparent;
cursor: pointer;
font-size: 16px;
}
.navigation p {
margin: 0 25px;
}
.add-container {
opacity: 0;
z-index: -1;
background-color: #95a5a6;
border: 2px solid #eee;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 10px 0;
margin: 0;
position: absolute;
top: 0;
bottom: 0;
width: 100%;
transition: 0.4s ease;
}
.add-container .form-group label {
display: block;
margin: 20px 0 10px;
font-size: 1.5em;
font-weight: bold;
}
.add-container .form-group textarea {
border: 1px solid #aaa;
border-radius: 3px;
font-size: 16px;
padding: 12px;
min-width: 500px;
max-width: 100%;
}
.add-container.show {
opacity: 1;
z-index: 2;
}
寫入以上的內容後,你這時候應該會看到以下的畫面
畫面挺正常但沒有任何的按鈕有作用,點選卡片本身也不會翻動,這是非常正常的!
特別注意一下這次為了示範最初的畫面呈現,我有先將一些假資料寫進html中,之後需要清空,因為所有的資料都需要透過js動態載入。
Step2: 卡片結構分析
進入js事前規劃前我們必須要先理解,到底要怎麼樣實現卡片翻動的效果? 這個問題也困擾著當時的我,好在W3school有個相當基礎的教學讓我有了基本概念。
先仔細看一下我們在html的結構,你會發現實際上前後的元素都已經渲染出來了,有確實的出現在DOM tree,只是背面的元素被藏著沒有顯示而已。
要達成這樣的效果需要兩個css屬性配合
元素垂直翻轉了180度並蓋在原本的What is Javascript字串上,這就是因為此時由於翻轉的關係,元素已不是面向正面但我們又沒有加上backface-visibility: hidden屬性。理解這一點之後我想你大概知道該怎麼樣呈現兩者間的切換了,阿不就再翻個180度就好了嗎? 沒錯! 我們只要讓整個元素再次翻動180度就可以輕鬆達到這樣的效果了,目前在css中我們已經寫好一個class
.card.show-answer .inner-card {
transform: rotateX(180deg);
}
只要視情況給card元素加上.show-answer就可以讓inner-card再次翻轉,我們馬上來實驗下想法是否正確吧,請你偷偷的在html檔案下方加入以下的程式碼,這僅是作為實驗,今天結束後要記得刪掉:D
<script>
const card = document.querySelector(".card");
card.addEventListener("click", () => {
card.classList.toggle("show-answer");
});
</script>
加入後重整你的頁面並試著點擊卡片,我們朝思暮想的翻轉效果不就出現了?
我們今天完成了基本的UI畫面並了解了卡片翻轉的基礎原理,礙於篇幅的關係我今天必須停在這邊,明天開始我們會一步步的實踐剩餘的功能,今天可以先試著消化以上的內容即可,放輕鬆一些!
文章中的範例程式碼可以在這邊取得,歡迎自行取用
Danny,面試結束後HR聯繫我,要求我提供一些額外的個人資料,看起來與工作完全無關,那麼我應該怎麼辦?
這個問題剛好之前有在社群被問到,當時有人給了很詳細的回應,在這邊紀錄一下。通常遇到這樣的情況,很可能是面試的公司稍微有些傳統,台灣很多求職員工資料表都要填一大堆完全無用的資訊,舉凡你之前高中的社團到你哥有幾個孩子這樣,書面的東西就這樣了,許多額外的證明更是如此,我有聽過的像是良民證、個人信用資料或是貸款記錄等,根據台灣勞工局的解釋,勞工是有權利拒絕提供這類與工作無關的資訊,要是真的擔心的話你可以主動詢問一下為什麼需要這些資料,就像我之前提到的,面試求職是雙向的行為,真的不用把自己的地位放這麼低,很多需求都是可以討論的!
本文章同步發布於個人部落格,有興趣的朋友也可以來逛逛~!