JavaScript(二)
===
> 2023/10/23-
# JS Runtime執行環境
* 瀏覽器
* Node
* Deno
* 能夠正常執行
# DOM (Document Object Model)
>HTML、XML 和 SVG 文件的程式介面。它提供了一個文件(樹)的結構化表示法,並定義讓程式可以存取並改變文件架構、風格和內容的方法。
>JavaScript透過方法取得DOM,進而操作HTML
>瀏覽器讀完HTML後,建立一個樹狀的DOM結構

# window ->整塊視窗 ; document -> 網頁
* 
# getElementByClassName
## HTML Collection
元素集合體
## Node List
一堆Node的集合,更多好用的方法
## 有哪些爪子可以抓
### getElement系列
拿到html Collection
```javascript=
document.getElementById()
document.getElementsByClassName() //s要寫
```
### querySelector系列
拿到[NodeList] 換行也是一個節點
可以用forEach
```javascript=
//只能拿到一個:
document.querySelector("#id") or (".class") //id一定要寫#
//能拿到很多個:
document.querySelectorAll("#id") or (".class")
```
### 當querySelector相同ID
只能拿到第一個
# 實作
## 初始作業
參考:[PJCHENder](https://pjchender.dev/javascript/js-async-defer/)
1. 把script放在</body>前面
```htmlembedded=
<script src="app.js"></script>
```
2. 加監聽器(就不用把script放在</body>前面)
>EventListener 會把東西一直加上去
```javascript=
document.addEventListener("DOMContentLoaded", () => {
const h1 = document.getElementById("hi");//DOM元素
console.log(hi); //加監聽器,<script>放哪都沒差
});
```
>整理版本
```javascript=
const green = document.querySelector("#green");
const blue = document.querySelector("#blue");
function buleChangeColor(e) {
console.log("blue");
}
function greenChangeColor(e) {
console.log("green");
}
blue.addEventListener("click", buleChangeColor);
green.addEventListener("click", greenChangeColor);
```
3. 加上defer(延遲),等到頁面解析完再做
```javascript=
<script src="app.js" defer></script>
```
4. async
下載JS時不會暫停,下載後暫停解析並直接執行
若放在中間,拿不到後面的東西
```javascript=
<script src="demo1.js" async></script>
<script src="demo2.js" async></script>
```
# 事件
User點擊、按按鈕時,事件會產生
### 把Class內的東西印出來
* 加監聽器<script>放哪都沒差
```javascript=
//getElememts =>只能用for
document.addEventListener("DOMContentLoaded", () => {
const items = document.getElementsByClassName("cat");
for (let i = 0; i < items.length; i++) {
console.log(items[i].textContent);
}
});
```
```javascript=
//querySelector => 可以用forEach
document.addEventListener("DOMContentLoaded", () => {
var items = document.querySelectorAll(".cat");
items.forEach((t) => {
console.log(t.textContent);
});
//只要像陣列,就把他當陣列看
//加監聽器,<script>放哪都沒差
});
```
## 按按鈕,把數字123改456
```javascript=
const btn = document.querySelector("#btn");
btn.addEventListener("click", () => {
const num = document.querySelector("#num");
if (num.textContent === "123") {
num.textContent = "456";
} else {
num.textContent = "123";
}
});
```
### classList
css抓取
```javascript=
btn.classList
```
內含的
```javascript=
classList.add(className) //將一個類別添加到元素的類別列表中。
classList.remove(className) //從元素的類別列表中刪除一個類別。
classList.toggle(className) //切換一個類別的存在。如果該類別已存在,則刪除它;如果不存在,則添加它。
classList.contains(className) //檢查元素是否具有特定的類別。它返回一個布爾值。
```
```javascript=
classList.item(index) //檢索類別列表中特定索引處的類別。
var className = element.classList.item(0); // 獲取列表中的第一個類別。
```
```javascript=
classList.length - 返回類別列表中的類別數量。
var numberOfClasses = element.classList.length;
```
### Event Flow
預設值為flase 若改為true監聽器會改裝在capture階段,出來的順序就會按照capture,預設是flase,出來加在冒泡往上的階段
```javascript=
blue.addEventListener("click", buleChangeColor); //, true);
```

### e.target
這個事件流 下去再上來 最底部的
```javascript=
console.log(e.target);
```
### 1026 實作兩個div
```javascript=
const green = document.querySelector("#green");
const blue = document.querySelector("#blue");
function buleChangeColor(e) {
console.log("blue");
console.log(e.currentTarget);
console.log("-------------");
}
function greenChangeColor(e) {
console.log("green");
console.log(e.target); //這個事件流 下去再上來 最底部的
console.log(e.currentTarget); //事件當時註冊在誰身上
console.log("-------------");
}
//監聽器整理版本
blue.addEventListener("click", buleChangeColor); //, true); //用true 監聽器會改裝在capture階段,出來的順序就會按照capture,預設是flase,出來加在冒泡往上的階段
green.addEventListener("click", greenChangeColor);
//監聽器一般寫法
// blue.addEventListener("click", () => {
// console.log("blue");
// });
// green.addEventListener("click", () => {
// console.log("green");
// });
```
### creatElement
可以建立所有HTML標籤
剛建立時不會被我看到,如果想要被看到必須要加到某元件上
### 建立一個 H1 元素
```javascript=
const h = document.createElement("h1");
h.textContent = "hi";
```
### 再建立一個 div 元素
```javascript=
const s = document.createElement("div");
s.textContent = "hi I am div";
```
### 把元素加到hello裡,並放在最後面
加入到某個元件裡,所以才會出現
```javascript=
hello.appendChild(h);
h.appendChild(s);
```
## 刪除DOM
### 方法一:removeChild
```javascript=
const btn = document.querySelector(".removeBtn");
btn.addEventListener("click", () => {
const lastOne = document.querySelector("li:last-child");
if (lastOne) {
const u = document.querySelector("ul");
u.removeChild(lastOne);
}
});
```
### 方法二:抓出lastOne
```javascript=
const btn = document.querySelector(".removeBtn");
btn.addEventListener("click", () => {
const lastOne = document.querySelector("li:last-child");
if (lastOne) {
lastOne.remove();
}
});
```
### 補充:
```javascript=
const clickHandler = (e) =>{
console.log("clicked");
}
btn.addEventListener("click",hello)
btn.removeEventListener("click",hello) // -> 不會重複做
btn.removeEventListener("click",()=>{}) //->會重複做,因為有不一樣的function
btn.removeEventListener("click",()=>{}) //->會重複做,因為有不一樣的function
```
### Remove()
```javascript=
const btn = document.querySelector(".Btn");
btn.addEventListener("click",function(e){
console.log(e.target); //不管今天按到誰,按到就可以指向target,他會告訴我們哪個備案到了
console.log(e);
})
const btn1 = document.querySelector(".Btn1");
const btn2 = document.querySelector(".Btn2")
const btn3 = document.querySelector(".Btn3")
btn1.addEventListener("click",function(e){
btn1.remove() //指定的東西消失
})
btn1.addEventListener("click",function(e){
e.target.remove() // 按到哪個 哪個就消失,可以把一系列的btn放進去,然後把這些btn都用同一樣的class名稱,用querySelectAll
})
btn1.addEventListener("click",function(e){
btn1.remove() //消失
})
```
## 抓到上層
```javascript=
const lastOne = document.querySelector("li:last-child");
```
## parentElement取得上層Element
```javascript=
console.log(lastOne.parentElement);
```
## parentNode取得Node
```javascript=
console.log(lastOne.parentNode);
```
## Node,Element 選哪個
Node有的,Element基本都有
該用哪一種?盡量選Element,操作簡單功能也較多
## childNodes 取得子層DOM(下一層)
```javascript=
const p = document.querySelector("ul");
console.log(p.childNodes);
NodeList(9) [text, li, text, li, text, li, text, li, text]
```
## children 取得子層DOM(下一層)
```javascript=
const p = document.querySelector("ul");
console.log(p.children);
HTMLCollection(4) [li, li, li, li]
```
## previousElementSibling拿到兄弟姊妹層
```javascript=
const lastOne = document.querySelector("li:nth-child(2)"); //抓第li第二個
```
## element系列
```javascript=
console.log(lastOne.previousElementSibling); //拿上一個
console.log(lastOne.nextElementSibling); //拿下一個
```
## node系列
```javascript=
console.log(lastOne.previousSibling); //拿上一個
console.log(lastOne.nextSibling); //拿下一個
```
## 在指定位置安插DOM
前置作業
```javascript=
const ul = document.querySelector("ul");
const li = document.createElement("li");
li.textContent = "Xxxxxxx";
```
### beforebegin 放在最上面
```javascript=
ul.insertAdjacentElement("beforebegin", li);
```
### afterbegin 放在裡面的最上面
```javascript=
ul.insertAdjacentElement("afterbegin", li);
```
### beforeend 放在裡面的最下面
```javascript=
ul.insertAdjacentElement("beforeend", li);
```
### afterend 放在最尾巴
```javascript=
ul.insertAdjacentElement("afterend", li);
```
### insertAdjacentHTML 安置DOM方便的做法
```javascript=
const ul = document.querySelector("ul");
const li = "<li>zzzz</li>";
ul.insertAdjacentHTML("beforebegin", li);
```
## 語法
### textContent
```javascript=
h1.textContent = 4567890 //更改內容
```
# ES6語法
## 箭頭函數不是function的簡寫
## 物件簡寫
key和變數同名,可以寫成如下
```javascript=
let name = "kk";
let age = 18;
let cat = {
age,
};
console.log(cat); //{ age: 18 }
```
# 解構
## 寫法
```javascript=
const cat = {
name: "kk",
age: 18,
};
const { name, age } = cat;
console.log(name);
console.log(age); // kk 18
```
## 解構放在function裡
```javascript=
function printUser(userData) {
const { name, age } = userData;
console.log(name);
console.log(age);
}
const user = {
name: "kk",
age: 18,
};
printUser(user); // kk 18
```
## 放在參數裡
```javascript=
function printUser({ name, age }) {
console.log(name);
console.log(age);
}
const user = {
name: "kk",
age: 18,
};
printUser(user);
```
## ...功能
放在陣列前等於要把他展開的意思
```javascript=
const heroes = ["悟空", "魯夫", "娜美"];
const marvelHeroes = ["鋼鐵人", "索隆", "奇異博士"];
// const allHeroes = heroes.concat(marvelHeroes);
const allHeroes = [...heroes, ...marvelHeroes];
console.log(allHeroes);
```
...+解構功能 剩下我全收了
```javascript=
const heroes = ["悟空", "魯夫", "娜美"];
let [h1, ...h2] = heroes;
console.log(h1, h2);//悟空 [ '魯夫', '娜美' ]
```
解構2
```javascript=
const heroes = ["悟空", "魯夫", "娜美"];
let [h1, ...h2] = heroes;
console.log(h1, h2); //悟空 [ '魯夫', '娜美' ]
```
放在function內的參數
```javascript=
function sayHello(user, ...others) {
console.log(user);
console.log(others);
}
sayHello("悟空", "魯夫", "娜美", "琦玉");
//悟空
//[ '魯夫', '娜美', '琦玉' ]
```
## 備註
### 轉變二進位
```javascript=
(11).toString(2)
//"1011"
```
### 顏色

### chmod
參考wiki:[chmod](https://zh.wikipedia.org/wiki/Chmod)

## 回傳flase
前面成功,後面再往下做,
前面失敗,後面就不用做,可以調整執行順序
## 真值表
| AND | T | F |
| --- | --- | --- |
| T | T | F |
| F | F | F |
| OR | T | F |
| --- | --- | --- |
| T | T | T |
| F | T | F |
| XOR | T | F |
| --- | --- | --- |
| T | F | T |
| F | T | F |
# API
* Application Programming Interface
* 交換或獲取資料的網址
* [{JSON} Placeholder](https://jsonplaceholder.typicode.com/)
* JSON 格式(JavaScript Object Notation)
* fetch為非同步行為
* [HTTP狀態](https://developer.mozilla.org/zh-TW/docs/Web/HTTP/Status)
* 抓取的語法:
```javascript=
const url = "https://jsonplaceholder.typicode.com/users"; //->Promise
const result = fetch(url);
result
.then((res) => res.json()) //目的->Obj,回傳Promise
//callback function
.then((r) => console.log(r)); // r = Obj
```
* 網路上拿到的東西都是文字
* 印出抓取的文字:
```javascript=
const url = "https://jsonplaceholder.typicode.com/users"; //->Promise
const result = fetch(url);
result
.then((res) => res.json())
.then((users) => {
users.forEach(({ name }) => {
console.log(name);
});
});
```
* 若有錯誤,使用catch,印出錯誤訊息
```javascript=
const url = "https://jsonplaceholder.typicode.com/users"; //->Promise then,catch
fetch(url)
.then((res) => res.json())
.then((users) => {
console.log(users);
// users.forEach(({ name }) => {
// console.log(name);
// });
})
.catch((err) => {
console.log("------------");
console.log(err);
console.log("------------");
});
```
# 非同步方法 async, await
>2023/11/02
```javascript=
//前面要用async表示非同步function
async function getUsers() {
try {
const res = await fetch(url);
//原本會抓東西,但還沒保證一定回來,所以要加await
const users = await res.json();
users.forEach(({ name }) => console.log(name));
console.log(users);
} catch {
console.log("err");
}
}
```
# CORS
>跨來源資源共用(Cross-Origin Resource Sharing,簡稱CORS)
當利用JavaScript抓取別人資料時可能發生狀況,CORS主要擋瀏覽器內的JS
錯誤訊息如下:
```javascript
has been blocked by CORS policy
```
## 如何解決CORS?
* 和對方說請對方開
* 自己寫一個API網站,API網站透過後端去抓資料,再抓自己寫的API
雖然這樣比較麻煩,但因為CORS只擋瀏覽器裡的JS,如果是用Node js,Ruby,Python等其他程式語言,透過後端去抓資料的話,基本上不會被CORS擋下
# JQuery
## 如何抓東西?
```javascript=
$("#id") or $(".id")
```
## 替換文字
```javascript=
$("#id").html("hello")
```
## 加監聽器
```javascript=
$().ready(() => {
const hero = $("#hero");
hero.html("hi");
console.log(hero);
});
```
## Stack 堆疊
- FIFO

來源:[PJchender—[JS] 理解 JavaScript 中的事件循環、堆疊、佇列和併發模式(Learn event loop, stack, queue, and concurrency mode of JavaScript in depth)](https://pjchender.dev/javascript/js-event-loop-stack-queue/)
- 只要還沒做完,呼叫其他函數,就會長一個泡泡,泡泡會先壓著
- 為什麼是印出 a, c, b ?
- 以結構來說,最先放進去的會最後出來
```javascript=
console.log("a");
setInterval(() => {
console.log("b");
}, 1000);
console.log("c");
```
## Recursive 遞迴
### 費波納契約數列
- 因為有結束的條件,所以不會造成無窮迴圈
- 效率其實不好
```javascript=
//1,1,2,3,5
function fib(n) {
if (n < 2) {
return n;
}
return fib(n - 1) + fib(n - 2);
}
```
## Quequ
- FIFO 先進先出法
- 非同步的東西完成後會排進去Quequ
# Event Loop

- 所有function執行時都要通過stack
- [可以玩看看!loupe](http://latentflip.com/loupe/)
## 所有的物件都有__proto__屬性
搜尋方法的步驟:
```javascript=
1. 先找自己找不到,去問問看別人
2. h1.eat -> h1.eat.__proto__ -> h1.eat.__proto__.__proto__
3. h1.__proto__.__proto__ -> null
4. 確定都沒有 -> undefined
```
## jQuery抓網路資料
```javascript=
const url = "https://jsonplaceholder.typicode.com/posts";
$.ajax({ url }).done((posts) => {
posts.forEach((post) => {
console.log(post.title);
});
});
```
## 標準版本
```javascript=
document.querySelector(".myList").innerHTML = "你好";
```
## jQuery版本
```javascript=
$(".myList").html("你好");
```
## 效能最好、跨瀏覽器的框架
- Vanilla.js
- 什麼都沒有XD,最好的就是把JS學好!
# Promise
- Promise => .then, .catch, .finally, .finally
- .finally() 不管成功或失敗一定會繼續做,最後要關檔、關連DM連線