🔍 搜尋結果:.

🔍 搜尋結果:.

討論-淺談html/js之關係以及,延伸至XSS

>最近在忙著實作我的一些小項目,沒有上來發文,但還是一直學習JS, >累積滿多經驗,也累積不少疑問。 >之前站長阿川有跟我提到相關話題,也就是UJS,想說把它拿過來聊一聊。 >剛好最近弄的東西,跟這個也有一些關聯性。 ## html與JS的交互關係。 Inline JavaScript是什麼呢? 就把JS寫在html裡面的一種手段,也就是onclick那些東西,在自學教材會學到唷:))。 其中也有個東西叫做JavaScript pseudo-protocol,他好像有點像是給一個js的前綴, 讓瀏覽器讀取html的時候知道要開始讀取JS了,用法如: `<a href="javascript:alert(1)">my website</a>` 這樣子的作法都讓JS侵入到html裡面,也就是行為跟結構沒有分離。 是否要UJS,站長上次有提到這可以看專案大小來區分,這篇閒聊開起來可以提供更多細節。 --- 不過我不打算就此結束文章,我要額外分享與XSS相關的內容。 XSS其實是CSS,跨站指令碼Cross Site Script的意思,只是因為縮寫重複CSS樣式(Cascading Style Sheets )所以用X代替。 簡單理解它就是一種攻擊手段,讓別人網站上執行我的代碼。 --- 有趣的事情來了,那html跟JS交互關係跟XSS又有什麼關聯呢? 想像一下,當今天小明要做一個討論區,讓使用者用html編輯打文章,就必須小心有人會放一些JS的代碼。 小明想說,我要過濾`<script>`標籤來保證他們乖乖地寫html。 請問這樣子是否就安全了呢? 答案是否定的,這時候使用Inline JavaScript會發生什麼狀況? 試想一下因為小明沒有CSP相關設定,防止使用者偷偷塞JS到html裡面,導致小明的壞朋友大胖, 可以直接寫一個`<img src=x onerror="alert(1)">`在留言區裡面, 完全沒有使用到`<script>`標籤,卻也能造成XSS。 --- ## 關於防禦: ### CSP 設定嚴格一點,可以阻止很多XSS,舉例來說文章一開始有`href="javascript:alert(1)"`, 可以設定href後面只能接上http開頭的東西。 ### WAF 他是Web Application Firewall 的簡稱,中文通常叫作「網站應用程式防火牆」 可以建立及管理避免網際網路威脅的規則,包括IP 位址、HTTP 標頭、HTTP 主體、URI 字串、跨網站命令檔(XSS)、SQL 隱碼攻擊及其他OWASP 定義的漏洞。 ### 自己建立fliter 自己過濾也是可行的(不建議),把字串符碼都轉成HTML Entities,例如把 (<)寫成 ` &lt; or &#60;` ,透過這樣的過濾render出來的就不會是指令了。 為什麼不建議,是因為你過濾的東西有可能會被double encoded URL之類的手法繞過。 或是你覆蓋了document.write,以為安全了,但是我用孿生函數(twin function)的document.writeIn你可能就忘了過濾。 這剛好就是我最近在研究的東西。 ## 相關討論問題: 1.對於文章的內容,有沒有什麼錯誤呢?或相關名詞想法想補充? 2.是否要UJS的實際使用,有哪些想法?或是有什麼經驗?

此篇閒聊-關於這個網站以及關於站長(+此站使用心得

因為我有個點子是搞一個梗圖的網站 就想說上網先看看人家怎麼弄的 看著就到了梗圖倉庫 欸 突然眼熟的看到站長阿川!? 稍微確認一下才知道是我們的站長 挖真的是偶像啊0.0 早我好幾百年就已經弄得票漂亮亮有聲有色  我還在那邊1+1咧 哭暈在廁所 難怪自我介紹說有經驗 相關問題都能回答 這豈止是回答 是模範 是偶像欸(跪了 是說站長人格方面可能才是我最欽佩的  為人善良熱情  低調謙虛 沒有把自己的行頭  作品都丟上來搞一個自我經歷很豐富這樣的介紹  真的很低調欸 小弟個人覺得這真的是各位學習的典範 這邊不知道會不會不太好 推薦這篇 [工程師的缺德行為:叫朋友去學C/C++](https://blog.turn.tw/?p=2568) 太中肯啦 最屌的一句話 我真心覺得屌 非吹捧 是「別擔心,這不是你的錯,是那些書寫得不夠好」 說實話要說那些書很爛嗎 也沒有 但說真的 真的不夠好 其實現在的網路課程 線上教學也是有同樣的問題 課程編排都很死 講老半天的閉包 hoisting 那些東西 不是說這些不重要 要面試 要工作恐怕那是最基本的 可是我身為一個愛好者 我沒看見那些觀念真的被套用&應用在實際狀況 舉例來說我學變數的觀念 是經由RPG製作大師 要修復做寶箱會無限開的bug 因此設定一個變數是off 打開是on 然後用if條件式去判斷 我一次學會好多東西 第一個我懂了變數 我知道需要記憶體幫我存資料 第二資料型態 Boolean 就是on跟off兩種 然後我也學會流程判斷 也就是if x=off then....這樣子 後來用CE修改器發現要找血量 是小數點 所以搜尋要用浮點數(float) 再來是玩VB要寫自動發話所以瞭解了迴圈 並知道要寫一個函式 再去call它 多麼精彩啊這些片段!每一個細節我都忘不了!全部都是畫面!全部都很有趣! 以此類推 講真的到現在 每一個語法都是我靠著自己實踐 找出來的 雖然這樣子花了十來幾年 程度還很弱 但我敢說這樣子的內容有趣 甚至我也敢說 這樣才有意義 我至今到現在常常看網路教學影片 還是一堆講課講老半天都是程式的語法 沒有實戰也就算了 先理解語法 但講解的都很生硬 完完全全都只是為了講課而解釋&示範 真正要實戰了的話也都玩那幾招 沒有變化 明明這個網路世界 程式世界是如此的繽紛 到了他們教學的手上 真的是變黑白 也難怪需要特別聰明跟有天分的人才能駕馭 因為教學資源的傾向就是科板書的教法 當然要足夠聰明 而不是有熱誠 純有熱誠也要能像我這樣十來年慢慢去搞 錯誤了沒地方搞懂 再等好幾年後發現問題 對我來說 我不是特別聰明 我一路走來也理解了 我的確偏笨 但打開書本或是課程 從VScode建置開始 慢慢開始介紹語言的歷史 我真的看不下去 無聊死了 我寧願花個十年 用我有趣的摸索去實戰 去玩 也不要聽課 對我來說 這些課不是爛 是真的不夠好 我用我的自學證明 有一套比他們的課程更生動活潑 更有愛的方法能學程式 至少對我自己來說永遠為真 嗯嗯 離題了 主要是在這個論壇 真的很棒 鼓勵大家也多多發文分享 大多數擁有聰明頭腦的人 或是自己熬過來的工程師 只會轉頭批評外行人蠢或笨 要不然就是酸 藏私 不回答你還講幹話說  這你就不懂  或是重複一次問題說  就把這個解決就好啦 優越感灌下去 見不得你好 甚至批評你是在搞東搞西 我真的遇到一個 我發問問A問題 結果有個嘴臭仔他不回答就算了 在那邊嘴說看我過去發文 過去問B過去問C 說我是搞東搞西沒屁用 叫我去系統性學 在那邊講跟我問題無關的屁話 系統性學要是能解決 我早就解決了== 要怎樣的系統 笑死 你說的系統才是系統喔 我只想知道我研究的領域 你要我系統去哪裡 真的超傻眼 雖然文長有點抒發心情 但收尾主要還是想說 這個網站的資源是不同於其他地方的 還有這個網站的站長也是很真心要提升台灣程式社群的 所以我們可以一起利用這個資源~一起好好努力~ 讓未來新加入的分子 可以看到有趣的例子、找到真的有用的問答文章~ 站長是個很熱心 很有才華想法的大家長 我認為難得的是也很善良與見解獨到 能體會我們這種熱愛程式的人的心 https://codelove.tw/@howtomakeaturn/post/VaGR3p 各位都應該去點個讚!!

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/ - 只要按過確認,重新整理之後,蓋板警告就不會再跳出來 做出以上功能,你就完成這次的課程目標了!

7 個好用的 React Hooks:可以在很多專案直接使用

Hooks 是 React 最強大的功能之一。 它們使我們能夠輕鬆地在應用程式的元件中重用功能。掛鉤的最大優點在於它們的可重用性——您可以跨元件和專案重用掛鉤。 以下是我在每個 React 專案中重複使用的七個最重要的鉤子。今天就試一試,看看它們在建置您自己的 React 應用程式時是否有幫助吧。 原文出處:https://dev.to/webdevhero-com/7-react-hooks-for-every-project-1jdo --- 在我們開始之前,要先澄清一下:並非每個自定義 React 鉤子都需要由您編寫。事實上,我將提到的所有鉤子都來自一個庫“@mantine/hooks”。 Mantine 是一個很棒的第三方庫,其中包含這些鉤子等等。他們將為您的 React 應用程式加入您能想到的幾乎所有重要功能。 您可以在 [mantine.dev](https://mantine.dev) 查看“@mantine/hooks”的文件。 ## `useIntersection` 鉤子 當用戶在您的應用程式中向下滾動頁面時,您可能想知道某個元素何時對他們可見。 例如,您可能希望僅在用戶看到特定元素時才啟動動畫。或者,您可能希望在他們向下滾動頁面一定程度後,顯示或隱藏元素。 ![use intersection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zilwvvdjppw9uz82iixo.gif) 要獲取有關元素是否可見的訊息,我們可以使用 **Intersection Observer API。**這是瀏覽器中內建的 JavaScript API。 我們可以使用純 JavaScript 單獨使用 API,但要知道有關特定元素是否在其滾動容器內,有個好方法是使用 useIntersection 掛鉤。 ``` import { useRef } from 'react'; import { useIntersection } from '@mantine/hooks'; function Demo() { const containerRef = useRef(); const { ref, entry } = useIntersection({ root: containerRef.current, threshold: 1, }); return ( <main ref={containerRef} style={{ overflowY: 'scroll', height: 300 }}> <div ref={ref}> <span> {entry?.isIntersecting ? 'Fully visible' : 'Obscured'} </span> </div> </main> ); } ``` 要使用它,我們需要做的就是在我們的元件中呼叫鉤子,並提供一個根元素。 Root 是滾動容器,可以使用 useRef 掛鉤將其作為 ref 提供。 `useIntersection` 回傳一個我們傳遞給目標元素的 ref,我們想要觀察其在滾動容器中的交集。 一旦我們有了對元素的引用,我們就可以追蹤元素是否相交。在上面的範例中,我們可以根據 entry.isIntersecting 的值查看元素何時被遮擋或何時完全可見。 您可以傳遞其他參數,這些參數允許您配置與目標可見百分比相關的**閾值**。 ## `useScrollLock` 鉤子 另一個與滾動相關的鉤子是 useScrollLock 鉤子。這個鉤子非常簡單:它使您能夠鎖定 body 元素上的任何滾動。 我發現當您想在當前頁面上顯示疊加層或跳出視窗,並且不想讓用戶在後台頁面上上下滾動時,它會很有幫助。這使您可以將注意力集中在視窗上,或允許在其自己的滾動容器內滾動。 ``` import { useScrollLock } from '@mantine/hooks'; import { Button, Group } from '@mantine/core'; import { IconLock, IconLockOpen } from '@tabler/icons'; function Demo() { const [scrollLocked, setScrollLocked] = useScrollLock(); return ( <Group position="center"> <Button onClick={() => setScrollLocked((c) => !c)} variant="outline" leftIcon={scrollLocked ? <IconLock size={16} /> : <IconLockOpen size={16} />} > {scrollLocked ? 'Unlock scroll' : 'Lock scroll'} </Button> </Group> ); } ``` `useScrollLock` 將用戶的滾動鎖定在頁面上的當前位置。該函數回傳一個陣列,它可以被解構,如上面的程式碼所示。 第二個值是一個允許我們鎖定滾動的函數。另一方面,第一個解構值是一個布林值,它告訴我們滾動條是否已被鎖定。 這個值很有用,例如,如果你想在滾動鎖定時顯示某些內容或告訴用戶它已被鎖定。您可以在下面的示例中看到,當滾動條被鎖定或解鎖時,我們會在我們的按鈕中進行指示。 ![use scroll lock](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wywccv7uexfrhfgayqj9.gif) ## `useClipboard` 鉤子 在許多情況下,您希望提供一個按鈕,允許用戶將內容複製到他們的剪貼板,這是存儲複製文本的地方。 一個很好的例子是,如果您的網站上有一個程式碼片段,並且您希望用戶輕鬆複製它。為此,我們可以使用另一個 Web API——**剪貼板 API**。 `@mantine/hooks` 為我們提供了一個方便的 `useClipboard` 鉤子,它回傳幾個屬性:`copied`,它是一個布林值,告訴我們是否已使用鉤子將值複製到剪貼板,以及` copy` 函數,我們可以將我們喜歡的任何字串值傳遞給它以進行複制。 在我們的範例中,我們想複製一個程式碼片段,供我們的用戶粘貼到他們喜歡的地方,如下面的影片所示: ![使用剪貼板](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6n0xd02vdg65bbfmb2xh.gif) ## useClipboard demo 當他們點擊我們指定的複制按鈕時,我們呼叫我們的 `copy` 函數,將程式碼片段傳遞給它,然後顯示一個小複選標記或向他們表明文本已被複製的東西。 巧妙的是 `useClipboard` 掛鉤帶有 **超時值**。在給定的超時時間(以毫秒為單位)之後,複製的狀態將被重置,向用戶顯示他們可以再次復製文本。 ## `useDebouncedValue` 鉤子 如果您的應用程式中有搜尋輸入,下一個鉤子“useDebouncedValue”是必不可少的。 每當用戶使用輸入執行搜尋時,搜尋操作通常涉及對 API 的 HTTP 請求。 您將遇到的一個典型問題是每次擊鍵都會執行查詢(請求),尤其是如果您希望用戶在鍵入時接收搜尋結果。即使對於一個簡單的搜尋查詢,也不需要在用戶完成輸入他們想要的內容之前執行這麼多請求。 這是 useDebounceValue 掛鉤的一個很好的用例,它對傳遞給它的文本應用「防抖」功能。 ``` import { useState } from 'react'; import { useDebouncedValue } from '@mantine/hooks'; import { getResults } from 'api'; function Demo() { const [value, setValue] = useState(''); const [results, setResults] = useState([]) const [debounced] = useDebouncedValue(value, 200); // wait time of 200 ms useEffect(() => { if (debounced) { handleGetResults() } async function handleGetResults() { const results = await getResults(debounced) setResults(results) } }, [debounced]) return ( <> <input label="Enter search query" value={value} style={{ flex: 1 }} onChange={(event) => setValue(event.currentTarget.value)} /> <ul>{results.map(result => <li>{result}</li>}</ul> </> ); } ``` 您使用 useState 將輸入的文本儲存在一個狀態中,並將狀態變數傳遞給 useDebouncedValue 。 作為該掛鉤的第二個參數,您可以提供一個等待時間,即值被「反抖」的時間段。反抖使我們能夠執行更少的查詢。 您可以在下面的影片中看到結果,用戶在其中鍵入內容,並且僅在 200 毫秒後,我們才能看到去抖值。 ![使用去抖值](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfb5kqb6bxbkqfvog1hw.gif) ## `useMediaQuery` 鉤子 我一直使用的另一個非常有用的鉤子是 useMediaQuery 鉤子。 Media Queries 在純 CSS 中使用,`useMediaQuery` 鉤子允許我們訂閱我們傳遞給鉤子的任何媒體查詢。 例如,在我們的元件中,假設我們想要顯示一些文本或根據特定螢幕寬度(例如 900 像素)更改元件的樣式。我們像在 CSS 中一樣提供媒體查詢,並且 useMediaQuery 回傳給我們一個 true 或 false 的 matches 值。 ``` import { useMediaQuery } from '@mantine/hooks'; function Demo() { const matches = useMediaQuery('(min-width: 900px)'); return ( <div style={{ color: matches ? 'teal' : 'red' }}> {matches ? 'I am teal' : 'I am red'} </div> ); } ``` 它用 JavaScript 告訴我們媒體查詢的結果,這在我們想要使用 `style` 屬性在 JSX 中直接更改樣式時特別有用,例如。 ![使用媒體查詢](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7al1apywiq08ntgs4d8l.gif) 簡而言之,對於少數無法使用 CSS 處理媒體查詢的情況,這是一個必不可少的鉤子。 ## `useClickOutside` 鉤子 下一個掛鉤 - `useClickOutside` - 可能看起來很奇怪,但當您真正需要它時,您會發現它的重要性。 當你開發一個下拉菜單,或者在頁面內容前面彈出,並且之後需要關閉的東西時,這個鉤子是必不可少的。通過單擊按鈕打開這些類型的元件之一非常容易。關閉這些元件有點困難。 為了遵循良好的 UX 實踐,我們希望任何阻礙用戶視圖的東西都可以通過單擊元素外部輕鬆關閉。這正是 useClickOutside 掛鉤讓我們做的。 當我們呼叫 useClickOutside 時,它會返回一個 ref,我們必須將其傳遞給我們想要檢測點擊的外部元素。通常該元素將由一個布林狀態片段控制,例如我們在下面的示例中的狀態(即值“opened”)。 ``` import { useState } from 'react'; import { useClickOutside } from '@mantine/hooks'; function Demo() { const [opened, setOpened] = useState(false); const ref = useClickOutside(() => setOpened(false)); return ( <> <button onClick={() => setOpened(true)}>Open dropdown</button> {opened && ( <div ref={ref} shadow="sm"> <span>Click outside to close</span> </div> )} </> ); } ``` `useClickOutside` 接受一個回調函數,該函數控制當您實際單擊該元素外部時發生的情況。 在大多數情況下,我們想做一些非常簡單的事情,就是關閉它。為此,您可能需要一個狀態設置器(如 `setOpened`)並向其傳遞一個 false 值,然後隱藏您覆蓋的內容。 ![use click outside](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4jidp380brennyatol7w.gif) ## `useForm` 鉤子 在這個列表中,我最喜歡和最有用的鉤子是 `useForm` 鉤子。 這個鉤子專門來自 Mantine,涉及從庫中安裝一個特定的包:`@mantine/form`。它會為您提供在 React 中建立表單所需的一切,包括驗證輸入、顯示錯誤訊息以及在提交表單之前確保輸入值正確的能力。 `useForm` 接受一些初始值,這些初始值對應於您在表單中的任何輸入。 ``` import { TextInput, Button } from '@mantine/core'; import { useForm } from '@mantine/form'; function Demo() { const form = useForm({ initialValues: { email: '' }, validate: { email: (value) => (/^\S+@\S+$/.test(value) ? null : 'Invalid email'), }, }); return ( <div> <form onSubmit={form.onSubmit((values) => console.log(values))}> <TextInput withAsterisk label="Email" placeholder="[email protected]" {...form.getInputProps('email')} /> <Button type="submit">Submit</Button> </form> </div> ); } ``` `useForm` 的最大好處是它的助手,例如 `validate` 函數,它接收輸入到每個輸入的值,然後允許您建立驗證規則。 例如,如果您有一個電子郵件輸入,您可能有一個正則表達式來確定它實際上是否是一個有效的電子郵件(如您在上面的程式碼中所見)。如果沒有,那麼您可以顯示一條錯誤訊息並阻止提交表單。 ![使用表格](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n13l1gqejzx6y227p0j2.gif) 您如何獲取已輸入到表單中的值? Mantine 提供了一個非常方便的助手,叫做“getInputProps”,你只需提供你正在使用的輸入的名稱(比如電子郵件),它就會自動設置一個 onChange 來追蹤你在表單中輸入的值. 此外,為了處理表單提交,並在其值未通過驗證規則時阻止送出,它有一個特殊的 `onSubmit` 函數,您可以將其包裹在常規的 onSubmit 函數中。除了應用驗證規則之外,它還會負責在表單事件上呼叫 `preventDefault()`,這樣您就不必手動執行此操作。 我只用了這個鉤子的基本功能,但我強烈建議您在下一個專案中使用它。傳統上,表單很難正常工作,尤其是需要驗證和可見錯誤訊息的表單。 `useForm` 讓它變得異常簡單! --- 以上,簡單分享,希望對您有幫助。