# JavaScript 網頁事件處理 ###### tags: `javascript` ## eventListener 事件監聽 ```javascript= const elements = document.querySelector('div') elements.addEventListener('click', onClick) function onClick(){ alert('Heyyyyy!') } } ``` [Introduction to events](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Building_blocks/Events#Event_bubbling_and_capture ) # callback function 回呼函式 當到餐廳要執行吃飯的動作,但櫃台姊姊說現在客滿哦~豬還再殺~要等40分鐘,你要不要留個電話等到有空位時再打給你,你在回來的時候就準備好空位置和烤乳豬了,就可以開始享用大餐。 辦公室電話響了 (事件被觸發 Event fired) -> 接電話 (處理事件 Event Handler) > 為何要用 callback function? 為了讓功能模組化,可新增更多功能,讓 function pointer 指到一個實際的函式,會在適當的時間呼叫此 function,把 function 當作參數傳進去使用,則此function就是所謂的 callback function。 callback 是「被動」等待某個條件產生才去執行你寫的 function, 這個條件通常指的是訊息的發生。比如說視窗上某個按鈕被按時, 才會去呼叫你寫的函式,便是「被動」等待 user 去按按鈕。 [為何要用 callback function](http://work.oknow.org/2016/04/callback-function.html) [重新認識 JavaScript: Day 18 Callback Function 與 IIFE](https://ithelp.ithome.com.tw/articles/10192739) [callback, promise, async/await 使用方式教學以及介紹 Part I](https://yu-jack.github.io/2018/07/22/promise/) ## 確保執行順序 為了確保先執行 funcA 再執行 funcB 我們在 funcA 加上 callback 參數 ```javascript= var funcA = function(callback){ var i = Math.random() + 1; window.setTimeout(function(){ console.log('function A'); // 如果 callback 是個函式就呼叫它 if( typeof callback === 'function' ){ callback(); } }, i * 1000); }; var funcB = function(){ var i = Math.random() + 1; window.setTimeout(function(){ console.log('function B'); }, i * 1000); }; // 將 funcB 作為參數帶入 funcA() funcA( funcB ); ``` 無論 funcA 在執行的時候要等到天荒地老海枯石爛, funcB 都會癡癡得等等到 console.log('function A'); 之後才執行。 ## 模組化 模組化前 ```javascript= let calc =function(num1, num2, calcType){ if(calcType ==='add'){ return num1+ num2 } } console.log(calc(7,8,'add')) ``` 模組化後 ```javascript= const add = function(a, b){ return a + b } const multiply = function(a, b){ return a * b } let calc = function(num1, num2, callback){ return callback(num1, num2) } console.log(calc(7, 8, add)) ``` ## event (e) ```javascript= // MouseEvent 顯示滑鼠選取的元素 'click' const element = document.querySelector('.unicorn') element.addEventListener('click', function(e){ console.log(e.target) }) // KeyboardEvent  顯示鍵盤輸入的鍵 'keydown' window.addEventListener('keydown', function(e){ console.log(e.key) }) ``` --- ## 變更背景的按鈕實作 1. 將 switch 按鈕在 DOM 樹裡的節點位置,儲存再變數 element 中 2. 註冊 click 按鈕事件發生時,執行匿名函式將 body 元素新增/移除 changeColor的 CSS 屬性。 3. click 變更 body 的背景,再 click 就恢復原本 body 的背景。 ```javascript= // CSS .changeColor{ background: pink; } //HTML <button class="switch">press</button> //JavaScript const element = document.querySelector('.switch') element.addEventListener('click', function(e){ document.querySelector('body').classList.toggle('changeColor') }) ``` --- ## 表單驗證邏輯 - 防止 2 次密碼不同的表單 ```javascript= //HTML <form action="" class="login"> <div>ueser:<input type="text"></div> <div>password:<input class='psw1'type="password"></div> <div>password again:<input class='psw2'type="password"></div> <input type="submit"> </form> <a href="https://hackmd.io/">HACK</a> //JavaScript const element = document.querySelector('.login') element.addEventListener('submit', function(e){ const psw1 = document.querySelector('.psw1') const psw2 = document.querySelector('.psw2') if (psw1.value !== psw2.value){ e.preventDefault() alert('passwords are diffrent!!!') } ``` > [name=Ying Chen Chung] > [What Are Event Listeners In JavaScript](https://www.youtube.com/watch?v=jqU3uaRgQyQ) ## 事件傳遞機制 當滑鼠點擊元件,會同時點及該元件的父元件。 ```javascript= //HTML <div class="outer"> <div class="inner"> <button class="btn">pork </button> </div> </div> //JavaScript function addEvent(className){ document.querySelector(className) .addEventListener('click',function(){ alert(className) }) } addEvent('.outer') addEvent('.inner') addEvent('.btn') ``` >回傳內容 >(click).btn => (捕獲) .outer -> .inner -> .btn ->(冒泡) .btn -> .inner -> .outer > ## 事件傳遞機制詳解:捕獲與冒泡 [DOM 的事件傳遞機制:捕獲與冒泡](https://blog.techbridge.cc/2017/07/15/javascript-event-propagation/) ```javascript= function addEvent(className){ document.querySelector(className) .addEventListener('click',function(){ console.log(className, '捕獲') }, true) document.querySelector(className) .addEventListener('click',function(){ console.log(className, '冒泡') }, false) } ``` - stopPropagation() ```javascript= document.querySelector('.btn') .addEventListener('click',function(e){ e.stopPropagation() console.log('.btn 冒泡') }) ``` ## 事件代理 原始 ```javascript= let num = 5 const element = document.querySelectorAll('.btn') for (var i = 0; i < element.length; i++){ element[i].addEventListener('click',function(e){ console.log(e.target.getAttribute('data-value')) }) } //動態新增按鈕 document.querySelector('.add').addEventListener('click', function(){ const btn = document.createElement('button') btn.setAttribute('data-value', num) btn.innerText =num num++ document.querySelector('.outer').appendChild(btn) } ) ``` 優化 自動加 addEventListener ```javascript= //HTML <div class="outer"> <button class="add">add</button> <button class="btn" data-value='1'>1</button> <button class="btn"data-value='2'>2</button> <button class="btn"data-value='3'>3</button> <button class="btn"data-value='4'>4</button> </div> //JS let num = 5 //動態新增按鈕 document.querySelector('.add').addEventListener('click', function(){ const btn = document.createElement('button') btn.setAttribute('data-value', num) btn.classList.add('btn') btn.innerText =num num++ document.querySelector('.outer').appendChild(btn) } ) document.querySelector('.outer').addEventListener('click', function(e){ if (e.target.classList.contains('btn')){ alert(e.target.getAttribute('data-value')) } } ) ``` ## 綜合示範:簡易密碼產生器 ```javascript= <div class="app"> <div><label><input type="checkbox" name="en" >English</label></div> <div><label><input type="checkbox" name="num">Number</label></div> <div><label><input type="checkbox" name="sp">Special</label></div> <div><button class="btn">產生</button></div> <div class="result"> [] </div> </div> //JS document.querySelector('.btn').addEventListener('click', function(){ let char = ''; if(document.querySelector('input[name=en]').checked){ char += 'abcdefghijklmnopqrstuvwxyz' } if(document.querySelector('input[name=num]').checked){ char += '0123456789' } if(document.querySelector('input[name=sp]').checked){ char += '~!@#$%^&*()_+' } console.log(char) let result =''; for(let i = 0; i < 10; i++){ const num = Math.floor(Math.random()*char.length) result += char[num] } document.querySelector('.result').textContent = result } ) ```