🔍 搜尋結果:JavaScript 系列

🔍 搜尋結果:JavaScript 系列

JavaScript 系列六:第1課 ── 認識 data model 與 render function

## 課程目標 認識 data model 的觀念 認識 render function 的觀念 ## 課程內容 如果電商網站上有這樣的內容 ``` <div> 商品名稱:<span id="name">冬季外套</span> 價格:<span id="price">$1,990</span> 分類:<span id="category">女裝</span> 剩餘數量:<span id="remain">5</span> </div> ``` 在網站上的任何操作,菜鳥工程師會覺得就直接去更新 DOM 就好了 在程式還小的時候,這樣開發沒問題 但是當專案變大之後,這樣的開發會遇到問題,程式碼會越來越難維護 這種很難維護的寫法,我稱之為「在各處胡亂更新各處 DOM」的寫法 --- 有經驗的工程師在開發的時候,會習慣將應用程式的「狀態」與程式的其他部份分開來 這個「狀態」我們叫 state 或者 model 或者 data model 實務上,這三種名詞都很常看到,我在文章中也會混雜著交互使用 同樣的電商頁面,資深工程師會覺得看到了以下 data model ``` var product = { name: "冬季外套", price: 1990, category: "women", remain: 5 } ``` 而在開發各種功能的時候,資深工程師會覺得,一律先更新 data model,再接著拿 data model 來呈現出 UI 比較好 這樣在開發複雜應用程式的時候,相關函式一律只要關心 data model 就好,不用管 UI 在思考的時候,腦子的負擔會小很多,因為你變得只要想著應用程式的「狀態」就好 --- 那麽只更新 data model,那何時更新 UI 呢? 這邊介紹一個簡單的方法,叫做 render function 就是放一個 root 元素,作為程式 UI 的容器 接著寫一個 render 函式,來根據 data model,畫出全部 UI 到 root 裡面 這個 render 函式有三個注意事項 - 第一行要先清空 UI - 在所有跟「狀態」有關操作的最後一行,都要呼叫這個函式 - 所有 DOM 操作一律由 render 函式處理(其他全部函式,通通禁止更新 DOM) 請在 jsfiddle 嘗試以下範例 ``` <div id="root"> </div> <button onclick="decrease()">decrese</button> <button onclick="increase()">increase</button> ``` ``` var product = { name: "冬季外套", price: 1990, category: "women", remain: 5 }; render(); function render() { var root = document.querySelector('#root'); root.textContent = ""; var name = document.createElement('div'); name.textContent = '商品名稱:' + product.name; var price = document.createElement('div'); price.textContent = '價格:' + product.price; var category = document.createElement('div'); category.textContent = '分類:' + product.category; var remain = document.createElement('div'); remain.textContent = '剩餘數量:' + product.remain; root.append(name); root.append(price); root.append(category); root.append(remain); } function decrease() { product.remain = product.remain - 1; render(); } function increase() { product.remain = product.remain + 1; render(); } ``` 這樣的寫法,很神奇地,關於 DOM 的操作通通放在 `render` 即可 雖然 `render` 函式變得很多行、很大、寫起來比較麻煩 但是除了 `render` 以外的函式,通通都變簡單了 這是「短期麻煩,長期方便」的一個明顯例子 ## 課後作業 在之前的課程,你開發過一個「待辦事項小工具」,甚至還加上了 local storage 儲存功能 在開發的過程中,我相信你有感覺到,程式碼越來越大團了,新功能雖然寫得出來,但越來越難寫了 回頭看看當初的程式碼,你會發現讀起來不容易,要再維護、擴充功能,也都不太容易 讓我們使用新方法,重新開發一次這個小工具 請使用 https://jsfiddle.net 並且建立一份新的 fiddle --- 這一課,不開發任何功能,先實作把 data model 給 render 出來的效果 請使用以下 html 作為 root 元素 ``` <div id="root"> </div> ``` 然後複製以下 js 使用,將 `render` 函式完成 ``` var todos = [ { title: "倒垃圾" }, { title: "繳電話費" }, { title: "採買本週食材" }, ]; function render() { // 請寫出此函式內容 } render(); ``` 在這段 js 中,`todos` 陣列,就是我們的 data model 最後,在畫面上,應該會出現以下內容 ``` <div id="root"> <ul> <li> <span>倒垃圾</span> </li> <li> <span>繳電話費</span> </li> <li> <span>採買本週食材</span> </li> </ul> </div> ``` 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列五:第7課 ── 學會 AJAX 與 data attribute 的結合

## 課程目標 學會結合 AJAX 與 data attribute 來製作應用程式 ## 課程內容 打造應用程式的時候,有些資料不會直接顯示在畫面上,但之後的其他動作會用到 這時可以把資料存在 data attribute 裡面 ``` <div data-email="don't know yet" data-phone="0987654321"> <span></span> <button onclick="showEmail()">show email</button> <button onclick="showPhone()">show phone</button> </div> <hr> <button onclick="load()">Load User</button> ``` ``` function showEmail() { alert(document.querySelector('div').dataset.email) } function showPhone() { alert(document.querySelector('div').dataset.phone) } function load() { fetch('https://fakestoreapi.com/users/1') .then(res => res.json()) .then(json => { const element = document.querySelector('div'); element.querySelector('span').textContent = json.username; element.dataset.email = json.email; element.dataset.phone = json.phone; }) } ``` 到 jsfiddle 跑跑看,按鈕到處點點看,就知道 data attribute 的用法了 非常簡單,寫 html 的時候,直接設定 `data-*` 屬性就是了 寫 js 的時候,直接存取 DOM 元素的 `dataset` 屬性就是了 如果 AJAX 撈到了大量資料,但畫面上只需先顯示一部分資料 那麼就可以用 data attribute 先把資料整理起來放著 之後要擴充這個應用程式,或者有同事接手維護,要拿資料時,就很方便,也可以避免一直重複發 AJAX 拿同樣的資料 ## 課後作業 接續上一課的作業,這次要改得更漂亮 點擊 Details 按鈕,本來是連續跳出三個 alert 實務上不可能用連續跳出 alert 來說明商品細節,太醜了 這次要拿掉 alert,改成跳出一個 modal 互動視窗元件, 你可以使用之前課程中,自己製作過的 modal 元件 也可以上網找套件,找一款現成的使用 --- 這個 modal 視窗要包含以下資訊 - 名稱 - 分類 - 描述 - 圖片 - 價格 請使用 data attribute 將資料存放在 `<li>` 元素 點擊 Details 按鈕時,再將這些資料撈出來,放進 modal 元件內 --- 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列五:第6課 ── 學會 AJAX 與各種 HTTP 請求方法

## 課程目標 認識 AJAX 與不同的 HTTP 請求方法 ## 課程內容 HTTP 協定中,HTTP Request 有多種不同的方法 前面幾課的寫法,都是 HTTP GET 類型,這一課來接著談談更多不同的請求方法 繼續使用模擬電商網站的範例 API ### 使用 HTTP POST 新增一筆用戶資料 ``` fetch('https://fakestoreapi.com/users', { method: "POST", body: JSON.stringify({ email: '[email protected]', username: 'johnd', password: 'm38rmF$', name: { firstname: 'John', lastname: 'Doe' }, address: { city: 'kilcoole', street: '7835 new road', number: 3, zipcode: '12926-3874', geolocation: { lat: '-37.3159', long: '81.1496' } }, phone: '1-570-236-7033' }) }) .then(res => res.json()) .then(json => console.log(json)) ``` 在這個範例中,`fetch()` 函式的第二個參數是一個物件,把 method 屬性設定好,然後 body 代表 HTTP body 的內容 必須是字串,所以用 `JSON.stringify()` 把物件轉換成 JSON 字串 整段看不太懂沒關係,需要了解 HTTP 協定的細節才比較看得懂,現在就先照做即可 要注意我們是用模擬電商 API,一切都是模擬的 最後主機會回應一個新的用戶 ID,看起來是新增成功了,但實際上並沒有東西新增到資料庫喔~ ### 使用 HTTP PUT 更新一筆用戶資料 ``` fetch('https://fakestoreapi.com/users/7', { method: "PUT", body: JSON.stringify({ email: '[email protected]', username: 'johnd', password: 'm38rmF$', name: { firstname: 'John', lastname: 'Doe' }, address: { city: 'kilcoole', street: '7835 new road', number: 3, zipcode: '12926-3874', geolocation: { lat: '-37.3159', long: '81.1496' } }, phone: '1-570-236-7033' }) }) .then(res => res.json()) .then(json => console.log(json)) ``` 在這個範例中,去更新用戶 ID 為 7 的使用者資料 方法設定為 PUT,body 一樣放整個 JSON 字串 ### 使用 HTTP DELETE 刪除一筆用戶資料 ``` fetch('https://fakestoreapi.com/users/6', { method: "DELETE" }) .then(res => res.json()) .then(json => console.log(json)) ``` 在這個範例中,去刪除用戶 ID 為 6 的使用者資料 方法設定為 DELETE,不需要提供 body --- 實務上,API 設計時,有人偏好這種 GET POST PUT DELETE 都用到的寫法 有人則偏好只使用 GET 與 POST 撈資料一律都用 GET,除此之外,會更新到資料庫內容的動作,通通都用 POST 這屬於主觀偏好,沒有對錯問題,團隊討論後有共識即可 --- 上面的範例,用戶參數都是在網址最後加上 `/{ID}` 這種格式帶入 實務上,GET 參數也可能用 `?id={ID}` 這種格式 而在 POST 或其他類型的請求中,用戶參數也可能直接加在 `body: JSON.stringify({` 裡面的屬性之中 各種做法,都可以,一樣屬於主觀偏好,沒有對錯問題,團隊討論後有共識即可 ## 課後作業 接續上一課的作業,加上刪除按鈕 請翻閱 API 文件說明 https://fakestoreapi.com/docs 找出「刪除商品」的 API 把每個商品的 html 改成 ``` <li> <span>xxx</span> <button>Details</button> <button>Delete</button> </li> ``` 點擊 Delete 按鈕,就發送 API 出去 - 主機回應成功的話,就把整個 `<li>` 元素刪掉 - 主機回應失敗的話,就跳 alert 提醒用戶稍後再試 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列五:第5課 ── 學會 AJAX 錯誤處理

## 課程目標 認識 AJAX 錯誤處理 ## 課程內容 在前一課,我們說過以下的話 > 有些任務現在還不會立刻執行,但我先把要執行的任務交待清楚,時間點到的時候,就執行 > 以 UI 動作來說,時間點就是 `onclick` 之時、`onchange` 之時 > 以 AJAX 動作來說,時間點就是 `拿到主機回應` 之時 實際上,AJAX 的時間點,除了 `拿到主機回應` 之時,還有一個,就是 `發現主機回應失敗` 之時 把原本的範例 ``` fetch('https://fakestoreapi.com/users/1') .then(res=>res.json()) .then(json=>console.log(json)) ``` 故意打錯字試試看,然後加上錯誤處理機制 ``` fetch('https://fakestoreapi.com/users1') .then(res=>res.json()) .then(json=>console.log(json)) .catch(error => { alert(error); }) ``` 就跟 `.then()` 函式把任務傳進去類似,`.catch()` 一樣是把任務傳進去,只是改傳「AJAX 失敗的時候要執行的任務」 --- 這邊只是舉例,才故意打錯字 實務上,AJAX 失敗可能是 - 主機故障、或者過度忙碌無法回應 - 呼叫 API 時,登入驗證資訊過期 - API 設計成有額度限制,用戶額度耗完了,被主機拒絕 - 用戶自己的網路斷線了(網頁打開時正常,但發送 AJAX 時已斷線) 等等很多可能,要看 API 主機是如何設計的 --- 實務上,`.catch()` 內要提醒使用者,剛才的動作失敗,請他重新嘗試 我個人通常就用 alert 跳一個訊息「抱歉,系統出現錯誤,請稍後重新嘗試。若持續出錯,請聯絡客服信箱」就結束了 但在公司的大型專案,需要更好 UX 的話,請與設計師討論後決定如何優雅地處理錯誤情境 ## 課後作業 接續前一課的作業,請加上錯誤處理機制 用 alert 跳出 `抱歉,請稍後重新嘗試。` 就好了 接著,請把電腦的 wifi 或有線網路,斷線 然後點擊「Load Products」按鈕,應該會看到錯誤提示訊息 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列五:第4課 ── 學會 AJAX 基本原理

## 課程目標 認識基本的 AJAX 原理 ## 課程內容 這一課來認識大名鼎鼎的 AJAX 觀念 AJAX 全名 Asynchronous JavaScript and XML 簡單來說,就是「非同步從主機取得資料來更新網頁內容」的技術 舊式的網頁,都是瀏覽器向主機發送 HTTP 請求 -> 主機回應一大坨 html 內容 -> 瀏覽器顯示漂亮網頁給用戶看 因為是一次拿到一大坨 html 內容,我們說「網頁上全部內容都是同步取得」 現代的網頁,也是有很多頁面是這樣直接取得,但有更多功能,是依靠非同步取得資料之後來更新的 - 滑動到網頁下方,動態載入了更多貼文 - 對內容按讚,按讚成功網頁出現了小變化 - 聊天室與別人聊天,網頁也是一段一段文字更新 這些都是使用 AJAX 技術的例子 也就是先載入基本網頁內容,再接著根據需求,於不同時間點發送 HTTP 請求取得部份內容,所以叫做非同步 實務上,我們會說「這邊要發一個 AJAX 跟主機要資料」 --- 讓我們拿一個「模擬線上購物網站 API」來當作例子 https://fakestoreapi.com/ 發一個 AJAX 取得 ID 為 1 的用戶資料 ``` fetch('https://fakestoreapi.com/users/1') .then(res=>res.json()) .then(json=>console.log(json)) ``` 請在 jsfiddle 試試,看看結果 會看到一個包含信箱、ID、姓名、電話等等欄位的用戶個資,以物件的形式呈現 這邊使用了內建的 fetch 函式,參數放入要呼叫的 API 網址 接著使用 `.then()` 函式,由於是直接寫在後面,這相當於把 `fetch()` 回傳的東西,直接當成物件再接著呼叫 `.then()` 函式,然後再把結果當成物件再呼叫 `.then()` 一次 也就是跟這段一模一樣 ``` var result1 = fetch('https://fakestoreapi.com/users/1'); var result2 = result1.then(res=>res.json()); var result3 = result2.then(json=>console.log(json)); alert(result1) alert(result2) alert(result3) ``` 請在 jsfiddle 試試,會發現 console 顯示的個資一樣,這邊用三個 alert 觀察過程中的東西 會發現顯示三次 `[object Promise]`,這個 Promise 是一個進階觀念,這邊不細談,簡單講就是處理非同步請求的一種資料格式 `.then()` 參數傳進一個箭頭函式,這是省略大括號 `{}` 的箭頭函式寫法,其實就只是會自動回傳結果的函式寫法而已 但參數放了個函式,看起來有點怪,為何要這樣寫? --- 記得我們之前寫過的動態綁定 onclick 事件嗎? ``` <button id="my-btn">Click me</button> ``` ``` // 第一種寫法 function myFunction() { alert('你點擊了按鈕!'); } var btn = document.getElementById('my-btn'); btn.onclick = myFunction; ``` 網頁元素的事件處理,也是一種「非同步」程式設計 也就是我不確定「點擊」事件何時會發生,但我先「綁定」好事件發生時要做的任務,綁完就讓網頁正常呈現就好 上面的程式碼,可以改寫成這樣 ``` // 第二種寫法 var btn = document.getElementById('my-btn'); btn.onclick = () => { alert('你點擊了按鈕!'); } ``` 如果使用 jQuery,那還可以這樣改寫 ``` // 第三種寫法 $('#my-btn').click(() => { alert('你點擊了按鈕!'); }) ``` 第一種寫法,看起來像是:我先定義好函式,接著把函式名稱當作變數,綁定到 onclick 屬性 第二種寫法,看起來像是:onclick 這邊現場寫一個箭頭函式,把要執行的任務,當場交待清楚 第三種寫法,看起來像是:jQuery 提供的 `.click()` 函式,會負責把事件綁好,參數傳任務進去就對了 以上三種寫法,效果是完全一模一樣的! 所以你早就接觸過「非同步」程式設計了 也就是「有些任務現在還不會立刻執行,但我先把要執行的任務交待清楚,時間點到的時候,就執行」 以 UI 動作來說,時間點就是 `onclick` 之時、`onchange` 之時 以 AJAX 動作來說,時間點就是 `拿到主機回應` 之時 像這種不是馬上執行的動作,在 JavaScript 領域,我們習慣用「寫一段函式定義當作參數傳進去」來表達! --- 回頭看一下我們的範例 ``` fetch('https://fakestoreapi.com/users/1') .then(res=>res.json()) .then(json=>console.log(json)) ``` 因為 fetch 第一個回傳的結果,代表的是一個 `HTTP 回應物件`,這個回應物件的 HTTP body 是實際的 JSON 內容,可以用 `.json()` 函式取得內容 所以第二個 `.then()` 的參數,才是我們真正想做的事情 看不懂沒關係,我們多看幾個例子吧 取得全部用戶個資的 AJAX。觀察 console 結果,會看到一個陣列,內含大量個資物件 ``` fetch('https://fakestoreapi.com/users') .then(res=>res.json()) .then(json=>console.log(json)) ``` 取得五筆用戶個資,也是拿到陣列 ``` fetch('https://fakestoreapi.com/users?limit=5') .then(res=>res.json()) .then(json=>console.log(json)) ``` 以上內容,全部通通看不懂沒關係,畢竟,需要多了解一些 HTTP 協定與術語,比較好理解 你就先照做就好:要發 AJAX,就用 `fetch()` 函式,接著第一個 `then()` 要執行 `.json()` 函式,然後第二個函式才是你真正要執行的任務! ## 課後作業 請使用 https://jsfiddle.net/ 請使用「模擬線上購物網站 API」 https://fakestoreapi.com/ 假設正在開發一個讀取全部商品資料的頁面 用以下 html 為基礎 ``` <button>Load Products</button> <hr> <ul></ul> ``` 點擊按鈕,發送 AJAX 到 https://fakestoreapi.com/products 請求全部商品資料 拿到資料之後,將每筆資料用以下格式呈現,塞進 `<ul>` 元素裡面 ``` <li> <span>xxx</span> <button>Details</button> </li> ``` xxx 是商品名稱。點擊 Details 按鈕,連續跳出三個 alert,分別顯示 `id` `category` `description` --- 請注意,在 for 迴圈裡面綁定 onclick 事件的時候,for 迴圈的參數請加上 `const` 舉例來說,請這樣寫 ``` for (const product of json) { ``` 請「不要」這樣寫 ``` for (product of json) { ``` 否則,在迴圈裡面的 onclick 事件,執行起來會有 bug 原因跟上一課提到的 Hoisting 現象有關 我認為這是 JavaScript 的設計失敗,所以詳細原因我不想說明 這是屬於上個世代 JS 工程師的痛苦回憶,這一代的 JS 工程師不需要經歷 現在就用 ES6 語法,宣告變數一律記得加上 `const` 或 `let` 就對了 --- 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列五:第3課 ── 變數作用域、箭頭函式、ES6 語法

## 課程目標 認識變數作用域 認識函式的不同寫法與特性 ## 課程內容 來認識一些程式語言觀念與名詞 ``` <button onclick="action1()"> global scope </button> <button onclick="action2()"> local scope </button> ``` ``` var counterA = 0; function action1() { counterA = counterA + 1; alert(counterA); } function action2() { var counterB = 0; counterB = counterB + 1; alert(counterB); } ``` 到 jsfiddle 跑跑看,會發現第一個計數器會不斷 +1 疊加上去;第二個計數器卻永遠顯示 1 這就是變數作用域的區別:變數宣告在很外面的,會在很大的範圍內都可使用這變數;變數宣告在很裡面的,只在裡面的範圍內才可使用這變數 宣告在最外面的稱為全域變數(global),反之則稱為區域變數(local) 目前為止的作業,其實你已經到處在寫 global 與 local 變數了,這觀念還算簡單、直觀 --- 用精確的技術名詞來說明的話 在 ES6 (2015) 之前,JavaScript 中的變數作用域只有 Global Scope 跟 Function Scope 兩種 並且,在使用 `var` 關鍵字時,要留意一種名為 Hoisting 的現象,這是一種會讓人搞錯變數作用域的現象 在 ES6 之後,有了 `const` 與 `let` 兩種新關鍵字,宣告的變數為 Block Scope 使用這兩種關鍵字,就不會出現 Hoisting 的現象 --- 我個人認為,Hoisting 是一個設計失敗的程式語言特性 應該要讓 JavaScript 引擎直接報錯、程式直接壞掉比較好 一般程式語言沒有 Hoisting 這種現象,此為 JavaScript 獨有特性 這是當年 Netscape 瀏覽器公司,為了衝市占率、歡迎大家亂寫 JS 程式碼的產物 我不想細談 Hoisting,反正改天你真的遇到問題,大概知道要往這方向研究就是了 --- 實務上,現在大家都寫 `const` 與 `let`,比較不寫 `var` 了 所以 Function Scope 跟 Block Scope 的差別在哪? 簡單來說,這樣的程式碼,x 正常顯示,y 會報錯 ``` if (true) { var x = 1; const y = 2; } alert(x); alert(y); ``` `var` 會覺得變數作用域,只有 `function 函式` 內、外的差別,內就是同樣 local,外就是 global `const` `let` 會覺得變數作用域,每次遇到 `大括號 {}` 都算一次內、外的差別,大括號裡面就是 local,裡面的裡面就是 local 中的 local 看不太懂沒關係,總之,變數宣告時,遇到 bug,就往前面找大括號,把變數搬來搬去,試試看,會慢慢搞懂的 本課先教你區分 global 與 local 兩種概念就好,這在大多數程式語言都是通用概念 在本系列教材內容以及作業中,`const` `let` `var` 隨便混著用,都可以 大概知道當前變數是 global 還是 local 就好 反正改天你真的遇到問題,大概知道要往這方向研究就是了 --- 接下來談一談 JavaScript 中的函式 之前的課程中,有過這樣的範例 ``` <button id="my-btn">Click me</button> ``` ``` function myFunction() { alert('你點擊了按鈕!'); } var btn = document.getElementById('my-btn'); btn.onclick = myFunction; ``` `myFunction` 被當成變數一樣,被指派給一個物件的屬性了 在很多程式語言中,函式是不能這樣使用的!函式永遠只能單獨加上小括號去執行 `myFunction()` 這個差別有點像是,其他程式語言認為變數是「名詞」,函式是「動詞」。那些語言認為這樣才能溝通、描述世界 而 JavaScript 認為變數是「名詞」,函式是「動詞」也是「動名詞」,也就是認為函式也是一種「名詞」。JavaScript 認為這樣才能溝通、描述世界 中文說「我開車」跟「開車很好玩」,沒有在管「開車」是動詞還是名詞,中文使用者就是習慣這樣溝通 英文說「I drive」跟「Driving is fun」,句子裡面主詞的部份一定要是名詞,如果想放動詞,就先改寫成 +ing 動名詞,英文使用者就是習慣這樣溝通 上面通通看不懂沒關係,反正知道各種程式語言,都是設計者與社群的主觀偏好,然後都能完成任務、各有不同長處短處就好 --- 最後,跟大家談一下函式的不同寫法 ``` function func1() { alert(1); } var func2 = () => { alert(2); } func1(); func2(); ``` ES6 之後有所謂的箭頭函式 他跟傳統寫法的主要差別,在於對於 `this` 關鍵字的認定 在工程師主流推崇 OOP(物件導向程式設計)的年代,`this` 的使用很巧妙、也很讓人困惑 實務上現在寫前端,比較少用 OOP 寫法,稍微偏向 FP(函數式程式設計)多一點,所以 `this` 問題變比較小 我不想細談 `this` 以及兩種函式寫法的差別,在本系列教材內容以及作業中,隨便混著用,都可以 反正改天你真的遇到問題,大概知道要往這方向研究就是了 ## 課後作業 請使用 https://jsfiddle.net 用以下 html 為基礎(你可以稍微修改),id 跟 class 之類的你可以自由決定 ``` simple counter: <button>-</button> <button>+</button> <hr> simple calculator: <input type="text" /> <input type="text" /> <button>加/減/乘/除</button> ``` 這邊有兩個小型應用程式 第一個應用程式,是簡單的計數器 - 第一次點擊 + 號按鈕,會用 alert 跳出 1 - 第二次點擊 + 號按鈕,會用 alert 跳出 2 - 依此類推,每次點 + 都會遞增,每次點 - 都會遞減 - 你會宣告一個全域變數,記錄這個累積的值,才能完成此功能 第二個應用程式,是簡單的計算機 - 有兩個欄位可以輸入數字 - 點擊按鈕,連續跳出四個 alert,分別顯示「加/減/乘/除」的計算結果 - 例如:輸入 6 與 2 -> alert 顯示 8 -> alert 顯示 4 -> alert 顯示 12 -> alert 顯示 3 - 你會宣告兩個區域變數,分別記錄兩個輸入的值,接著用來進行四種計算,才能完成此功能 --- 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列五:第2課 ── 學會 Local Storage 相關功能

## 課程目標 認識並且能使用 Local Storage 相關功能 ## 課程內容 Local Storage 提供了前端開發者簡易的儲存功能,能夠把資料儲存在瀏覽器上 跟 cookie 不同的是,local storage 的資料不會挾帶在 http request 中發送,也就是伺服器端無法直接存取 並且 local storage 能儲存的資料大小,比 cookie 大很多 ``` Name: <input type="text"> Age: <input type="text"> <button onclick="save()">Save</button> ``` ``` if (localStorage.getItem("me")) { var me = JSON.parse(localStorage.getItem("me")); alert('hello, ' + me.name + ' (' + me.age + ')'); } function save() { var name = document.querySelectorAll('input')[0].value; var age = document.querySelectorAll('input')[1].value; var me = { name: name, age: age }; localStorage.setItem("me", JSON.stringify(me)); alert('saved'); } ``` 在這邊的範例中,使用 `localStorage.setItem()` 來儲存資料,使用 `localStorage.getItem()` 來取得資料 因為只能在 local storage 中儲存字串,所以使用 `JSON.stringify` 將物件轉換成字串,使用 `JSON.parse` 將字串轉換成物件 在 JavaScript 中,表達物件的字串格式又稱為 JSON 字串,反正就是一種格式而已,資料類型是字串 上述的程式碼,寫起來比 cookie 簡單很多,API 乾淨很多 實務上,如果儲存的資料,不需要在主機端使用,就都用 local storage 即可 --- 關於 local storage 的 debug 除錯,也要知道一下 跟 cookie 差不多 打開瀏覽器開發者工具 -> Application -> Storage -> Local Storage 可以看到網站上有哪些資料,也可以直接在這邊修改、刪除資料 ## 課後作業 請回頭找出你在系列課程二寫的作業 https://codelove.tw/@howtomakeaturn/course/vx8gqZ 當初的待辦事項小工具,少了一個重要功能:儲存按鈕 也就是重新整理之後,待辦事項就通通不見了 請在「匯出」按鈕旁邊,增加一個「儲存」按鈕,讓待辦事項在頁面重新整理之後,還可以繼續使用 - 按下按鈕之後,使用 local storage 儲存待辦事項資料,並跳出 alert 提示「儲存成功!」 - 頁面載入的時候,檢查 local storage,有資料的話,就復原到畫面上 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列五:第1課 ── 學會 Cookie 相關功能

## 課程目標 認識並且能使用 Cookie 相關功能 ## 課程內容 大名鼎鼎的 cookie,很多人在科技新聞上都聽過 某某公司利用網頁 cookie 追蹤個資、某某公司偷偷用 cookie 分享個資 這類新聞提到的 cookie,就是本課在說的 cookie 目前為止的作業,你會發現只要重新整理網頁,畫面上的所有東西就都歸零回預設值了 因為我們說網頁 HTTP 協議是「無狀態」的,也就是每次打開網頁的「狀態」不會被保留 這樣的設計當然有好處以及方便的地方,因為每次打開網頁都是獨立事件、彼此不會互相影響,開發起來單純很多 但是在實務上,經常需要「有狀態」才能完成某些功能,例如「已登入帳號」的狀態,否則網站會變很難用 Cookie 技術就是為了這個原因而誕生 --- Cookie 主要有兩個特性 - cookie 的內容,主機端也能存取 - cookie 的內容,網頁重新整理之後還會保留 Cookie 會出現在 HTTP Request 內,一起送到主機 會員登入功能、各種廣告追蹤功能,就是這樣做到的 HTTP 協議不熟沒關係,反正就是前端工程師設定過 cookie 值之後,後端工程師也能在主機端拿到那些值就對了 --- 注意,由於 cookie 會包含一些網站上的敏感資訊,jsfiddle 等等線上程式碼實驗工具、以及各種免費架站工具,幾乎通通都關閉了 cookie 功能 我目前找到一個可以用 cookie 的架站工具 https://replit.com/ 本課請使用 replit 來跑範例程式碼、寫作業 註冊登入之後,點 Create -> Templates -> 選 HTML, CSS, JS 那個 -> Create Repl 就可以開始寫了,但是右邊的預覽視窗,因為是 iframe,一樣不能用 cookie 請點擊右上方的 Open in a new tab,就可以正常跑 cookie 功能了 --- 來看一些範例程式碼吧 ``` Your Age: <input type="text"> Your Name: <input type="text"> <button onclick="save()">Save</button> <script> if (document.cookie) { alert(document.cookie); var arr = document.cookie.split(';'); var myage = arr[0].split('=')[1]; var myname = arr[1].split('=')[1]; document.querySelectorAll('input')[0].value = myage; document.querySelectorAll('input')[1].value = myname; } function save() { var myage = document.querySelectorAll('input')[0].value; var myname = document.querySelectorAll('input')[1].value; document.cookie = 'myage=' + myage; document.cookie = 'myname=' + myname; alert('saved!'); } </script> ``` 這個範例中,輸入年齡、名字一次,接著重新整理網頁,會發現資料一樣存在欄位裡面,不用重新輸入! 存取值,都是透過 `document.cookie` 這個物件屬性,值的格式是 `name=value` 雖然 `document.cookie=` 寫了兩次,但第二次並不會蓋掉第一次的值,而是兩個都存進 cookie 了 (這個設計,很糟糕,沒錯,讓人看得很混亂。應該是瀏覽器的設計者,有他們的苦衷,我們先不細究原因,先知道怎麼用就好) 然後,讀取的時候,多個值會用分號 `;` 隔開,所以讀取時我用了 `.split()` 這個字串函式 除此之外,cookie 還可以設定「使用期限」以及「限定網域」,但我這邊就先不示範 總之,cookie 的效果大概就是這樣,稍微理解就好。然後這些值,後端工程師在伺服器端,也會收到,就這樣! --- 如上所示,cookie 寫起來,有點麻煩 實務上,每次都這樣手寫,太囉唆,通常會直接找套件來用,我是用這款 https://github.com/js-cookie/js-cookie 用來操作 cookie 的 API,漂亮多了! --- 關於 cookie 的 debug 除錯,也要知道一下 打開瀏覽器開發者工具 -> Application -> Cookies 可以看到網站上有哪些 cookie,也可以直接在這邊修改、刪除 cookie --- cookie 還有一些特性,以及大小限制、使用限制 這邊不細談,稍微知道怎麼用 cookie 就好 有興趣的話,請上網搜尋一下,多研究一些 cookie 的細節 ## 課後作業 請使用 https://replit.com/ 來寫作業 假設你正在幫客戶寫一個「成人限制級網站」 頁面第一次打開會顯示蓋板警告,下方有兩個按鈕 ``` 警告︰您即將進入之網頁內容需滿十八歲方可瀏覽。 根據「兒童及少年福利與權益保障法」規定,本網站已於非闔家皆宜之網頁加以標示。若您尚未年滿十八歲,請點選離開。若您已滿十八歲,亦不可將本站之內容派發、傳閱、出售、出租、交給或借予年齡未滿18歲的人士瀏覽,或將本網站內容向該人士出示、播放或放映。 您年滿十八歲嗎? [離開] [是,我已年滿十八歲] ``` - 點擊離開,把用戶跳轉到 google 首頁 - 點擊確認,就關閉蓋板警告,在網頁上顯示一張美女 or 帥哥圖片:參考圖庫 https://unsplash.com/ - 只要按過確認,重新整理之後,蓋板警告就不會再跳出來 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列四:第4課 ── jQuery 套件

## 課程目標 認識並且能使用 jQuery 套件 ## 課程內容 這一課要教的東西比較有爭議 jQuery 是 2006 - 2015 年代,前端網頁開發的王者 當時的瀏覽器廠商眾多,網頁規格 API 支援方式不同、不完整(尤其是 IE 一堆奇怪的 API) 當時的前端工程師,非常痛苦,要為了 IE 多改很多寫法 加上當時很多瀏覽器沒有支援 `.querySelector` `.querySelectorAll` 這些 Selector API 所以 jQuery 的出現,強大的選擇器語法,以及眾多方便的功能,大幅降低了前端開發的成本 不過,時至今日,jQuery 的大部份功能,都已在瀏覽器 JavaScript 語法中原生支援了 再加上開發大型應用程式,DOM 的管理一旦複雜起來,就會直接使用 React 或者 Vue 這種框架來管理 DOM,不會再手動去操作 DOM (前端框架會處理完 DOM 互動的部份。此時如果再手動操作 DOM,那程式碼就會一團混亂) 也就是說,小型專案,「不需要」用 jQuery;大型專案,「不可以」用 jQuery 除此之外,jQuery 套件本身檔案不小,引入此套件會影響網頁載入速度,影響 UX,同時會影響搜尋引擎 SEO 排名 --- 因為上述種種原因,在今天,前端工程師普遍鄙視 jQuery,避之惟恐不及 我個人的建議是:團隊在開發複雜的前端應用時,的確不要再用 jQuery 了! 但是,當只是做小網站,接小案子賺錢,或者做個人的業餘專案(side project),只是希望快速做完某些功能時,jQuery 還是非常好用! 原因有兩個 第一,jQuery 選擇器寫起來很短,可以少打很多字,開發很快速 第二,jQuery 外掛生態系存在已久,很多強大的 UI 元件會用到 jQuery,適合的話,實在沒道理全面放棄不用 而且,並不是所有網站,都需要直接導入 React 跟 Vue 這種大型框架!沒必要! 除此之外,維護一大堆現有的程式碼,難免已經用到 jQuery,還是能看懂 API 比較好 所以,還是稍微教大家一些 jQuery 的用法。至於什麼場合要使用,你就自行判斷吧 --- 官網 https://jquery.com/ API 用法 https://api.jquery.com/ CDN 連結 https://releases.jquery.com/ 請自行逛一逛,大概知道 jQuery 有哪些 API 就好,不用花太多時間學習 jQuery 你應該會發現,一大堆功能,你自己就寫得出來,而且寫起來也不複雜,例如 `.hide()` `.show()`,你之前的作業就寫過,根本不需要用 jQuery 剩下的一大堆功能,目前看不懂沒關係、覺得自己做不出來沒關係,未來的課程會教你 --- 簡單地說,jQuery 選擇器就是使用金錢符號來呼叫函示 `$()` 其實是 `jQuery()` 的簡寫而已 ``` <h1 class="title"> Hide me </h1> <p class="para"> Hide me </p> <p class="para"> Hide me </p> <p class="para"> Hide me </p> ``` ``` document.querySelector('.title').style.display = 'none'; for (var p of document.querySelectorAll('.para')) { p.style.display = 'none'; } ``` 像這樣的程式,隱藏了畫面上多個元素 使用 jQuery 的寫法會變成 ``` $('.title').hide(); $('.para').hide(); ``` 很多人覺得,jQuery 只剩一些少打字的功能,卻讓網頁多載入整個 jQuery 套件,很沒意義、不值得 我倒是覺得,還是滿方便的。快速開發時,少打很多字。而且 jQuery 可以自動處理多個元素,可以少寫 for 迴圈 當然了,上面的程式碼,跟下面這段是完全一樣的 ``` jQuery('.title').hide(); jQuery('.para').hide(); ``` jQuery 基本上就這樣而已,你喜歡嗎? 實務上,使用時,如果翻文件,還是不知怎麼寫,就去搜尋引擎找關鍵字 `jquery 改變顏色` `jquery change color` 之類的 別忘了,軟體工程師在工作時,會花很多時間在翻文件、上網到處看文章、搜尋範例程式碼,所以要習慣這件事 --- 坊間很多課程,會在一開始就教 jQuery。這跟直接教前端框架的問題一樣:學生的基本功,因此變得很差 但是本課程已讓你知道最基本的原生 DOM 寫法,所以你在用 jQuery 時,會知道背後發生什麼事,也知道刪掉 jQuery 的話,要怎麼用原生語法改寫 所以,我認為在本課程的安排下,jQuery 並不會耽誤你的學習旅程 喜歡 jQuery 的話,就把 API 網頁,多逛一逛,工作上想用的話,就大方使用吧! ## 課後作業 請使用 https://jsfiddle.net 這次的作業要交兩份 請建立兩份 jsfiddle,並使用以下 html ``` <h1 class="title"> Make me green </h1> <p class="para"> Make me red </p> <p class="para"> Me red too </p> <p class="para"> Also me </p> ``` 在第一份,不使用 jQuery,把 h1 文字變為綠色,p 文字變為紅色 在第二份,請使用 jQuery,把 h1 文字變為綠色,p 文字變為紅色 寫完之後,你自行比較一下,網頁多加一個肥大的套件,但是可以少打這些字,你覺得值得嗎? 這是一個主觀問題,你就根據情況決定你的答案吧! 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列四:第3課 ── Chart.js 套件

## 課程目標 認識並且能使用 Chart.js 套件 ## 課程內容 這次的套件是 https://github.com/chartjs/Chart.js 文件與 demo 在 https://www.chartjs.org/ 這是一個畫圖表、報表的工具,而且非常漂亮 如果不使用套件,自己手動做圖表的話,要用 `canvas api` 來畫,而且需要很多數學計算來繪圖,非常麻煩 這次就完全自己研究看看吧。先試著讓官方範例可以成功跑出來,然後再試著調整設定、參數、放進自己的資料畫圖看看 --- 軟體工程師,經常需要翻閱大量的國外文件,所以需要基本的英語閱讀能力 不用精通沒關係,一邊翻文件,一邊查字典,拼湊線索看看 如果實在不行,就搜尋 `套件名稱 教學` 來找找看有沒有好心人寫過教學,例如 `chart js 教學` 長遠來說,還是要逐漸提升自己的英語閱讀能力才行 ## 課後作業 請使用 https://jsfiddle.net 請在網頁上,畫出三種圖表 - 折線圖 Line Chart - 長條圖 Bar Chart - 圓餅圖 Pie Chart 政府近年在提倡透明化,公佈了許多資料 請前往 政府資料開放平台 https://data.gov.tw/ 或是 台北市政府資料開放平台 https://data.taipei/ 逛逛看,找幾份有趣的資料。 這三種圖表的使用時機不太一樣,適用的資料類型也不太一樣,你可以稍微研究一下,選擇你覺得適合的 (資料量巨大的話,請在圖表內呈現10筆資料即可。) 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列四:第2課 ── vanilla-lazyload 套件

## 課程目標 認識並且能使用 vanilla-lazyload 套件 ## 課程內容 這次的套件是 https://github.com/verlok/vanilla-lazyload 文件與 demo 在 https://www.andreaverlicchi.eu/vanilla-lazyload/ 當畫面上圖片很多的時候,頁面載入會很慢 lazy load 是所謂的延遲載入技術 也就是不會在一開始就通通載入,而是使用者在網頁上下滑動的時候,圖片元素出現在畫面中的時候,才動態去載入圖片 ``` <img alt="A lazy image" class="lazy" data-src="lazy.jpg" /> ``` 使用了 img 元素,class 設為 lazy,但不設定 src 屬性(因為設了瀏覽器就會直接去抓圖片),而是使用 data attribute 來另外給一個 data-src 屬性 這是一種常用的 API 設計,套件會自己去找出 `.lazy` 的元素,然後去找元素的 `data-src` 屬性,接著載入圖片 要啟用套件,需要以下 javascript ``` var lazyLoadInstance = new LazyLoad({ // Your custom settings go here }); ``` 這樣就完成了,很簡單吧? 這邊用到了 javascript 的 OOP 物件導向語法,new 出一個新物件。可以先不管 OOP 細節,照做就好,反正就是創造一個物件然後執行一些函式 創造的同時,會傳一個用大括號 `{}` 包起來的物件進去,當作套件的設定值。這邊因為都用預設的設定,所以傳空物件進去就好 `//` 開頭的是註解,是給開發者自己看的提醒文字,沒有實際功能 比較進階的用法會類似這樣 ``` var lazyLazy = new LazyLoad({ elements_selector: ".horizContainer", callback_enter: initOneLazyLoad, unobserve_entered: true // Stop observing .horizContainer(s) after they entered }); ``` 這個例子傳了有三個屬性的物件進去 各個設定與功能效果,可以再查文件,反正會用 `{}` 包起來的物件去進行參數設定就對了 這種「傳一個物件當參數,來進行套件的功能設定」的設計,也是 javascript 套件的常用 API 設計 關於物件這種資料型態,之後的課程會再進一步學習 ## 課後作業 請使用 https://jsfiddle.net 接著上網尋找 10 張漂亮圖片。參考圖庫:https://unsplash.com/ 假設正在開發一個包含大量高畫質、大圖片的企業形象網站,包含這 10 張圖片,上往下依序排列 一次載入,實在太慢了,尤其是手機用戶、舊電腦的用戶、或者網速很慢的用戶 請替這些圖片加上 lazy loading 功能 做出以上功能,你就完成這次的課程目標了!

JavaScript 系列四:第1課 ── autosize 套件

## 課程目標 認識並且能使用 autosize 套件 ## 課程內容 這次的套件是 https://github.com/jackmoore/autosize 文件與 demo 在 https://www.jacklmoore.com/autosize/ 這是一個讓 `<textarea>` 的 UX(User Experience 使用者體驗)瞬間升級的好用套件 ``` // from a NodeList autosize(document.querySelectorAll('textarea')); // from a single Node autosize(document.querySelector('textarea')); ``` 使用起來非常簡單,把 DOM 元素找到,當成參數傳給 autosize 函式,就可以了 在設計各種第三方套件給別人的時候,通常都會設計成這樣,很簡單、易用吧 程式的使用方法,我們叫 API(Application Programming Interface,應用程式介面) 實務上,我們會跟同事說:「去查一下這個套件的 API」,也就是去看一下這套件的使用方法。可以理解成「產品規格」 --- 在上網搜尋套件的時候,第一個要先看這個專案的星星數量,至少要有幾百顆以上吧 如果是太冷門的套件,代表可能不夠成熟、文件不完整、潛在 bug 會很多 第二個要看的是最近更新日期,也就是 last commit 的時間,至少兩年內有更新過吧 太久沒更新的專案,可能有點過時,會跟最新的瀏覽器或者其他工具有衝突 不過,如果只是用在網站上的小小地方,真的出問題,再換掉的成本也不大,那就先導入也沒關係 --- 要使用第三方套件,除了直接下載然後使用 `<script>` 引入之外,還有一個常用的懶人方法 就是搜尋 `套件名稱 CDN`,直接找現成的 CDN 連結來用 CDN 全名 Content Distribution Network,內容傳遞網路。這是大型企業讓資源在全球加速載入用的 看不懂沒關係,反正搜尋 `套件名稱 CDN`,通常會有現成 `<script>` 可用 搜尋 `autosize cdn` 看看搜尋結果,就會知道了 (要注意這是一個懶人方法,工作上直接這樣用,網站可能會載入的有點慢) ## 課後作業 請使用 https://jsfiddle.net 假設你正在開發一個留言功能 有暱稱欄位、留言欄位、以及一個送出按鈕 留言的區塊,`textarea` 預設只有三行的高度 使用者在輸入時,輸入越多行,輸入區塊會自動變高,而不會出現捲軸 做出以上功能,你就完成這次的課程目標了!