理解瀏覽器事件模型css
understandEventModel.html 代碼:html
<!DOCTYPE HTML> <html> <head> <title>Understand Event Model</title> <meta charset="UTF-8" /> <link rel="stylesheet" href="../css/main.css"/> <style> img { display: block; margin: auto; } </style> </head> <body> <img id="example" src="images/example.jpg" alt="A bolt of lightning" onclick="console.log('At ' + formatDate(new Date()) + ' BOOM!');" /> <p>The output is printed on the console</p> <!-- 在 onsubmit 中調用返回 false 的方法不能阻止默認動做 --> <!-- <form id="myForm" action="www.cnblogs.com" onsubmit="doSubmit()"> --> <form id="myForm" action="www.cnblogs.com" onsubmit="return false"> <div> <label for="name">Name:</label> <input id="name" name="name" type="text" /> </div> <div> <button type="submit">Click me!</button> </div> </form> <form id="myForm2" action="www.cnblogs.com" onsubmit="prevent()"> <div> <label for="name2">Name:</label> <input id="name2" name="name2" type="text" /> </div> <div> <button type="submit">Click me2!</button> </div> </form> <div id="dom2"> <button id="multipleButton" type="button">multiple events</button> </div> <div id="firstLevel"> <div id='secondLevel'> <input id="testButton" type="button" value="捕獲和冒泡" /> </div> </div> <script src="js/understandEventModel.js"></script> </body> </html>
understandEventModel.js:node
/* DOM Level 用來表示實現 W3C DOM 規範的級別要求 DOM Level 0 Event Model,在這個模型背後,事件處理器經過把函數賦值給元素的屬性來聲明實現 */ function formatDate(date) { return (date.getHours() < 10 ? '0' : '') + date.getHours() + ':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes() + ':' + (date.getSeconds() < 10 ? '0' : '') + date.getSeconds() + '.' + (date.getMilliseconds() < 10 ? '00' : (date.getMilliseconds() < 100 ? '0' : '')) + date.getMilliseconds(); } /* id 爲 example 的圖片在 HTML 代碼中直接在標籤屬性 onclick 中聲明瞭一個 click 事件,這個屬性值會被使用建立一個 匿名函數體,相似下面的代碼,直接在標籤屬性中聲明事件不是推薦的作法 */ document.getElementById('example').onmouseover = function(event) { console.log(this); console.log('At ' + formatDate(new Date()) + ' Crackle!'); //當觸發事件處理器時,在全部兼容標準的瀏覽器裏,一個名爲 Event 的對象會做爲第一參數傳遞給處理器,只有 IE9 以上, //Firefox,Chrome, Safari 和 Opera 支持, IE8 以前的瀏覽器能夠經過本身的方式把 Event 對象傳給全局的 event 屬性 event = event || window.event; //獲取目標元素的引用,也就是觸發事件的元素,必須經過兼容標準瀏覽器的 target 屬性獲取,可是舊的 IE 使用 //的是 srcElement var target = event.target || event.srcElement; }; /* 事件冒泡 目標元素有機會處理事件以後,瀏覽器的事件處理機制會檢查元素的父元素是否包含此事件類型的處理器,若是有也會調用,再 依次檢查爺爺元素,直到 DOM 樹的頂部 */ var elements = document.getElementsByTagName('*'); //獲得頁面上的全部元素 for(var i = 0; i < elements.length; i++){ if(elements[i].id != 'example'){ (function(current){ current.onclick = function(event){ event = event || window.event; var target = event.target || event.srcElement; console.log('At ' + formatDate(new Date()) + ' For ' + current.tagName + '#' + current.id + ' target is ' + target.tagName + '#' + target.id); } })(elements[i]); } } /* 要阻止事件的傳播,在現代瀏覽器中能夠調用 Event 實例的 stopPropagation() 方法 在舊的 IE 瀏覽器中能夠設置 Event 實例的 cancelBubble 屬性爲 true 實現,如今不少新的瀏覽器也提供了該屬性,儘管 這不是 W3C 標準(我以爲這個實踐中仍是不用的好,知道做用就好) 有些事件是默認動做,好比 form 的 submit,要取消這些語義動做,在新的瀏覽器調用 Event 實例的 preventDefault() 方法。舊的 IE 中沒有此方法,設置 returnValue 屬性的值爲 false;另外一種方法是處理器代碼返回 false <form name="myForm" onsubmit="return false;" ... > */ /* 被頁面的 onsubmit 屬性引用,但沒有成功阻止默認的 submit 動做 */ function doSubmit(){ return false; } /* 阻止了默認的 submit 動做 */ function prevent(){ event = event || window.event; event.preventDefault(); } /* DOM Level 2 event model DOM Level 0 event model 的缺點是每一個元素每次只能註冊一種特定類型的事件處理器,主要是由於屬性用來存儲 事件處理器函數的引用 DOM Level 2 event model 就是爲了解決上面的問題而創建的。這裏的事件處理器經過標準的元素方法而不是元素屬性賦值 來實現,每一個 DOM 元素都實現了一個 addEventListener() 方法。 addEventListener(eventType, listener, useCapture) eventType: 要處理的事件類型,DOM 0 中使用的事件,好比 click listener:表示元素事件處理器的函數 useCapture:布爾值 */ var multipleButton = document.getElementById('multipleButton'); /* 這個 multipleButton 按鈕在下面代碼增長事件處理器以前,上面的代碼中增長了 click 事件是處理函數,從結果能夠看出, 這個處理函數有被保留,並且是做爲第一個觸發 event.stopPropagation() 隨便寫在哪一個處理函數中效果都同樣,不會事件冒泡到父元素 */ multipleButton.addEventListener('click', function(event){ console.log('At ' + formatDate(new Date()) + ' BOOM once!'); //event.stopPropagation(); }); multipleButton.addEventListener('click', function(event){ console.log('At ' + formatDate(new Date()) + ' BOOM twice!'); //event.stopPropagation(); }); multipleButton.addEventListener('click', function(event){ console.log('At ' + formatDate(new Date()) + ' BOOM thrice!'); event.stopPropagation(); }); /* DOM Level 2 event model 中的事件傳播 事件首先從 DOM 樹根結點向目標元素傳播,而後從目標元素向 DOM 根節點冒泡傳播。前面的階段成爲捕獲階段,後者 是冒泡階段 addEventListener(eventType, listener, useCapture) 方法中的 useCapture 是用來標記函數做爲什麼種處理器使用的,false 爲 冒泡階段處理器,true 爲捕獲階段處理器,默認爲 false */ /*function eventLog(curElement, event){ event = event || window.event; console.log('At ' + formatDate(new Date()) + ' Capture for ' + curElement.tagName + '#' + curElement.id ) + ' target is ' + event.target.tagName + '#' + event.target.id; }*/ var firstLevel = document.getElementById('firstLevel'); var secondLevel = document.getElementById('secondLevel'); var testButton = document.getElementById('testButton'); firstLevel.onclick = function(){}; secondLevel.onclick = function(){}; testButton.onclick = function(){}; //捕獲階段運行 firstLevel.addEventListener('click', function(event){ console.log('At ' + formatDate(new Date()) + ' Capture for ' + this.tagName + '#' + this.id ) + ' target is ' + event.target.tagName + '#' + event.target.id; }, true); firstLevel.addEventListener('click', function(event){ console.log('At ' + formatDate(new Date()) + ' Bubble for ' + this.tagName + '#' + this.id ) + ' target is ' + event.target.tagName + '#' + event.target.id; }, false); secondLevel.addEventListener('click', function(event){ console.log('At ' + formatDate(new Date()) + ' Capture for ' + this.tagName + '#' + this.id ) + ' target is ' + event.target.tagName + '#' + event.target.id; }, true); secondLevel.addEventListener('click', function(event){ console.log('At ' + formatDate(new Date()) + ' Bubble for ' + this.tagName + '#' + this.id ) + ' target is ' + event.target.tagName + '#' + event.target.id; }, false); testButton.addEventListener('click', function(event){ console.log('At ' + formatDate(new Date()) + ' Capture for ' + this.tagName + '#' + this.id ) + ' target is ' + event.target.tagName + '#' + event.target.id; }, true); testButton.addEventListener('click', function(event){ console.log('At ' + formatDate(new Date()) + ' Bubble for ' + this.tagName + '#' + this.id ) + ' target is ' + event.target.tagName + '#' + event.target.id; }, false);
jQuery 事件模型jquery
jQueryEventModel.html:數組
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Events in jQuery Example - jQuery in Action, 3rd edition</title> <link rel="stylesheet" href="../css/main.css"/> <style> img { display: block; margin: auto; } #wrapper { border: 1px solid #3A5895; padding: 10px; } #address:focus { border: 3px solid #000000; } .outer { position: relative; width: 200px; height: 100px; border: 1px solid #000000; background-color: #55AA55; } .inner { width: 50%; height: 50%; margin: 0.5em auto; border: 1px solid #000000; background-color: #FFFF4F; } #outer2 { margin-top: 2em; } </style> </head> <body> <img id="example" src="images/example.jpg" alt="A bolt of lightning"/> <p> <button id="btn1">Click me!</button> <button id="btn2">Don't click me!</button> </p> <ul id="myList"></ul> <p> <button id="btn">Does nothing</button> <button id="btn-attach">Attach handler</button> <button id="btn-remove">Remove handler</button> </p> <p> <div id="foo"> Trigger part </div> </p> <div id="wrapper"> <button id="btnTrigger">Click me!</button> <button id="anotherBtn">Trigger custom event</button> <input type="text" id="address" /> </div> <div class="outer" id="outer1"> Outer 1 <div class="inner" id="inner1">Inner 1</div> </div> <div class="outer" id="outer2"> Outer 2 <div class="inner" id="inner2">Inner 2</div> </div> <p> <button id="btnTestNamespacing">Test Namespacing</button> <button id="btn-remove-by-namespacing">Remove by namespacing</button> </p> <p>The output is printed on the console</p> <script src="../js/jquery-3.2.1.js"></script> <script src="js/jQueryEventModel.js"></script> </body> </html>
jQueryEventModel.js:瀏覽器
/* jQuery Event Model 1. 爲建立事件處理器提供統一方法 2. 容許每一個元素、每一個事件類型註冊多個方法 3. 適用標準事件名稱,如 click 或 mouseover 4. 傳遞 Event 實例做爲第一個參數 5. 規範事件實例中最經常使用的屬性 6. 爲事件取消和阻止操做提供統一的方法 除了不支持捕獲階段,jQuery 事件模型幾乎完美支持 DOM Level 2 Event Model */ function formatDate(date) { return (date.getHours() < 10 ? '0' : '') + date.getHours() + ':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes() + ':' + (date.getSeconds() < 10 ? '0' : '') + date.getSeconds() + '.' + (date.getMilliseconds() < 10 ? '00' : (date.getMilliseconds() < 100 ? '0' : '')) + date.getMilliseconds(); } /* on(eventType[, selector][, data], handler) on(eventHash[, selector][, data]) 爲選擇的元素的一個或多個事件添加一個或多個事件處理器 eventType(String):事件類型的名稱,多個事件類型能夠用空格分隔 selector(String):過濾器用來過濾選中元素的子元素,這些子元素觸發事件 data(Any):傳遞給 Event 實例的數據,賦值給 data 屬性 handler(Function):事件處理器的函數,冒泡階段的當前元素做爲函數上下文,false 值表示函數 return false eventHash(Object):單個調用中爲多個事件類型創建處理器的對象。屬性名區分事件類型,屬性值提供事件處理器 返回 jQuery 集合 */ $('#example') .on('click', function (event) { console.log('At ' + formatDate(new Date()) + ' BOOM once!'); }) .on('click', function (event) { console.log('At ' + formatDate(new Date()) + ' BOOM twice!'); }) .on('click', function (event) { console.log('At ' + formatDate(new Date()) + ' BOOM thrice!'); }) .on('mouseenter mouseleave', function(event){ console.log(event.type) }); $('#btn1').on('click', function(){ console.log('The button is clicked!'); }).on('mouseenter mouseleave', myFunctionHandler); /* 使用 eventHash 參數類型的代碼 */ $('#btn2').on({ click: function(){ console.log('Oh no, you clicked me!'); }, mouseenter: myFunctionHandler, mouseleave: myFunctionHandler }); function myFunctionHandler(event){ event.stopPropagation(); console.log(event.type + ' ' + event.target.id); } /* 注意這裏的第二個參數,設置了 data 參數後,能夠經過 event 參數的 data 屬性訪問 */ $('#btn1').on('click', { name: 'Martin_Fu' }, function(event){ console.log(event.data.name + ' clicked the button!'); }); /* 事件委託(event delegation) 這是一種向元素的父元素添加事件處理器的重要技術,能夠爲不存在的元素添加事件處理器 */ $('<li>item1</li>').add($('<li>item2</li>')).appendTo('#myList'); /* 原生 JS 寫法 */ document.getElementById('myList').addEventListener('click', function(event){ if(event.target.nodeName === 'LI'){ console.log('List item: ' + (Array.prototype.indexOf.call(document.getElementById('myList').children, event.target) + 1)); } }); /* 這裏注意第二個參數,它會對子元素進行篩選 事件委託的優點不僅侷限於爲不存在的元素執行事件處理器,更能夠節省內存和時間。好比 myList 下有不少 <li> ,那麼須要循環 添加事件處理器,若是 <li> 元素不少,那麼會耗費很多時間,因爲 JS 是單線程的,會致使很差的用戶體驗。 但也不能由於這個緣由給一個元素(好比 document )添加過多處理器,一樣影響性能,建議儘量爲離目標元素近的元素添加處理器 */ $('#myList').on('click', 'li', function(event){ console.log('List item(jQuery): ' + ($(this).index() + 1)); }); /* one(eventType[, selector][, data], handler) one(eventHash[, selector][, data]) 爲選擇的元素的一個或多個事件添加一個或多個事件處理器,一旦執行,事件處理器會自動刪除 返回 jQuery 集合 */ /* off(eventType[, selector][, data], handler) off(eventHash[, selector][, data]) 刪除參數指定的 jQuery 對象中全部元素的事件處理器,若是沒有提供參數,那麼刪除全部的元素處理器 返回 jQuery 集合 */ /* 這裏注意一點,若是屢次點擊 btn-attach 按鈕,btn 按鈕會添加兩個 click 事件,可是隻要點擊一次 btn-remove 按鈕,這兩個事件都會移除 */ var $btn = $('#btn'); var counter = 1; function logHandler(event){ console.log('Click ' + counter); counter++; } $('#btn-attach').on('click', function(event){ $btn.on('click', logHandler).text('Log'); }); $('#btn-remove').on('click', function(event){ $btn.off('click', logHandler).text('Does nothing'); }); /* 觸發事件 有時候,系統使用腳本控制事件的觸發和執行過程 調用做爲處理器的函數不會致使語義動做或者冒泡傳播發生 */ /* trigger(eventType[, data]) 爲全部匹配的元素調用傳遞事件類型創建任意事件處理器和行爲,全力模擬事件被觸發,包括 DOM 層級傳播和語義動做執行 eventType(String|jQuery.Event):指定要調用的事件類型的名字,也能夠包含命名空間;還能夠傳遞 jQuery.Event data(Any):傳遞給處理器的數據,若是是數組,傳遞的元素會做爲不一樣的參數 返回 jQuery 集合 */ $('#foo').on('click', function(event, par1, par2, par3){ console.log(event.type + "_" + event.target.id); console.log(par1, par2, par3); }); /* 這裏要注意的是參數傳遞的是數組 */ $('#foo').trigger('click', [1, 2, 3]); /* triggerHandler(eventType[, data]) 爲匹配的元素(只是集合中的第一個元素)調用創建的事件處理器,沒有出現冒泡傳播、語義動做或者實時事件 eventType(String|jQuery.Event):指定要調用的事件類型的名字,也能夠包含命名空間;還能夠傳遞 jQuery.Event data(Any):傳遞給處理器的數據,若是是數組,傳遞的元素會做爲不一樣的參數 返回最後一個處理器的返回值,若是沒有返回值,返回 undefined */ $('#wrapper') .on('focus', function(event){ console.log('Div focused!'); }) .on('click', function(event){ console.log('Div clicked!'); }); $('#address') .on('focus', function(event){ console.log('Input focused!'); }) .on('click', function(event){ console.log('Input clicked!'); }).triggerHandler('focus'); $('#btnTrigger') .on('click', function(event){ console.log('Button clicked!'); }) .trigger('click'); /* 快捷方式 編寫 on() 和 off() 完整語法不少時候會變得煩人,所以 jQuery 提供了快捷方式 eventName([data, ] handler) 經過方法名爲指定事件使用的函數創建事件處理器,支持的方法以下: blur focusout mouseleave resize change keydown mousemove scroll click keypress mouseout select dbclick keyup mouseover submit focus mousedown mouseup focusin mouseenter ready data(Any):做爲 Event 實例的 data 屬性傳遞給處理器的數據 handler(Function):做爲事件處理器的函數 返回 jQuery 集合 */ $('#btnTrigger') .dblclick(function(){ console.log('Button double clicked!'); }) .dblclick(); /* hover(enterHandler, leaveHandler) hover(handler) 爲匹配元素的 mouseenter 和 mouseleave 事件創建處理器 enterHandler(Function):做爲 mouseenter 處理器的函數 leaveHandler(Function):做爲 mouseleave 處理器的函數 handler(Function):對於 mouseenter 和 mouseleave 事件都會調用的單個處理器 */ function report(event) { event.stopPropagation(); console.log(event.type + ' on ' + event.target.id); } $('#outer1').on('mouseenter mouseleave', report); $('#inner1').on('mouseenter mouseleave', report); $('#outer2').hover(report); $('#inner2').hover(report); /* 自定義事件 */ $('#btnTrigger').on('customEvent', function(){ console.log('Custom event invoked!') }); $('#anotherBtn').click(function(){ $('#btnTrigger').trigger('customEvent'); }); /* 爲事件添加命名空間 事件是後綴命名空間,用圓點分割 */ $('#btnTestNamespacing') .on('click.editMode.myApp', function(){ console.log('Click event under editMode namespacing invoked!'); }) .on('mouseenter.editMode', function(){ console.log('Mouseenter event under editMode namespacing invoked!'); }) .on('mouseleave.myApp', function(){ console.log('Mouseleave event under editMode namespacing invoked!'); }); $('#btn-remove-by-namespacing').click(function(){ $('#btnTestNamespacing').off('.editMode'); })