Web 0x2

JavaScript 的奇妙冒險

講者:YuKAi

Outline

  • JavaScript Intro.
  • JavaScript Syntax
  • JavaScript & Web Interact
  • Local Storage、Cookie、Session

今天目標:寫出一個 To Do List

JavaScript Intro.

什麼是 JavaScript

  • 讓你的網頁可以有互動
  • 直譯式程式語言
  • 弱型別,變數型別可以任意轉換
  • 搭配 Node.jsDenoBun 可以寫後端
  • 不是 Java

將 HTML 與 JavaScript 結合

方法一:在 HTML 裡使用 <script>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
<script>
    alert("Hello World")
</script>
</html>

將 HTML 與 JavaScript 結合

方法二:引入外部 JavaScipt 檔案

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
    
</body>
<script src="main.js"></script>
</html>
alert(123)

index.html

main.js

DevTools Console 使用 JavaScript

JavaScript Syntax

如何打印

console.log

如何打印

console.log(100);     // 100
console.log("Hello"); // Hello
console.log(1 + 1);   // 2
  • 用 console.log 打印
  • 可以打印任何型別
  • 也能進行簡單的 JS 運算

變數宣告

var、let、const

變數宣告 - var

var n1 = 10;
console.log(n1); // 10

var n1 = 20; // 可以重複宣告
console.log(n1); // 20

n2 = 30; // 若沒有任何宣告,會自動採用 var 宣告
console.log(n2); // 30

變數宣告 - var

function f1(n){
    var n1 = 10;
}
console.log(n1); // 找不到 n1

若在外面呼叫函數內的變數會找不到

變數宣告 - let

let n1 = 10;
console.log(n1); // 10

{
    // 在區塊內宣告
    let n2 = 20;
    console.log(n2); // 20
}
console.log(n2); // 抓不到 n2 因為 n2 在區塊內
  • 不能重複宣告
  • 作用範圍為區塊 { }

變數宣告 - const

const n1 = 10;
console.log(n1); // 10

n1 = 20; // 不能修改 n1 的值
  • 不能重複宣告
  • 宣告後不能改變值
  • 作用範圍為區塊 { }

變數型別

什麼是弱型別

let a = 100;
let b = '1';
console.log(a + b); 
// 1001
a = 0
b = "2"
print(a + b)
# TypeError: unsupported operand type(s) for +: 'int' and 'str'

JavaScript

Python

弱型別超怪的地方

let x = (![] + [])[+[]] +
(![] + [])[+!+[]] +
([![]] + [][[]])[+!+[] + [+[]]] +
(![] + [])[!+[] + !+[]];

console.log(x) // fail

嗯 沒錯它可以被執行,有夠酷

怎麼知道是什麼型別

let n1 = 100;                       
console.log(typeof(10));            // Number
console.log(typeof("HackerSir"));   // String
console.log(typeof(n1));            // Number
console.log(typeof({}));            // Object
console.log(typeof([]));            // Object

用 typeof( ) 可以查看型別

全部的數字都是 Number 型別,不管小數點、正負數

let n1 = 10;
let n2 = 12.5;
let n3 = Infinity;       // 無限,當然也有負無限
let n4 = NaN;            // Not a number
console.log(typeof(n1)); // number
console.log(typeof(n2)); // number
console.log(typeof(n3)); // number
console.log(typeof(n4)); // number
  • 字串
  • 不分單引號、雙引號
let s1 = "Hello World";      // 用雙引號
let s2 = 'How do you do?';   // 用單引號
let s3 = "Let's go";         // 通常會雙引號在外,單引號在內
let s4 = s1 + " | "+ s2;     // 用 + 連接 string
console.log(s1); // Hello World
console.log(s2); // How do you do?
console.log(s3); // Let's go
console.log(s4); // Hello World | How do you do?

可以用 ` 搭配 ${ } 來加入變數

let who = "JavaScript";
let id = 100;
let s = `Test ${id}:Hello! ${who} !`;
console.log(s); // Test 100:Hello!! JavaScript!
console.log(typeof(true));  // boolean
console.log(typeof(false)); // boolean 
  • null:代表這個變數曾經有值,只是現在沒有
  • undefined:代表這個變數還沒有被賦值,也是預設值
let n1 = null;
let n2 = undefined;
let n3;
console.log(n1, n2, n3); // null undefined undefined
console.log(typeof(n1), typeof(n2), typeof(n3)); // object undefined undefined
let arr1 = [1, 2, 3];
console.log(arr1); // [1, 2, 3]

arr1[0] = 100;
console.log(arr1); // [100, 2, 3]

[ ] 宣告

常用的 Array 方法

let arr1 = [1, 2, 3];
arr1.push(100);
console.log(arr1); // [1, 2. 3, 100]
arr1.pop();
console.log(arr1); // [1, 2, 3]
  • push(要加入的值):於最後新增
  • pop():於最後刪除

常用的 Array 方法

let arr1 = [1, 2, 3, 4, 5];
console.log(arr1.includes(1));   // true
console.log(arr1.includes(100)); // false
  • includes(要尋找的值, 開始尋找的點):查找有沒有這個值

常用的 Array 方法

let arr1 = [1, 2, 3, 4, 5];
let arr2 = arr1.slice(1, 3);
console.log(arr2); // [2, 3]
  • slice(開始切割點, 結束切割點):切割陣列

let obj = {
    "name": "YuKai",
    "role": "student",
    "prpperty": "1000"
}

console.log(obj["name"]);  // YuKai
console.log(obj.prpperty); // 1000
  • { } 宣告物件
  • [ ]. 來獲得值

Object

let obj = {
    "name": "YuKai",
}

// 新增
obj["feeling"] = "happy?";
obj.id = "D1111111";
console.log(obj); // { name: 'YuKai', feeling: 'happy?', id: 'D1111111' }

// 刪除
delete obj.id;
console.log(obj); // { name: 'YuKai', feeling: 'happy?' }

新增、刪除

條件判斷 & 迴圈

if、for、while

條件判斷

let n1 = 10;
if(n1 > 20){
    console.log("Yes");
}else{
    console.log("No", n1);
}

// Output:
// No 10
  • 用 if -> (else if) -> else 來做條件判斷
  • 用 && 和 || 來表達條件關係
let n1 = 20;
if(n1 % 2 == 0 && n1 > 10){
    console.log("Yes");
}else{
    console.log("No", n1);
}

// Output:
// Yes

迴圈循環 - for Loop

for(let i = 0; i < 10; i++){
    console.log(i); // 0 1 2 3 4 5 6 7 8 9
}

注意變數宣告要用 let

迴圈循環 - while Loop

let i = 0;
while(i < 10){
    console.log(i); // 0 1 2 3 4 5 6 7 8 9
    i++;
}

函式宣告

function、( ) => { }

函式宣告

// 一般函式宣告
function add(n1, n2) {
    console.log(n1 + n2);
}
add(10, 20); // 30

// 匿名函式宣告
const subtract = function(n1, n2) {
    console.log(n1 - n2);
}
subtract(10, 20); // -10
  • 用 function 來定義一個函式

函式宣告 - Arrow Function

// 匿名函式宣告
const subtract = function(n1, n2){
    console.log(n1 - n2);
}
subtract(10, 20); // -10

// Arrow Function
const subtract2 = (n1, n2) => {
    console.log(n1 - n2);
}
subtract2(10, 20); // -10

在 ES6 版本中,將剛才的匿名函式再簡化

稱為 Arrow Function

JavaScript 網頁互動

1

按下按鈕有反應

2

新增 To Do 項目

3

完成樣式

第一步:按下按鈕有反應

抓取 HTML Element

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <link rel="stylesheet" href="style.css" />
    <title>Todo List</title>
  </head>
  <body>
    <h1>MY TODO LIST</h1>
    <div id="form">
        <input type="text" class="input" id="input" placeholder="Enter your todo">
        <button id="submit">ADD</button>
    </div>
    <ul class="todos" id="todos"></ul>

    <script src="script.js"></script>
  </body>
</html>

找到我要用的 Button

抓取 HTML Element

const button = document.getElementById('submit');

document.getElementById 語法

透過 Id 來定位 Element

抓取 HTML Element

其他的定位方式:

document.getElementByClassName();  // 用 class 定位
document.getElementByTagName();    // 用 HTML 的 Tag 定位

// 定位多個元素
document.getElementsById();

// 較新的定位方法
document.querySelector();
document.querySelectorAll();

事件監聽

// button 事件監聽
button.addEventListener("click", click);

// 定義按下按鈕後要呼叫的函式
function click(){
    console.log("按鈕被點擊");
}

addEventListener 語法來監聽按鈕是否有被按下

被按下 => 執行後面的 function

此時這個 function 被稱為 callback function

事件名稱 說明
click 點擊
contextmenu 右鍵點擊
dblclick 雙鍵點擊
mouseenter 移動到某個 element 上
mouseleave 移出某個 element 外
keydown 當有按鍵被按下
keypress 當有按鍵被按下並鬆開
keyup 當有按鍵被鬆開

成功畫面

嘗試把 input 的內容打印出來

Lab 1

Hint.

先把 input 這個 Element 抓出來打印,

看看哪個值是有變化的

// 抓取 HTML Element
const input = document.getElementById('input');
const button = document.getElementById('submit');

// button 事件監聽
button.addEventListener("click", () => {
    console.log(input.value);
})

Ans

第二步:新增 To Do 內容

建立一個 HTML Element

const todoElement = document.createElement('li');
todoElement.innerText = input.value;

document.createElement() 用來生成一個 HTML Element

將輸入框的內容加入到這個新 Element 的內容裡面

將新的 Element 加入 HTML

const todo = document.getElementById('todos');
todo.appendChild(todoElement);

appendChild() 是指在 todo 這個 Element 裡面

加上一個 Element

記得清空輸入框

input.value = "";

成功畫面

完整程式碼

// 抓取 HTML Element
const input = document.getElementById('input');
const button = document.getElementById('submit');
const todo = document.getElementById('todos');

// button 事件監聽
button.addEventListener("click", () => {
    const todoElement = document.createElement('li');
    todoElement.innerText = input.value;
    todo.appendChild(todoElement);
    input.value = "";
})

如果有重複內容或是空白,

要怎麼預防加入到 To Do List 項目

Lab 2

Hint.

條件判斷+陣列

Ans

// 抓取 HTML Element
const input = document.getElementById('input');
const button = document.getElementById('submit');
const todo = document.getElementById('todos');

// button 事件監聽
const todoList = [];
button.addEventListener("click", () => {
    if(input.value && !(todoList.includes(input.value))){
        const todoElement = document.createElement('li');
        todoList.push(input.value);
        todoElement.innerText = input.value;
        todo.appendChild(todoElement);
        input.value = "";
    }else{
        alert("不能為重複事件或空白");
    }
})

第三步:完成樣式

先定義一個完成的樣式

.todos li.completed {
  color: #b6b6b6;
  text-decoration: line-through;
}

接下來就是把這個樣式套到要變化的 To Do 項目

將樣式加入 ClassList

button.addEventListener("click", () => {
    if(input.value && !(todoList.includes(input.value))){
        const todoElement = document.createElement('li')
        todoElement.innerText = input.value
        todo.appendChild(todoElement)
        input.value = ""

        function completed(){
          todoElement.classList.add('completed')
        }
    }else{
        alert("不能為重複事件或空白");
    }
})

classList.add() 將 class name 加入 classList

相反的, classList.remove() 能移除 class name 

對 To Do 的項目事件監聽

button.addEventListener("click", () => {
    if(input.value && !(todoList.includes(input.value))){
        const todoElement = document.createElement('li');
        todoList.push(input.value);
        todoElement.innerText = input.value;
        todoElement.addEventListener("click", completed);
        todo.appendChild(todoElement);
        input.value = "";

        function completed(){
          todoElement.classList.add('completed');
        }
    }else{
        alert("不能為重複事件或空白");
    }
})

成功畫面

當再次點擊被刪除的項目時,可以取消刪除樣式

Lab 3

Hint.

去查 classList,還是你要用條件判斷也可以

Ans

// 抓取 HTML Element
const input = document.getElementById('input');
const button = document.getElementById('submit');
const todo = document.getElementById('todos');

// button 事件監聽
const todoList = [];
button.addEventListener("click", () => {
    if(input.value && !(todoList.includes(input.value))){
        const todoElement = document.createElement('li');
        todoList.push(input.value);
        todoElement.innerText = input.value;
        todoElement.addEventListener("click", () => {
            todoElement.classList.toggle('completed');
        })
        todo.appendChild(todoElement);
        input.value = "";
    }else{
        alert("不能為重複事件或空白");
    }
})

Local Storage

Local Storage

  • 可以將資料存在瀏覽器
  • 當訪問相同網址時,可以將資料撈出來

Local Storage 用法

// 設置一組資料,透過 key 來獲得 value
localStorage.setItem('key', 'value');

/// 獲取 key 的資料
const val = localStorage.getItem('key');

// 刪除 key 的資料
localStorage.removeItem('key');

// 清除所有資料
localStorage.clear();

使用 Local Storage 存儲資料

補充:Session Storage

  • 資料存在瀏覽器
  • 儲存每個分頁的資料

Cookie & Session

Cookie

  • 存儲在用戶端
  • 可以設置過期時間
  • 用於儲存簡易的用戶資訊,如用戶 ID、用戶行為

Session

  • 存儲在伺服器端
  • 可以設置過期時間,取決於伺服器的設定
  • 用於儲存較複雜的用戶資訊,如購物車、登入信息等

於 Header 內設定
Set-Cookie: <cookie-name>=<cookie-value>

伺服器設定客戶端的 Cookie

Cookie: <cookie-name>=<cookie-value>;

客戶端設定 Cookie

創建 Cookie

document.cookie 可以看成是個集合

// Create Cookie
document.cookie = "username=John Doe; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/;";
document.cookie = "role=Student; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/;";
console.log(document.cookie);

修改、刪除 Cookie

  • 要修改就重新給 cookie
  • 要刪除就將時間設過期 
document.cookie = "username=John Doe; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/;";
document.cookie = "role=Student; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/;";
console.log("修改、刪除前:", document.cookie);

// 修改 Username Cookie
document.cookie = "username=ELon Mask; expires=Thu, 18 Dec 2023 12:00:00 UTC; path=/;";
// 刪除 role Cookie
document.cookie = "role=Student; expires=Thu, 18 Dec 2013 12:00:00 UTC; path=/;";
console.log("修改、刪除後:", document.cookie);

Cookie 參數

  • Secure:只在 https 傳送
  • HttpOnly:讓 JavaScript 不能抓取
  • Path:可以被抓取的路徑
  • Expires:過期時間

學完 JavaScript 之後 ?

有好幾條路你可以選擇

  • 點 Back-end 技能
    • 熟悉一個後端語言(Python、JavaScript、Golang)
    • 上手 Framework(Node.js + Express.js、Flask)
    • 學習資料庫(MySQL、MariaDB)

還有很多東西等著你學

儲備幹部

Made with Slides.com