(Complete guide, from Novice to Ninja)
Learning Udemy Course:Modern JavaScript
建立者:The Net Ninja (Shaun Pelling)
Learn Modern JavaScript from the very start to ninja-level & build awesome JavaScript applications.
您會學到
- Learn how to program with modern JavaScript, from the very beginning to more advanced topics
- Learn all about OOP (object-oriented programming) with JavaScript, working with prototypes & classes
- Learn how to create real-world front-end applications with JavaScript (quizes, weather apps, chat rooms etc)
- Learn how to make useful JavaScript driven UI components like popups, drop-downs, tabs, tool-tips & more.
- Learn how to use modern, cutting-edge JavaScript features today by using a modern workflow (Babel & Webpack)
- Learn how to use real-time databases to store, retrieve and update application data
- Explore API’s to make the most of third-party data (such as weather information)
Forms & Form Events
Events Inside Forms
Form Events
- Capture data or information from a user
- E.g. a username & password
Submit Events
- “submit” event, which submits the form
- keyboard events
Submit Events
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
<style>
body {
background: #eee;
}
form {
max-width: 200px;
margin: 40px auto;
background: white;
padding: 10px;
}
input {
display: block;
margin: 10px auto;
padding: 4px;
}
</style>
</head>
<body>
<form class="signup-form">
<input type="text" id="username" placeholder="username">
<input type="submit" value="submit">
</form>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js
const form = document.querySelector('.signup-form');
// const username = document.querySelector('#username');
form.addEventListener('submit', e => {
e.preventDefault();
// console.log(username.value);
console.log(form.username.value);
});
Regular Expressions (正規表達式)
資源
Regex101 介紹
- ninja
- ^ninja$
- ^[a-z]$
- ^[c-p]$
- ^[a-zA-Z]$
- ^[a-zA-Z]{6,10}$,大小寫a~z、字數6~10
- ^[a-zA-Z0-9]{6,10}$,大小寫a-z、數字0到9、字數6~10
- ^.{6,10}$,任何字符字元、字數6~10
Testing RegEx Patterns
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
<style>
body {
background: #eee;
}
form {
max-width: 200px;
margin: 40px auto;
background: white;
padding: 10px;
}
input {
display: block;
margin: 10px auto;
padding: 4px;
}
</style>
</head>
<body>
<form class="signup-form">
<input type="text" id="username" placeholder="username">
<input type="submit" value="submit">
</form>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js - 1
const form = document.querySelector('.signup-form');
// const username = document.querySelector('#username');
form.addEventListener('submit', e => {
e.preventDefault();
// console.log(username.value);
console.log(form.username.value);
});
// testing RegEx
const username = 'shaunp123';
const pattern = /^[a-z]{6,}$/;
let result = pattern.test(username);
// console.log(result);
if(result){
console.log('regex test passed :)');
}
else{
console.log('regex test failed :(');
}
// sandbox.js - 2
const form = document.querySelector('.signup-form');
// const username = document.querySelector('#username');
form.addEventListener('submit', e => {
e.preventDefault();
// console.log(username.value);
console.log(form.username.value);
});
// testing RegEx
const username = 'shaunp123';
const pattern = /^[a-z]{6,}$/;
// let result = pattern.test(username);
// // console.log(result);
// if(result){
// console.log('regex test passed :)');
// }
// else{
// console.log('regex test failed :(');
// }
let result = username.search(pattern);
console.log(result);
// Google Console - 2
-1
>
// sandbox.js - 3
const form = document.querySelector('.signup-form');
// const username = document.querySelector('#username');
form.addEventListener('submit', e => {
e.preventDefault();
// console.log(username.value);
console.log(form.username.value);
});
// testing RegEx
const username = 'shaunp';
const pattern = /^[a-z]{6,}$/;
// let result = pattern.test(username);
// // console.log(result);
// if(result){
// console.log('regex test passed :)');
// }
// else{
// console.log('regex test failed :(');
// }
let result = username.search(pattern);
console.log(result);
// Google Console - 3
0
>
// sandbox.js - 4
const form = document.querySelector('.signup-form');
// const username = document.querySelector('#username');
form.addEventListener('submit', e => {
e.preventDefault();
// console.log(username.value);
console.log(form.username.value);
});
// testing RegEx
const username = '3434shaunp3656';
const pattern = /[a-z]{6,}/;
// let result = pattern.test(username);
// // console.log(result);
// if(result){
// console.log('regex test passed :)');
// }
// else{
// console.log('regex test failed :(');
// }
let result = username.search(pattern);
console.log(result);
// Google Console - 4
4
>
// sandbox.js - 5
const form = document.querySelector('.signup-form');
// const username = document.querySelector('#username');
form.addEventListener('submit', e => {
e.preventDefault();
// console.log(username.value);
console.log(form.username.value);
});
// testing RegEx
const username = '3434shaunp3656';
const pattern = /^[a-z]{6,}$/;
// let result = pattern.test(username);
// // console.log(result);
// if(result){
// console.log('regex test passed :)');
// }
// else{
// console.log('regex test failed :(');
// }
let result = username.search(pattern);
console.log(result);
// Google Console - 5
-1
>
Basic Form Validation
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
<style>
body {
background: #eee;
}
form {
max-width: 200px;
margin: 40px auto;
background: white;
padding: 10px;
}
input {
display: block;
margin: 10px auto;
padding: 4px;
}
</style>
</head>
<body>
<form class="signup-form">
<input type="text" id="username" name="username" placeholder="username">
<input type="submit" value="submit">
<div class="feedback"></div>
</form>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js
const form = document.querySelector('.signup-form');
const feedback = document.querySelector('.feedback');
form.addEventListener('submit', e => {
e.preventDefault();
// validation
const username = form.username.value;
const usernamePattern = /^[a-zA-Z]{6,12}$/;
if(usernamePattern.test(username)){
// feedback good info
feedback.textContent = 'that username is valid!';
}
else{
// feedback help info
feedback.textContent = 'username must contain letters only & be between 6 & 12 characters long';
}
});
Keyboard Events
MDN 文件
// index.html - 1
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
<style>
body {
background: #eee;
}
form {
max-width: 200px;
margin: 40px auto;
background: white;
padding: 10px;
}
input {
display: block;
margin: 10px auto;
padding: 4px;
}
</style>
</head>
<body>
<form class="signup-form">
<input type="text" id="username" name="username" placeholder="username">
<input type="submit" value="submit">
<div class="feedback"></div>
</form>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js - 1
const form = document.querySelector('.signup-form');
const feedback = document.querySelector('.feedback');
const usernamePattern = /^[a-zA-Z]{6,12}$/;
form.addEventListener('submit', e => {
e.preventDefault();
// validation
const username = form.username.value;
if(usernamePattern.test(username)){
// feedback good info
feedback.textContent = 'that username is valid!';
}
else{
// feedback help info
feedback.textContent = 'username must contain letters only & be between 6 & 12 characters long';
}
});
// live feedback
form.username.addEventListener('keyup', e => {
// console.log(e.target.value, form.username.value);
if(usernamePattern.test(e.target.value)){
console.log('passed');
}
else{
console.log('failed');
}
});
// Google Console - 1
5 failed
passed
>
// index.html - 2
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
<style>
body {
background: #eee;
}
form {
max-width: 200px;
margin: 40px auto;
background: white;
padding: 10px;
}
input {
display: block;
margin: 10px auto;
padding: 4px;
}
.success {
border: 2px solid limegreen;
}
.error {
border: 2px solid crimson;
}
</style>
</head>
<body>
<form class="signup-form">
<input type="text" id="username" name="username" placeholder="username">
<input type="submit" value="submit">
<div class="feedback"></div>
</form>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js - 2
const form = document.querySelector('.signup-form');
const feedback = document.querySelector('.feedback');
const usernamePattern = /^[a-zA-Z]{6,12}$/;
form.addEventListener('submit', e => {
e.preventDefault();
// validation
const username = form.username.value;
if(usernamePattern.test(username)){
// feedback good info
feedback.textContent = 'that username is valid!';
}
else{
// feedback help info
feedback.textContent = 'username must contain letters only & be between 6 & 12 characters long';
}
});
// live feedback
form.username.addEventListener('keyup', e => {
// console.log(e.target.value, form.username.value);
if(usernamePattern.test(e.target.value)){
// console.log('passed');
form.username.setAttribute('class', 'success');
}
else{
// console.log('failed');
form.username.setAttribute('class', 'error');
}
});
// sandbox.js - 3 console.log(e);
const form = document.querySelector('.signup-form');
const feedback = document.querySelector('.feedback');
const usernamePattern = /^[a-zA-Z]{6,12}$/;
form.addEventListener('submit', e => {
e.preventDefault();
// validation
const username = form.username.value;
if(usernamePattern.test(username)){
// feedback good info
feedback.textContent = 'that username is valid!';
}
else{
// feedback help info
feedback.textContent = 'username must contain letters only & be between 6 & 12 characters long';
}
});
// live feedback
form.username.addEventListener('keyup', e => {
console.log(e);
// console.log(e.target.value, form.username.value);
if(usernamePattern.test(e.target.value)){
// console.log('passed');
form.username.setAttribute('class', 'success');
}
else{
// console.log('failed');
form.username.setAttribute('class', 'error');
}
});
Project – Interactive Ninja Quiz
Project Preview & Setup
Bootstrap Basics
資源
- Bootstrap Website – 使用 v4.3
HTML Template
資源
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>Ninja Quiz</title>
</head>
<body>
<!-- top section -->
<div class="intro py-3 bg-white text-center">
<div class="container">
<h2 class="text-primary display-3 my-4">Ninja Quiz</h2>
</div>
</div>
<!-- quiz section -->
<div class="quiz py-4 bg-primary">
<div class="container">
<h2 class="my-5 text-white">On with the questions...</h2>
<form class="quiz-form text-light">
<div class="my-5">
<p class="lead font-weight-normal">1. How do you give a ninja directions?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="A" checked>
<label class="form-check-label">Show them a map</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="B">
<label class="form-check-label">Don't worry, a ninja will find you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">2. If a ninja has 3 apples, then gives one to Mario & one to Yoshi, how many apples does the ninja have left?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="A" checked>
<label class="form-check-label">1 apple</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="B">
<label class="form-check-label">3 apples, and two corpses</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">3. How do you know when you've met a ninja?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="A" checked>
<label class="form-check-label">You'll recognize the outfit</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="B">
<label class="form-check-label">The grim reaper will tell you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">4. What is a ninja's favorite array method?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="A" checked>
<label class="form-check-label">forEach()</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="B">
<label class="form-check-label">Slice()</label>
</div>
</div>
<div class="text-center">
<input type="submit" class="btn btn-light">
</div>
</form>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
Checking Answers
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>Ninja Quiz</title>
</head>
<body>
<!-- top section -->
<div class="intro py-3 bg-white text-center">
<div class="container">
<h2 class="text-primary display-3 my-4">Ninja Quiz</h2>
</div>
</div>
<!-- quiz section -->
<div class="quiz py-4 bg-primary">
<div class="container">
<h2 class="my-5 text-white">On with the questions...</h2>
<form class="quiz-form text-light">
<div class="my-5">
<p class="lead font-weight-normal">1. How do you give a ninja directions?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="A" checked>
<label class="form-check-label">Show them a map</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="B">
<label class="form-check-label">Don't worry, a ninja will find you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">2. If a ninja has 3 apples, then gives one to Mario & one to Yoshi, how many apples does the ninja have left?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="A" checked>
<label class="form-check-label">1 apple</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="B">
<label class="form-check-label">3 apples, and two corpses</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">3. How do you know when you've met a ninja?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="A" checked>
<label class="form-check-label">You'll recognize the outfit</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="B">
<label class="form-check-label">The grim reaper will tell you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">4. What is a ninja's favorite array method?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="A" checked>
<label class="form-check-label">forEach()</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="B">
<label class="form-check-label">Slice()</label>
</div>
</div>
<div class="text-center">
<input type="submit" class="btn btn-light">
</div>
</form>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
// app.js
const correctAnswers = ['B','B','B','B'];
const form = document.querySelector('.quiz-form');
form.addEventListener('submit', e => {
e.preventDefault();
let score = 0;
const userAnswers = [form.q1.value, form.q2.value, form.q3.value, form.q4.value];
// check answers
userAnswers.forEach((answer, index) => {
if(answer === correctAnswers[index]){
score += 25;
}
});
console.log(score);
});
Showing the Score
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>Ninja Quiz</title>
</head>
<body>
<!-- top section -->
<div class="intro py-3 bg-white text-center">
<div class="container">
<h2 class="text-primary display-3 my-4">Ninja Quiz</h2>
</div>
</div>
<!-- result section -->
<div class="result py-4 d-none bg-light text-center">
<div class="container lead">
<p>You are <span class="text-primary display-4 p-3">0%</span> ninja</p>
</div>
</div>
<!-- quiz section -->
<div class="quiz py-4 bg-primary">
<div class="container">
<h2 class="my-5 text-white">On with the questions...</h2>
<form class="quiz-form text-light">
<div class="my-5">
<p class="lead font-weight-normal">1. How do you give a ninja directions?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="A" checked>
<label class="form-check-label">Show them a map</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="B">
<label class="form-check-label">Don't worry, a ninja will find you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">2. If a ninja has 3 apples, then gives one to Mario & one to Yoshi, how many apples does the ninja have left?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="A" checked>
<label class="form-check-label">1 apple</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="B">
<label class="form-check-label">3 apples, and two corpses</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">3. How do you know when you've met a ninja?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="A" checked>
<label class="form-check-label">You'll recognize the outfit</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="B">
<label class="form-check-label">The grim reaper will tell you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">4. What is a ninja's favorite array method?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="A" checked>
<label class="form-check-label">forEach()</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="B">
<label class="form-check-label">Slice()</label>
</div>
</div>
<div class="text-center">
<input type="submit" class="btn btn-light">
</div>
</form>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
// app.js
const correctAnswers = ['B','B','B','B'];
const form = document.querySelector('.quiz-form');
const result = document.querySelector('.result');
form.addEventListener('submit', e => {
e.preventDefault();
let score = 0;
const userAnswers = [form.q1.value, form.q2.value, form.q3.value, form.q4.value];
// check answers
userAnswers.forEach((answer, index) => {
if(answer === correctAnswers[index]){
score += 25;
}
});
// show result on page
result.querySelector('span').textContent = `${score}%`;
result.classList.remove('d-none');
});
The Window Object (觀念、scrollTo)
// Google Console
> window
< Window {window: Window, self: Window, document: document, name: '', location: Location, …}
> window.outerWidth
< 1280
> outerWidth
< 1280
>
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>Ninja Quiz</title>
</head>
<body>
<!-- top section -->
<div class="intro py-3 bg-white text-center">
<div class="container">
<h2 class="text-primary display-3 my-4">Ninja Quiz</h2>
</div>
</div>
<!-- result section -->
<div class="result py-4 d-none bg-light text-center">
<div class="container lead">
<p>You are <span class="text-primary display-4 p-3">0%</span> ninja</p>
</div>
</div>
<!-- quiz section -->
<div class="quiz py-4 bg-primary">
<div class="container">
<h2 class="my-5 text-white">On with the questions...</h2>
<form class="quiz-form text-light">
<div class="my-5">
<p class="lead font-weight-normal">1. How do you give a ninja directions?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="A" checked>
<label class="form-check-label">Show them a map</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="B">
<label class="form-check-label">Don't worry, a ninja will find you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">2. If a ninja has 3 apples, then gives one to Mario & one to Yoshi, how many apples does the ninja have left?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="A" checked>
<label class="form-check-label">1 apple</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="B">
<label class="form-check-label">3 apples, and two corpses</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">3. How do you know when you've met a ninja?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="A" checked>
<label class="form-check-label">You'll recognize the outfit</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="B">
<label class="form-check-label">The grim reaper will tell you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">4. What is a ninja's favorite array method?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="A" checked>
<label class="form-check-label">forEach()</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="B">
<label class="form-check-label">Slice()</label>
</div>
</div>
<div class="text-center">
<input type="submit" class="btn btn-light">
</div>
</form>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
// app.js
const correctAnswers = ['B','B','B','B'];
const form = document.querySelector('.quiz-form');
const result = document.querySelector('.result');
form.addEventListener('submit', e => {
e.preventDefault();
let score = 0;
const userAnswers = [form.q1.value, form.q2.value, form.q3.value, form.q4.value];
// check answers
userAnswers.forEach((answer, index) => {
if(answer === correctAnswers[index]){
score += 25;
}
});
// show result on page
scrollTo(0,0);
result.querySelector('span').textContent = `${score}%`;
result.classList.remove('d-none');
});
// window object (global object)
// console.log('hello');
// window.console.log('hello');
// console.log(document.querySelector('form'));
// console.log(window.document.querySelector('form'));
// alert('hello');
// window.alert('hello');
// setTimeout(() => {
// alert('hello, ninjas');
// }, 3000);
Intervals & Animating the Score
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<title>Ninja Quiz</title>
</head>
<body>
<!-- top section -->
<div class="intro py-3 bg-white text-center">
<div class="container">
<h2 class="text-primary display-3 my-4">Ninja Quiz</h2>
</div>
</div>
<!-- result section -->
<div class="result py-4 d-none bg-light text-center">
<div class="container lead">
<p>You are <span class="text-primary display-4 p-3">0%</span> ninja</p>
</div>
</div>
<!-- quiz section -->
<div class="quiz py-4 bg-primary">
<div class="container">
<h2 class="my-5 text-white">On with the questions...</h2>
<form class="quiz-form text-light">
<div class="my-5">
<p class="lead font-weight-normal">1. How do you give a ninja directions?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="A" checked>
<label class="form-check-label">Show them a map</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q1" value="B">
<label class="form-check-label">Don't worry, a ninja will find you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">2. If a ninja has 3 apples, then gives one to Mario & one to Yoshi, how many apples does the ninja have left?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="A" checked>
<label class="form-check-label">1 apple</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q2" value="B">
<label class="form-check-label">3 apples, and two corpses</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">3. How do you know when you've met a ninja?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="A" checked>
<label class="form-check-label">You'll recognize the outfit</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q3" value="B">
<label class="form-check-label">The grim reaper will tell you</label>
</div>
</div>
<div class="my-5">
<p class="lead font-weight-normal">4. What is a ninja's favorite array method?</p>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="A" checked>
<label class="form-check-label">forEach()</label>
</div>
<div class="form-check my-2 text-white-50">
<input type="radio" name="q4" value="B">
<label class="form-check-label">Slice()</label>
</div>
</div>
<div class="text-center">
<input type="submit" class="btn btn-light">
</div>
</form>
</div>
</div>
<script src="app.js"></script>
</body>
</html>
// app.js
const correctAnswers = ['B','B','B','B'];
const form = document.querySelector('.quiz-form');
const result = document.querySelector('.result');
form.addEventListener('submit', e => {
e.preventDefault();
let score = 0;
const userAnswers = [form.q1.value, form.q2.value, form.q3.value, form.q4.value];
// check answers
userAnswers.forEach((answer, index) => {
if(answer === correctAnswers[index]){
score += 25;
}
});
// show result on page
scrollTo(0,0);
result.classList.remove('d-none');
let output = 0;
const timer = setInterval(() => {
result.querySelector('span').textContent = `${output}%`;
if(output === score){
clearInterval(timer);
}
else{
output++;
}
}, 10);
});
// setTimeout(() => {
// // do something
// }, 3000);
// let i = 0;
// const timer = setInterval(() => {
// console.log('hello');
// i++;
// if(i === 5){
// clearInterval(timer);
// }
// }, 1000);
Array Methods(陣列方法)
Filter Method
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<h1>Array Methods</h1>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js - 1
const scores = [10, 30, 15, 25, 50, 40, 5];
// const filteredScores = scores.filter((score) => {
// return score > 20;
// });
// console.log(filteredScores);
const users = [
{name: 'shaun', premium: true},
{name: 'yoshi', premium: false},
{name: 'mario', premium: false},
{name: 'chun-li', premium: true}
];
const premiumUsers = users.filter(user => {
return user.premium
});
console.log(premiumUsers);
// sandbox.js - 2
const scores = [10, 30, 15, 25, 50, 40, 5];
// const filteredScores = scores.filter((score) => {
// return score > 20;
// });
// console.log(filteredScores);
const users = [
{name: 'shaun', premium: true},
{name: 'yoshi', premium: false},
{name: 'mario', premium: false},
{name: 'chun-li', premium: true}
];
const premiumUsers = users.filter(user => user.premium);
console.log(premiumUsers);
Map Method
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<h1>Array Methods</h1>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js - 1
// map method
const prices = [20, 10, 30, 25, 15, 40, 80, 5];
const salePrices = prices.map((price) => {
return price / 2;
});
console.log(salePrices);
// sandbox.js - 2 縮寫
// map method
const prices = [20, 10, 30, 25, 15, 40, 80, 5];
const salePrices = prices.map(price => price / 2);
console.log(salePrices);
// sandbox.js - 3 與 4 做比較
// map method
const prices = [20, 10, 30, 25, 15, 40, 80, 5];
// const salePrices = prices.map(price => price / 2);
// console.log(salePrices);
const products = [
{name: 'gold star', price: 20},
{name: 'mushroom', price: 40},
{name: 'green shells', price: 30},
{name: 'banana skin', price: 10},
{name: 'red shells', price: 50}
];
const saleProducts = products.map((product) => {
if(product.price > 30){
product.price = product.price / 2;
return product;
}
else {
return product;
}
});
console.log(saleProducts, products);
// sandbox.js - 4 與 3 做比較
// map method
const prices = [20, 10, 30, 25, 15, 40, 80, 5];
// const salePrices = prices.map(price => price / 2);
// console.log(salePrices);
const products = [
{name: 'gold star', price: 20},
{name: 'mushroom', price: 40},
{name: 'green shells', price: 30},
{name: 'banana skin', price: 10},
{name: 'red shells', price: 50}
];
const saleProducts = products.map((product) => {
if(product.price > 30){
return {name: product.name, price: product.price / 2};
}
else {
return product;
}
});
console.log(saleProducts, products);
// sandbox.js - 5 縮寫
// map method
const prices = [20, 10, 30, 25, 15, 40, 80, 5];
// const salePrices = prices.map(price => price / 2);
// console.log(salePrices);
const products = [
{name: 'gold star', price: 20},
{name: 'mushroom', price: 40},
{name: 'green shells', price: 30},
{name: 'banana skin', price: 10},
{name: 'red shells', price: 50}
];
const saleProducts = products.map(product => {
if(product.price > 30){
return {name: product.name, price: product.price / 2};
}
else {
return product;
}
});
console.log(saleProducts, products);
Reduce Method
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<h1>Array Methods</h1>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js - 1
// reduce method
const scores = [10, 20, 60, 40, 70, 90, 30];
const result = scores.reduce((acc, curr) => {
if(curr > 50){
acc++;
}
return acc;
}, 0);
console.log(result);
// Google Console - 1
3
>
// sandbox.js - 2
// reduce method
// const scores = [10, 20, 60, 40, 70, 90, 30];
// const result = scores.reduce((acc, curr) => {
// if(curr > 50){
// acc++;
// }
// return acc;
// }, 0);
// console.log(result);
const scores = [
{player: 'mario', score: 50},
{player: 'yoshi', score: 30},
{player: 'mario', score: 70},
{player: 'crystal', score: 60},
{player: 'mario', score: 50},
{player: 'yoshi', score: 30},
{player: 'mario', score: 70},
{player: 'crystal', score: 60},
{player: 'mario', score: 90},
{player: 'yoshi', score: 30},
{player: 'mario', score: 30},
{player: 'crystal', score: 60},
{player: 'mario', score: 50},
{player: 'yoshi', score: 30},
{player: 'mario', score: 80},
{player: 'crystal', score: 60}
];
const marioTotal = scores.reduce((acc, curr) => {
if(curr.player === 'mario'){
acc += curr.score;
}
return acc;
}, 0);
console.log(marioTotal);
// Google Console - 2
490
>
補充 MDN 文件
Find Method
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<h1>Array Methods</h1>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js - 1
// find method
const scores = [10, 5, 0, 40, 30, 10, 90, 70];
const firstHighScore = scores.find((score) => {
return score > 50;
});
console.log(firstHighScore);
// sandbox.js - 2 縮寫
// find method
const scores = [10, 5, 0, 40, 30, 10, 90, 70];
const firstHighScore = scores.find(score => score > 50);
console.log(firstHighScore);
Sort Method (排序方法)
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<h1>Array Methods</h1>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js - 1
// example 1 - sorting strings
const names = ['mario', 'shaun', 'chun-li', 'yoshi', 'luigi'];
// names.sort();
// names.reverse();
// console.log(names);
// example 2 - sorting numbers
const scores = [10, 50, 20, 5, 35, 70, 45];
// scores.sort();
// scores.reverse();
// console.log(scores);
// example 3 - sorting objects
const players = [
{name: 'mario', score: 20},
{name: 'luigi', score: 10},
{name: 'chun-li', score: 50},
{name: 'yoshi', score: 30},
{name: 'shaun', score: 70}
];
players.sort((a,b) => {
if(a.score > b.score){
return -1;
} else if (b.score > a.score){
return 1;
} else {
return 0;
}
});
players.sort((a,b) => {
return b.score - a.score;
});
console.log(players);
// sandbox.js - 2 縮寫
// example 1 - sorting strings
const names = ['mario', 'shaun', 'chun-li', 'yoshi', 'luigi'];
// names.sort();
// names.reverse();
// console.log(names);
// example 2 - sorting numbers
const scores = [10, 50, 20, 5, 35, 70, 45];
// scores.sort();
// scores.reverse();
// console.log(scores);
// example 3 - sorting objects
const players = [
{name: 'mario', score: 20},
{name: 'luigi', score: 10},
{name: 'chun-li', score: 50},
{name: 'yoshi', score: 30},
{name: 'shaun', score: 70}
];
players.sort((a,b) => {
if(a.score > b.score){
return -1;
} else if (b.score > a.score){
return 1;
} else {
return 0;
}
});
players.sort((a,b) => b.score - a.score);
console.log(players);
// sandbox.js - 3
// example 1 - sorting strings
const names = ['mario', 'shaun', 'chun-li', 'yoshi', 'luigi'];
// names.sort();
// names.reverse();
// console.log(names);
// example 2 - sorting numbers
const scores = [10, 50, 20, 5, 35, 70, 45];
// scores.sort();
// scores.reverse();
// console.log(scores);
// example 3 - sorting objects
const players = [
{name: 'mario', score: 20},
{name: 'luigi', score: 10},
{name: 'chun-li', score: 50},
{name: 'yoshi', score: 30},
{name: 'shaun', score: 70}
];
// players.sort((a,b) => {
// if(a.score > b.score){
// return -1;
// } else if (b.score > a.score){
// return 1;
// } else {
// return 0;
// }
// });
players.sort((a,b) => b.score - a.score);
console.log(players);
// sandbox.js - 4
// example 1 - sorting strings
const names = ['mario', 'shaun', 'chun-li', 'yoshi', 'luigi'];
// names.sort();
// names.reverse();
// console.log(names);
// example 2 - sorting numbers
const scores = [10, 50, 20, 5, 35, 70, 45];
// scores.sort();
// scores.reverse();
// console.log(scores);
scores.sort((a,b) => a - b);
console.log(scores);
// example 3 - sorting objects
const players = [
{name: 'mario', score: 20},
{name: 'luigi', score: 10},
{name: 'chun-li', score: 50},
{name: 'yoshi', score: 30},
{name: 'shaun', score: 70}
];
// players.sort((a,b) => {
// if(a.score > b.score){
// return -1;
// } else if (b.score > a.score){
// return 1;
// } else {
// return 0;
// }
// });
players.sort((a,b) => b.score - a.score);
console.log(players);
// Google Console - 4
(7) [5, 10, 20, 35, 45, 50, 70]
(5) [{...}, {...}, {...}, {...}, {...}
0: {name: 'shaun', score: 70}
1: {name: 'chun-li', score: 50}
2: {name: 'yoshi', score: 30}
3: {name: 'mario', score: 20}
4: {name: 'luigi', score: 10}
length: 5
[[Prototype]]: Array(0)
>
MDN 文件
Chaining Array Methods
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<h1>Array Methods</h1>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js - 1
const products = [
{name: 'gold star', price: 30},
{name: 'green shell', price: 10},
{name: 'red shell', price: 40},
{name: 'banana skin', price: 5},
{name: 'mushroom', price: 50}
];
const filtered = products.filter(product => product.price > 20);
const promos = filtered.map(product => {
return `the ${product.name} is ${product.price / 2} pounds`;
});
console.log(promos);
// sandbox.js - 2
const products = [
{name: 'gold star', price: 30},
{name: 'green shell', price: 10},
{name: 'red shell', price: 40},
{name: 'banana skin', price: 5},
{name: 'mushroom', price: 50}
];
// const filtered = products.filter(product => product.price > 20);
// const promos = filtered.map(product => {
// return `the ${product.name} is ${product.price / 2} pounds`;
// });
const promos = products
.filter(product => product.price > 20)
.map(product => `the ${product.name} is ${product.price / 2} pounds`);
console.log(promos);
// Google Console - 2
(3) ['the gold star is 15 pounds', 'the red shell is 20 pounds', 'the mushroom is 25 pounds']
>
Project – Todo List (待辦清單)
Project Preview and Setup
操作步驟
- 新增專案資料夾 todos
- 新增 style.css、app.js 檔案
- 新增 index.html 檔案、載入 CSS、JS、Bootstrap、Font Awesome
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css" integrity="sha384-AYmEC3Yw5cVb3ZcuHtOA93w35dYTsvhLPVnYs9eStHfGJvOvKxVfELGroGkvsg+p" crossorigin="anonymous">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css" integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" crossorigin="anonymous">
<link rel="stylesheet" href="style.css">
<title>Todos</title>
</head>
<body>
<script src="app.js"></script>
</body>
</html>
HTML & CSS Template
資源
操作步驟
- index.html 程式碼撰寫
- style.css 程式碼撰寫
// index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://pro.fontawesome.com/releases/v5.10.0/css/all.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
<link rel="stylesheet" href="style.css">
<title>Todos</title>
</head>
<body>
<div class="container">
<header class="text-center text-light my-4">
<h1 class="mb-4">Todo List</h1>
<form class="search">
<input class="form-control m-auto" type="text" name="search" placeholder="search todos">
</form>
</header>
<ul class="list-group todos mx-auto text-light">
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>play mariokart</span>
<i class="far fa-trash-alt delete"></i>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>defeat ganon in zelda</span>
<i class="far fa-trash-alt delete"></i>
</li>
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>make a veggie pie</span>
<i class="far fa-trash-alt delete"></i>
</li>
</ul>
<form class="add text-center my-4">
<label class="text-light">Add a new todo...</label>
<input class="form-control m-auto" type="text" name="add">
</form>
</div>
<script src="app.js"></script>
</body>
</html>
// style.css
body{
background: #352f5b;
}
.container{
max-width: 400px;
}
input[type=text],
input[type=text]:focus{
color: #fff;
border: none;
background: rgba(0,0,0,0.2);
max-width: 400px;
}
.todos li{
background: #423a6f;
}
.delete{
cursor: pointer;
}
Adding Todos
// app.js
const addForm = document.querySelector('.add');
const list = document.querySelector('.todos');
const generateTemplate = todo => {
const html = `
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>${todo}</span>
<i class="far fa-trash-alt delete"></i>
</li>
`;
list.innerHTML += html;
}
addForm.addEventListener('submit', e => {
e.preventDefault();
const todo = addForm.add.value.trim();
// console.log(todo);
if(todo.length){
generateTemplate(todo);
addForm.reset();
}
});
Todo text overflowing outside the list
解決方式有兩種
- word-wrap: break-word;
- word-break: break-all;
// style.css - word-break: break-all;
body{
background: #352f5b;
}
.container{
max-width: 400px;
}
input[type=text],
input[type=text]:focus{
color: #fff;
border: none;
background: rgba(0,0,0,0.2);
max-width: 400px;
}
.todos li{
background: #423a6f;
}
.delete{
cursor: pointer;
}
/* todo text overflowing outside the list */
.todos li span{
word-break: break-all;
}
Deleting Todos
// app.js
const addForm = document.querySelector('.add');
const list = document.querySelector('.todos');
const generateTemplate = todo => {
const html = `
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>${todo}</span>
<i class="far fa-trash-alt delete"></i>
</li>
`;
list.innerHTML += html;
}
addForm.addEventListener('submit', e => {
e.preventDefault();
const todo = addForm.add.value.trim();
// console.log(todo);
if(todo.length){
generateTemplate(todo);
addForm.reset();
}
});
// delete todos
list.addEventListener('click', e => {
if(e.target.classList.contains('delete')){
e.target.parentElement.remove();
}
});
Searching & Filtering Todos
需重複觀看、練習。
// app.js
const addForm = document.querySelector('.add');
const list = document.querySelector('.todos');
const search = document.querySelector('.search input');
const generateTemplate = todo => {
const html = `
<li class="list-group-item d-flex justify-content-between align-items-center">
<span>${todo}</span>
<i class="far fa-trash-alt delete"></i>
</li>
`;
list.innerHTML += html;
}
// add todos
addForm.addEventListener('submit', e => {
e.preventDefault();
const todo = addForm.add.value.trim();
if(todo.length){
generateTemplate(todo);
addForm.reset();
}
});
// delete todos
list.addEventListener('click', e => {
if(e.target.classList.contains('delete')){
e.target.parentElement.remove();
}
});
const filterTodos = (term) => {
Array.from(list.children)
.filter((todo) => !todo.textContent.toLowerCase().includes(term))
.forEach((todo) => todo.classList.add('filtered'));
Array.from(list.children)
.filter((todo) => todo.textContent.toLowerCase().includes(term))
.forEach((todo) => todo.classList.remove('filtered'));
};
// keyup event
search.addEventListener('keyup', () => {
const term = search.value.trim().toLowerCase();
filterTodos(term);
});
// style.css
body{
background: #352f5b;
}
.container{
max-width: 400px;
}
input[type=text],
input[type=text]:focus{
color: #fff;
border: none;
background: rgba(0,0,0,0.2);
max-width: 400px;
}
.todos li{
background: #423a6f;
}
.delete{
cursor: pointer;
}
.filtered{
display: none !important;
}
/* todo text overflowing outside the list */
.todos li span{
word-break: break-all;
}
Dates & Times
Dates & Times in JavaScript
JavaScript Data Types
Object | Arrays, Object Literals, Functions, Dates etc |
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<h1>Dates & Times</h1>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js
// dates & times
const now = new Date();
console.log(now);
console.log(typeof now);
// year, months, day, times
console.log('getFullYear:', now.getFullYear());
console.log('getMonth:', now.getMonth());
console.log('getDate:', now.getDate());
console.log('getDay:', now.getDay());
console.log('getHours:', now.getHours());
console.log('getMinutes:', now.getMinutes());
console.log('getSeconds:', now.getSeconds());
// timestamps
console.log('timestamp:', now.getTime());
// date strings
console.log(now.toDateString());
console.log(now.toTimeString());
console.log(now.toLocaleString());
Timestamps & Comparisons (時間戳記 & 比較)
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<h1>Dates & Times</h1>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js
// timestamps
const before = new Date('February 1 2019 7:30:59');
const now = new Date();
// console.log(now.getTime(), before.getTime());
const diff = now.getTime() - before.getTime();
console.log(diff);
const mins = Math.round(diff / 1000 / 60);
const hours = Math.round(mins / 60);
const days = Math.round(hours / 24);
console.log(mins, hours, days);
console.log(`the blog was written ${days} days ago`);
// converting timestamps into date objects
const timestamp = 1675938474990;
console.log(new Date(timestamp));
Building a Digital Clock
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
<style>
body{
background: #333;
}
.clock{
font-size: 4em;
text-align: center;
margin: 200px auto;
color: yellow;
font-family: arial;
}
.clock span{
padding: 20px;
background: #444;
}
</style>
</head>
<body>
<div class="clock"></div>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js
const clock = document.querySelector('.clock');
const tick = () => {
const now = new Date();
const h = now.getHours();
const m = now.getMinutes();
const s = now.getSeconds();
// console.log(h, m, s);
const html = `
<span>${h}</span> :
<span>${m}</span> :
<span>${s}</span>
`;
clock.innerHTML = html;
};
setInterval(tick, 1000);
Date-fns Library
資源
Date-fns Library 文件使用
- isAfter
- isToday
- format
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<div class="clock"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/date-fns/1.9.0/date_fns.min.js"></script>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js
const now = new Date();
// console.log(dateFns.isToday(now));
// formatting options
console.log(dateFns.format(now, 'YYYY'));
console.log(dateFns.format(now, 'MMMM'));
console.log(dateFns.format(now, 'MMM'));
console.log(dateFns.format(now, 'dddd'));
console.log(dateFns.format(now, 'Do'));
console.log(dateFns.format(now, 'dddd, Do, MMMM, YYYY'));
console.log(dateFns.format(now, 'dddd Do MMMM YYYY'));
// comparing dates
const before = new Date('February 1 2019 12:00:00');
console.log(dateFns.distanceInWords(now, before, {addSuffix: true}));
Async JavaScript (非同步)
這個章節重要、比較困難,需要重複觀看、練習。
What is Asynchronous JavaScript?
Async JavaScript
- Governs how we perform tasks which take some time to complete
(e.g. Getting data from a database)
Start something now and finish it later
Synchronous JavaScript (同步)
- JavaScript can run ONE statement at a time
console.log('line one');
console.log('line two');
console.log('line three');
Single Threaded
Async to the Rescue…
Start something now & finish it later
Async Code in Action
// index.html
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="style.css">
<title>Modern JavaScript</title>
</head>
<body>
<script src="sandbox.js"></script>
</body>
</html>
// sandbox.js
console.log(1);
console.log(2);
setTimeout(() => {
console.log('callback function fired');
}, 2000);
console.log(3);
console.log(4);
// Google Console
1
2
3
4
callback function fired
>
What are HTTP Requests?
HTTP Requests
- Make HTTP requests to get data from another server
- We make these requests to API endpoints
API Endpoints
- Example API endpoint:
http://www.musicapi.com/artist/moby
JSONPlaceholder
Google Network
- Name – 1
- Headers
- Response
Making HTTP Requests (XHR)
// sandbox.js
const request = new XMLHttpRequest();
request.addEventListener('readystatechange', () => {
// console.log(request, request.readyState);
if(request.readyState === 4){
console.log(request.responseText);
}
});
request.open('GET', 'https://jsonplaceholder.typicode.com/todos/');
request.send();
MDN Web Docs
Response Status
- 404
- 200
// sandbox.js
const request = new XMLHttpRequest();
request.addEventListener('readystatechange', () => {
// console.log(request, request.readyState);
if(request.readyState === 4 && request.status === 200){
console.log(request, request.responseText);
} else if(request.readyState === 4){
console.log('could not fetch the data');
}
});
request.open('GET', 'https://jsonplaceholder.typicode.com/todoss/');
request.send();
Callback Functions (回呼函式)
// sandbox.js
const getTodos = (callback) => {
const request = new XMLHttpRequest();
request.addEventListener('readystatechange', () => {
// console.log(request, request.readyState);
if(request.readyState === 4 && request.status === 200){
// console.log(request, request.responseText);
callback(undefined, request.responseText);
} else if(request.readyState === 4){
// console.log('could not fetch the data');
callback('could not fetch data', undefined);
}
});
request.open('GET', 'https://jsonplaceholder.typicode.com/todos/');
request.send();
};
console.log(1);
console.log(2);
getTodos((err, data) => {
console.log('callback fired');
// console.log(err, data);
if(err){
console.log(err);
} else {
console.log(data);
}
});
console.log(3);
console.log(4);
JSON Data
// sandbox.js
const getTodos = (callback) => {
const request = new XMLHttpRequest();
request.addEventListener('readystatechange', () => {
if(request.readyState === 4 && request.status === 200){
const data = JSON.parse(request.responseText);
callback(undefined, data);
} else if(request.readyState === 4){
callback('could not fetch data', undefined);
}
});
request.open('GET', 'todos.json');
request.send();
};
getTodos((err, data) => {
console.log('callback fired');
if(err){
console.log(err);
} else {
console.log(data);
}
});
// todos.json
[
{ "text": "play mariokart", "author": "Shaun" },
{ "text": "buy some bread", "author": "Mario" },
{ "text": "do the plumming", "author": "Luigi" }
]
Callback Hell
// sandbox.js
const getTodos = (resource, callback) => {
const request = new XMLHttpRequest();
request.addEventListener('readystatechange', () => {
if(request.readyState === 4 && request.status === 200){
const data = JSON.parse(request.responseText);
callback(undefined, data);
} else if(request.readyState === 4){
callback('could not fetch data', undefined);
}
});
request.open('GET', resource);
request.send();
};
getTodos('todos/luigi.json', (err, data) => {
console.log(data);
getTodos('todos/mario.json', (err, data) => {
console.log(data);
getTodos('todos/shaun.json', (err, data) => {
console.log(data);
})
})
});
// todos/luigi.json
[
{ "text": "do the plumming", "author": "Luigi" },
{ "text": "avoid mario", "author": "Luigi" },
{ "text": "go kart racing", "author": "Luigi" }
]
// todos/mario.json
[
{ "text": "make fun of luigi", "author": "Mario" },
{ "text": "rescue peach (again)", "author": "Mario" },
{ "text": "go kart racing", "author": "Mario" }
]
// todos/shaun.json
[
{ "text": "play mariokart", "author": "Shaun" },
{ "text": "buy some bread", "author": "Shaun" },
{ "text": "take a nap", "author": "Shaun" }
]
Promise Basics
// sandbox.js
const getTodos = (resource) => {
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.addEventListener('readystatechange', () => {
if(request.readyState === 4 && request.status === 200){
const data = JSON.parse(request.responseText);
resolve(data);
} else if(request.readyState === 4){
reject('error getting resource');
}
});
request.open('GET', resource);
request.send();
});
};
getTodos('todos/luigi.json').then(data => {
console.log('promise resolved:', data);
}).catch(err => {
console.log('promise rejected:', err);
});
// promise example
// const getSomething = () => {
// return new Promise((resolve, reject) => {
// // fetch something
// resolve('some data');
// // reject('some error');
// });
// };
// getSomething().then((data) => {
// console.log(data);
// }, (err) => {
// console.log(err);
// });
// getSomething().then(data => {
// console.log(data);
// }).catch(err => {
// console.log(err);
// });
Chaining Promises
// sandbox.js
const getTodos = (resource) => {
return new Promise((resolve, reject) => {
const request = new XMLHttpRequest();
request.addEventListener('readystatechange', () => {
if(request.readyState === 4 && request.status === 200){
const data = JSON.parse(request.responseText);
resolve(data);
} else if(request.readyState === 4){
reject('error getting resource');
}
});
request.open('GET', resource);
request.send();
});
};
getTodos('todos/luigi.json').then(data => {
console.log('promise 1 resolved:', data);
return getTodos('todos/mario.json');
}).then(data => {
console.log('promise 2 resolved:', data);
return getTodos('todos/shauns.json');
}).then(data => {
console.log('promise 3 resolved:', data);
}).catch(err => {
console.log('promise rejected:', err);
});
The Fetch API
// sandbox.js
// fetch api
fetch('todos/luigi.json').then((response) => {
console.log('resolved', response);
return response.json();
}).then(data => {
console.log(data);
}).catch((err) => {
console.log('rejected', err);
});
Async & Await
// sandbox.js
// async & await
const getTodos = async () => {
const response = await fetch('todos/luigi.json');
const data = await response.json();
return data;
};
console.log(1);
console.log(2);
getTodos()
.then(data => console.log('resolved:', data));
console.log(3);
console.log(4);
// fetch('todos/luigi.json').then((response) => {
// console.log('resolved', response);
// return response.json();
// }).then(data => {
// console.log(data);
// }).catch((err) => {
// console.log('rejected', err);
// });
Throwing & Catching Errors
// sandbox.js
// async & await
const getTodos = async () => {
const response = await fetch('todos/luigis.json');
if(response.status !== 200){
throw new Error('cannot fetch the data');
}
const data = await response.json();
return data;
};
getTodos()
.then(data => console.log('resolved:', data))
.catch(err => console.log('rejected:', err.message));