wordpress_blog

This is a dynamic to static website.

Modern JavaScript (2)

(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

資源

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

Single Threaded

Async to the Rescue…

Start something now & finish it later

Async to the rescue

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
API Endpoints

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