# 10 JS projects - vanilla Javascript
---
tags: Javascript relate
---
###### tags: `Javascript`
# 製作十個小功能 JS 練習 - 1
## 成品: 漢堡選單功能

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/hamburger/index.html)
## 成品功能:
點擊漢堡會開關右側欄位
# HTML
## html程式碼:
```htmlembedded=
<body>
<button id="btn">☰</button>
<nav id="nav">
<ul>
<li><a href="">Google</a></li>
<li><a href="">Youtube</a></li>
<li><a href="">Twitter</a></li>
</ul>
</nav>
</body>
<script src="script.js"></script>
</html>
```
# CSS:
* 當class .active加btn及nav上面時則選單跳出消失則選單消失
* 使用簡單的特效取處在進出時稍慢一點
` transition: transform 0.3s ease-in-out;`
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
display: flex;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
}
上方為基礎設定 全置中 margin 0 border-box 字體設定
---
button {
background-color: gray;
border: none;
color: black;
position: fixed;
top: 20px;
right: 20px;
padding: 1rem;
transition: transform 0.3s ease-in-out;
}
button.active {
transform: translateX(-100px);
}
nav {
background-color: gray;
position: fixed;
top: 0;
right: 0;
height: 100vh;
padding: 2rem;
transform: translateX(100%);
}
nav.active {
transform: translateX(0)
}
/* 取消三個連結的padding,margin讓它們為0 */
nav ul {
padding: 0;
list-style-type: none;
margin: 0;
}
/* 行間上下的距離 */
nav ul li {
padding: 1rem 0;
}
nav a {
color: white;
text-decoration: none;
}
```
# JS:
* 抓取btn以及nav後做事件監聽
* 當點擊btn的時候nav,btn的元素會加上或是移除.active這個class
* `toggle()`的使用所以有開關的效果
## JS完整程式碼:
```javascript=
const btn = document.getElementById('btn');
const nav = document.getElementById('nav');
btn.addEventListener('click', (() => {
nav.classList.toggle('active');
btn.classList.toggle('active');
}))
```
# 製作十個小功能 JS 練習 - 2
## 成品: 點擊按鈕後跑出通知

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/toast-notification/index.html)
## 成品功能:
1. 點擊按鈕後跑出通知
1. 三秒後通知消失
# HTML
## html程式碼:
```htmlembedded=
<body>
<div id="container"></div>
<button id="btn">Click me Pretty Please</button>
</body>
```
# CSS:
* CSS處理.toast的修飾部分
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
display: flex;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
}
button {
background-color: gray;
color: white;
padding: 1rem;
/* 字體直接繼承body */
font-family: inherit;
border-radius: 5px;
border: none;
}
/* 把notification定位 */
#container {
position: fixed;
bottom: 10px;
right: 10px;
}
/* 針對notification修飾 */
.toast {
padding: 2rem;
background-color: gray;
color: white;
border-radius: 5px;
margin: 1rem;
}
```
# JS:
* 抓取btn以及container div
* 做事件監聽當點擊btn時會跑`createNotifiction()`
* 設置`setTimeout()`定時三秒消失
`createNotifiction()`
使用`createElement()`做div出來,給他加上class .toast(在toast做他的修飾)
並且`appnedChild()` notif 到container裡面就完成瞜!
## JS完整程式碼:
```javascript=
const btn = document.getElementById('btn');
const container = document.getElementById('container');
btn.addEventListener('click', () => {
createNotifiction();
});
function createNotifiction() {
const notif = document.createElement('div');
notif.classList.add('toast');
container.appendChild(notif);
notif.innerText = 'this challenge is crazy'
setTimeout(() => {
notif.remove();
}, 3000)
};
```
# 製作十個小功能 JS 練習 - 3
## 成品: 自動輸入文字的跑馬燈

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/auto-write-text/index.html)
## 成品功能:
* 文字會自動輸入在頁面中間
* 每0.1秒會跑下一段文字
* 不會中斷會無限跑下去
# HTML
空白配置
# CSS:
基本上也只修飾顏色跟字體
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
background-color: gray;
color: white;
font-size: 2rem;
display: flex;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
}
```
# JS:
* `slice(begin index,end inedex)`
到end index "之前"停止提取。
[slice() MDN](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Array/slice)
* 使用index++ 讓inneText不斷印出每個slice擷取的字
最後使用判斷式當index大於字串長度時歸零從頭開始
* 使用setInterval不斷的呼叫writeText函式秒0.1秒呼叫一次無限循環
## JS完整程式碼:
```javascript=
const text = 'This string is going to show on the browser automatically using Javascript';
let index = 0;
function writeText() {
document.body.innerText = text.slice(0, index);
index++;
if (index > text.length) {
index = 0;
}
};
setInterval(writeText, 100);
```
# 製作十個小功能 JS 練習 - 4
## 成品: 點擊按鈕後跳出視窗並且可以關閉

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/popup/index.html)
## 成品功能:
* 點擊按鈕後跳出視窗並且可以關閉
* 改變背景色
# HTML
## html程式碼:
```htmlembedded=
<body>
<button id="open">Open</button>
<div class="popup-container" id="container">
<div class="popup">
<button id="close">×</button>
<h1>Popup-btn</h1>
<p>Using JS to pop this up </p>
</div>
</div>
</body>
```
# CSS:
* 這邊的切換讓popup區塊出現
```css=
.popup-container.active {
display: flex;
}
```
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
display: flex;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
}
button {
background-color: black;
color: white;
padding: 1rem;
border-radius: 4px;
border: none;
font-family: inherit;
}
.popup-container {
position: absolute;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: none;
align-items: center;
justify-content: center;
}
.popup-container.active {
display: flex;
}
.popup {
background-color: #fff;
border-radius: 5px;
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
padding: 2rem;
position: relative;
width: 500px;
}
.popup button {
padding: .5rem;
background-color: #fff;
color: gray;
position: absolute;
top: 10px;
right: 10px;
}
```
# JS:
* 抓取open,close按鈕以及popup容器
* open, close設置事件監聽點擊時加上以及移除class.active
## JS完整程式碼:
```javascript=
const open = document.getElementById('open');
const close = document.getElementById('close');
const container = document.getElementById('container');
open.addEventListener('click', () => {
container.classList.add('active');
});
close.addEventListener('click', () => {
container.classList.remove('active');
});
```
# 製作十個小功能 JS 練習 - 5
## 成品: 下紫色愛心雨的背景

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/purple-heart-rain/index.html)
## 成品功能:
* 下紫色愛心雨
* 位置隨機
* 動畫延續時間隨機
# HTML
空白配置
# CSS:
heart的修飾
* 畫面一開始保持空白所以設置位置固定在螢幕外面
* 這邊把愛心移動到Y軸去生出來
* 動畫部分設置 延續0.3秒(讓愛心不那麼密集) linear線性(動畫等速移動) forwards(forwards代表動畫結束會就停在結束階段不會跑回去)
[animation-fill-mode forwards MDN](https://developer.mozilla.org/zh-TW/docs/Web/CSS/animation-fill-mode)
[animation-timing-function linear MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function)
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
display: flex;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
}
.heart {
position: fixed;
top: -1vh;
transform: translateY(0);
animation: fall 3s linear forwards;
font-size: 2rem;
}
@keyframes fall {
to {
transform: translateY(105vh);
}
}
```
# JS:
* `createElement('div')`創造愛心並且加上class .heart
* 把位置做隨機 使用style.left (其實right也可) 使用隨機數串接字串'vw'
* 把下降時間作隨機 使用style.animationDuration 使用隨機數串接字串's'
* 把出現在`innerText`的部分換成💜
* 把💜貼到body上面使用`appendchild()`
* 最後設置setTimeout讓愛心消失
* 每個愛心的出現時間0.3秒出現一顆`setInterval()`每0.3秒呼叫一次`createHeart()`
## JS完整程式碼:
```javascript=
function createHeart() {
const heart = document.createElement('div');
heart.classList.add('heart');
heart.style.left = Math.random() * 100 + 'vw';
heart.style.animationDuration = Math.random() * 2 + 5 + 's';
heart.innerText = '💜';
document.body.appendChild(heart);
setTimeout(() => {
heart.remove();
}, 5000)
}
setInterval(createHeart, 300);
```
# 製作十個小功能 JS 練習 - 6
## 成品: 點擊按鈕更改背景顏色

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/background-changer/index.html)
## 成品功能:
* 點擊按鈕更改被景色
# HTML
## html程式碼:
```htmlembedded=
<body>
<button id="btn">Change background</button>
</body>
```
# CSS:
修飾按鈕外觀
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
display: flex;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
}
button {
background-color: gray;
color: white;
padding: 1rem;
font-family: inherit;
box-shadow: 2px 2px 4px rgba(0, 0, 0, 0.2);
border-radius: 5px;
border: none;
outline: none;
}
button:hover {
cursor: pointer;
background-color: black;
}
```
# JS:
* 抓取btn後做事件監聽處理當點擊時觸發`randombg()`
* `randombg()`可以使用rgba或是hsl的方式用Math.floor(Math.random()*100)去處理顏色的係數變化並使用樣板字面值(Template literals)`${}`嵌入變數做操作
## JS完整程式碼:
```javascript=
const btn = document.getElementById('btn');
btn.addEventListener('click', () => {
document.body.style.background = randomBg();
})
function randomBg() {
// return `hsl(${Math.floor(Math.random()*360)}, 100%, 50%)`
return `rgba(${Math.floor(Math.random()*100)}%, ${Math.floor(Math.random()*100)}%, ${Math.floor(Math.random()*100)}%, ${Math.floor(Math.random()*100)}%)`
}
```
# 製作十個小功能 JS 練習 - 7
## 成品:製作一個切換頁面背景的按鈕

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/dark-mode-toggle/index.html)
## 成品功能:
* 製作一個切換頁面背景的按鈕
* 切換動畫有時間延遲
# HTML
label處的for必須要跟input的id名稱一樣才能作連動,也就是點擊label處input的checkbox連動,這樣點擊圓圈才有用
## html程式碼:
```htmlembedded=
<body>
<div class="toggle-container">
<input type="checkbox" id="toggle" name="toggle"><label for="toggle"></label>
</div>
<h1>using JS to toggle dark mode</h1>
<p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Cumque obcaecati sint, libero dignissimos aliquam natus,
magnam dolorum iure, porro commodi magni repellendus quis quibusdam accusantium culpa iusto! Unde, debitis
minima.</p>
</body>
```
# CSS:
* transition處理在body 並且因為背景以及文字都有改變所以都可以加入持續時間以及速度
* label的部分也會切換顏色所以一樣使用transition
* label需要有blcok屬性才有辦法設置width
* input做隱藏(把打勾框框藏起來)只出現label(所以要作連動)
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
text-align: center;
padding: 5rem;
transition: background 0.3s linear, color 0.3s linear;
}
body.dark {
background-color: #1f1f1f;
color: #fff;
}
.toggle-container {
position: fixed;
top: 10px;
right: 10px;
}
label {
background-color: gray;
border-radius: 50%;
display: inline-block;
width: 50px;
height: 50px;
cursor: pointer;
user-select: none;
transition: background 0.3s linear;
}
body.dark label {
background-color: #fff;
}
input {
visibility: hidden;
}
```
# JS:
* `e.target.checked`是 Boolean 會回傳true/false 也就是狀態checked與否,
* 當狀態是true則加入class .dark,flase的話則移除class .dark
* `toggle(要被開關的東西, boolean)`
[toggle MDN](https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle)
## JS完整程式碼:
```javascript=
const toggle = document.getElementById('toggle');
toggle.addEventListener('change', (e) => {
document.body.classList.toggle('dark', e.target.checked)
});
```
# 製作十個小功能 JS 練習 - 8
## 成品: 自己滾動的投影片

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/carousel/index.html)
## 成品功能:
* 每兩秒換一張投影片
* 換到最後一張時會回到最開頭
* 有慢進慢出特效
# HTML
## html程式碼:
```htmlembedded=
<body>
<div class="carousel">
<div class="image-container" id="imgs">
<img src="https://images.unsplash.com/photo-1599394022918-6c2776530abb?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1458&q=80"
alt="" />
<img src="https://images.unsplash.com/photo-1593642632559-0c6d3fc62b89?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80"
alt="" />
<img src="https://images.unsplash.com/photo-1599423300746-b62533397364?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80"
alt="" />
<img src="https://images.unsplash.com/photo-1599561046251-bfb9465b4c44?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1492&q=80"
alt="" />
</div>
</div>
</body>
```
# CSS:
* 使用overflow:hidden來隱藏圖片(不然因為flex圖片會並排並且出現卷軸)
* 在image-container做水平移動特效
* 使用`object-fit: cover;`讓圖片不失真
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
display: flex;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
}
.carousel {
box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.3);
overflow: hidden;
height: 500px;
width: 500px;
}
.image-container {
display: flex;
transition: transform 0.5s ease-in-out;
transform: translateX(0);
}
img {
object-fit: cover;
height: 500px;
width: 500px;
}
```
# JS:
* 抓取`translsateX`設定的視窗也就是image-container
* 為了取得index長度抓取所有的img tag
* 用style加上水平移動並且使用index * img寬度 切換投影片位置剛剛好到下一張
* 使用判斷式當idx加到超過img長度-1時回到開頭(因為只要跑三次)
## JS完整程式碼:
```javascript=
const imgs = document.getElementById('imgs');
const img = document.querySelectorAll('#img');
let idx = 0;
function run() {
idx++;
if (idx > img.length - 1) {
idx = 0;
}
imgs.style.transform = `translateX(${-idx * 500}px)`;
};
setInterval(run, 2000);
```
# 製作十個小功能 JS 練習 - 9
## 成品: 聲音按鈕

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/sound-board/index.html)
## 成品功能:
* 點擊按鈕會產生音效
* 當點擊下一個按鈕時上一個的音效會停止
* 下一次點擊同樣的按鈕時音效會歸零從頭開始跑
# HTML
* 設置audio tag並且引入檔案
## html程式碼:
```htmlembedded=
<body>
<audio id="clapping" src="sound/clapping.mp3"></audio>
<audio id="boo" src="sound/boo.mp3"></audio>
<audio id="gasp" src="sound/gasp.mp3"></audio>
</body>
```
# CSS:
* 處理按鈕外觀
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
display: flex;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
}
.btn {
background-color: gray;
color: white;
font-family: inherit;
font-size: 1.2rem;
padding: 1.5rem 3rem;
border-radius: 5px;
margin: 1rem;
border: none;
}
```
# JS:
* 建立陣列內容是音檔的id名稱
* 使用forEach個別印出所有的音檔的名字並且產生按鈕
* 處理點擊事件 1.先引入停止函式 2.開始撥放音效(這個順序讓聲音還是可以正常撥出)
* 停止函式的部分會把音效暫停`song.pause();`並且把撥放時間歸零`song.currentTime = 0;`
## JS完整程式碼:
```javascript=
const sounds = [
'clapping',
'boo',
'gasp',
]
sounds.forEach((sound) => {
const btn = document.createElement('button');
btn.classList.add('btn');
document.body.appendChild(btn);
btn.innerText = sound;
btn.addEventListener('click', () => {
stopSounds()
document.getElementById(sound).play();
})
})
function stopSounds() {
sounds.forEach((sound) => {
const song = document.getElementById(sound);
song.pause();
song.currentTime = 0;
})
}
```
# 製作十個小功能 JS 練習 - 10
## 成品: 有ZOOM效果的圖片

[成品網址](https://chiehliu.github.io/git-projects/10-Project-1-Hour/zoom-effect/index.html)
## 成品功能:
* 隨著滑鼠移動會有放大效果
* 滑鼠離開視窗效果就會解除
# HTML
## html程式碼:
```htmlembedded=
<body>
<div id="container">
<img src="https://images.unsplash.com/photo-1582769923195-c6e60dc1d8dc?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1000&q=80"
alt="purple kitty" />
</div>
</body>
```
# CSS:
* `overflow: hidden;`可以限制圖片在原地,非常重要
## CSS完整程式碼
```css=
@import url("https://fonts.googleapis.com/css2?family=Poppins:wght@200;400;600&display=swap");
* {
box-sizing: border-box;
}
body {
background-color: rebeccapurple;
display: flex;
align-items: center;
justify-content: center;
font-family: "Poppins", sans-serif;
margin: 0;
min-height: 100vh;
}
#container {
box-shadow: 3px 3px 4px rgba(0, 0, 0, 0.3);
height: 500px;
width: 500px;
overflow: hidden;
}
img {
transform-origin: center center;
object-fit: cover;
height: 100%;
width: 100%;
}
```
# JS:
* 抓取container, img
* clientX 代表滑鼠在瀏覽器上面的位置
* offsetLeft距離

* 故`x = e.clientX - e.target.offsetLeft;`就等於圖片上面的x軸的位置,y軸同理

* `scale`以及`transform-origin`的組合技會友針對位置放大的效果
* 透過mouseleave事件把組合技還原
[overflow MDN](https://developer.mozilla.org/zh-CN/docs/Web/CSS/overflow)
[offsetLeft MDN](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/offsetLeft)
[clientX MDN](https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/clientX)
[transform-origin MDN](https://developer.mozilla.org/zh-TW/docs/Web/CSS/transform-origin)
## JS完整程式碼:
```javascript=
const container = document.getElementById('container');
const img = document.querySelector('img');
container.addEventListener('mousemove', (e) => {
const x = e.clientX - e.target.offsetLeft;
const y = e.clientY - e.target.offsetTop;
img.style.transformOrigin = `${x}px ${y}px`;
img.style.transform = 'scale(2)';
})
container.addEventListener('mouseleave', () => {
img.style.tranformOrigin = '0 0';
img.style.transform = 'scale(1)';
})
```