JavaScript(二) === > 2023/10/23- # JS Runtime執行環境 * 瀏覽器 * Node * Deno * 能夠正常執行 # DOM (Document Object Model) >HTML、XML 和 SVG 文件的程式介面。它提供了一個文件(樹)的結構化表示法,並定義讓程式可以存取並改變文件架構、風格和內容的方法。 >JavaScript透過方法取得DOM,進而操作HTML >瀏覽器讀完HTML後,建立一個樹狀的DOM結構 ![](https://hackmd.io/_uploads/SkAWKhCZ6.png=30%x) # window ->整塊視窗 ; document -> 網頁 * ![image alt](https://miro.medium.com/v2/resize:fit:665/1*reaVYnqG4JISjGoLVHnPuA.jpeg) # 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); ``` ![](https://hackmd.io/_uploads/rkHlI2DM6.png) ### 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" ``` ### 顏色 ![](https://hackmd.io/_uploads/BkL48IDzT.png) ### chmod 參考wiki:[chmod](https://zh.wikipedia.org/wiki/Chmod) ![](https://hackmd.io/_uploads/SJhGtLDMa.png) ## 回傳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 ![](https://hackmd.io/_uploads/H1Ipmcx7a.png) 來源:[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 ![image](https://hackmd.io/_uploads/HyFsvqJ4a.png) - 所有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連線