在實際開發中,經常需要為Dom元素綁定事件,如果頁面上有4個li元素,點擊對應的li,彈出對應的li內容,怎麼做呢?是不是很簡單? 大多數人的做法都是:獲取元素,綁定事件 如果頁面上有1w個元素, 甚至10w個元素呢? 繼續使用上述方式,會有很大的性能問題,這個時候,有人可能要問,實際中的項目 哪有 ...
在實際開發中,經常需要為Dom元素綁定事件,如果頁面上有4個li元素,點擊對應的li,彈出對應的li內容,怎麼做呢?是不是很簡單?
大多數人的做法都是:獲取元素,綁定事件
1 <ul> 2 <li>跟著ghostwu學習javascript設計模式的應用1</li> 3 <li>跟著ghostwu學習javascript設計模式的應用2</li> 4 <li>跟著ghostwu學習javascript設計模式的應用3</li> 5 <li>跟著ghostwu學習javascript設計模式的應用4</li> 6 </ul> 7 <script> 8 var aLi = document.querySelectorAll( "li" ); 9 aLi.forEach(( ele, ind ) => { 10 ele.addEventListener( "click", ()=> { 11 alert( ele.innerHTML ); 12 } ); 13 }); 14 </script>
如果頁面上有1w個元素, 甚至10w個元素呢?
繼續使用上述方式,會有很大的性能問題,這個時候,有人可能要問,實際中的項目 哪有1w個,10w個元素的。一般的項目可能沒有,但是社交類的網站,如微博,其他的如大批量文件上傳等能功能,都是需要動態創建dom元素,而且數量巨大,並且創建出來的dom元素,一般都需要綁定事件和相應的特效。一般情況下,用普通的綁定事件方式是不能給動態創建的dom元素綁定到事件的,所以這裡就產生了兩個問題:
1,當頁面元素很多的時候,如果給這些元素綁定上事件?
2,當動態創建dom時,如果給動態創建的dom綁定上事件和相應的特效?
這就是本文需要討論的模式-委托模式
採用事件委托可以順利解決上面2個問題
那麼,什麼是事件委托呢?
給元素的父元素綁定事件,利用冒泡原理,當子元素觸發事件的時候,會去觸發父元素的事件,然後把相應的業務邏輯放在父元素的事件中去處理。通俗點講就是,子元素不做事件綁定,把綁定事件的操作委托給父元素,這就叫做事件委托, 委托有一個特性,他能夠在事件觸發中,識別到具體是由哪個子元素觸發的,這個就是事件對象的target屬性
1 <ul> 2 <li>跟著ghostwu學習javascript設計模式的應用1</li> 3 <li>跟著ghostwu學習javascript設計模式的應用2</li> 4 <li>跟著ghostwu學習javascript設計模式的應用3</li> 5 <li>跟著ghostwu學習javascript設計模式的應用4</li> 6 </ul> 7 <script> 8 var oUl = document.querySelector("ul"); 9 oUl.addEventListener( "click", ( ev )=>{ 10 var oEvent = ev || event; 11 target = oEvent.target || oEvent.srcElement; 12 alert( target.innerHTML ); 13 }); 14 </script>
當我們點擊li的時候,就能通過事件對象oEvent.target識別到觸發事件的li元素, srcElement是相容ie的寫法。
在沒有事件委托之間,我們通過javascript做一個hover的功能,一般這麼做.
1 <ul> 2 <li>跟著ghostwu學習設計模式</li> 3 <li>跟著ghostwu學習設計模式</li> 4 <li>跟著ghostwu學習設計模式</li> 5 <li>跟著ghostwu學習設計模式</li> 6 <li>跟著ghostwu學習設計模式</li> 7 </ul> 8 <script> 9 var aLi = document.getElementsByTagName( "li" ); 10 for( var i = 0, len = aLi.length; i < len; i++ ){ 11 aLi[i].onmouseover = function(){ 12 this.style.backgroundColor = 'red'; 13 } 14 aLi[i].onmouseout = function(){ 15 this.style.backgroundColor = ''; 16 } 17 } 18 </script>
如果li元素很多,就會產生性能問題,而採用委托模式,我們可以這麼做
1 <ul> 2 <li>跟著ghostwu學習設計模式1</li> 3 <li>跟著ghostwu學習設計模式2</li> 4 <li>跟著ghostwu學習設計模式3</li> 5 </ul> 6 <script> 7 var aLi = document.getElementsByTagName("li"); 8 var oUl = document.getElementsByTagName( "ul" )[0]; 9 oUl.onmouseover = function( ev ){ 10 var oEvent = ev || event; 11 var target = oEvent.target || oEvent.srcElement; 12 if ( target.tagName.toLowerCase() == 'li' ) { 13 target.style.backgroundColor = 'red'; 14 } 15 } 16 oUl.onmouseout = function( ev ){ 17 var oEvent = ev || event; 18 var target = oEvent.target || oEvent.srcElement; 19 if ( target.tagName.toLowerCase() == 'li' ) { 20 target.style.backgroundColor = ''; 21 } 22 } 23 </script>
通過事件委托,把元素綁定到父元素,大大提高性能
至此,我們解決了第一個問題:當頁面元素很多的時候,如果給這些元素綁定上事件
對於新創建的dom元素,普通綁定事件的方式,是不能綁定到這些dom元素的
1 <input type="button" value="創建"> 2 <ul> 3 <li>ghostwu1</li> 4 <li>ghostwu2</li> 5 </ul> 6 <script> 7 var oBtn = document.getElementsByTagName( "input" )[0]; 8 var oUl = document.getElementsByTagName( "ul" )[0]; 9 var aLi = document.getElementsByTagName( "li" ); 10 oBtn.onclick = function(){ 11 var oLi = document.createElement( "li" ); 12 oLi.innerHTML = 'ghostwu'; 13 oUl.appendChild( oLi ); 14 } 15 for( var i = 0, len = aLi.length; i < len; i++ ){ 16 aLi[i].onmouseover = function(){ 17 this.style.backgroundColor = 'red'; 18 } 19 aLi[i].onmouseout = function(){ 20 this.style.backgroundColor = ''; 21 } 22 } 23 24 </script>
新創建的li元素,是不能綁定到onmouseover和onmouseout事件的,我們採用委托模式之後,可以這麼做
1 <input type="button" value="創建"> 2 <ul> 3 <li>ghostwu1</li> 4 <li>ghostwu2</li> 5 </ul> 6 <script> 7 var oBtn = document.getElementsByTagName("input")[0]; 8 var oUl = document.getElementsByTagName("ul")[0]; 9 var aLi = document.getElementsByTagName("li"); 10 oBtn.onclick = function () { 11 var oLi = document.createElement("li"); 12 oLi.innerHTML = 'ghostwu'; 13 oUl.appendChild(oLi); 14 } 15 oUl.onmouseover = function( ev ){ 16 var oEvent = ev || event; 17 var target = oEvent.target || oEvent.srcElement; 18 if ( target.tagName.toLowerCase() == 'li' ) { 19 target.style.backgroundColor = 'red'; 20 } 21 } 22 oUl.onmouseout = function( ev ){ 23 var oEvent = ev || event; 24 var target = oEvent.target || oEvent.srcElement; 25 if ( target.tagName.toLowerCase() == 'li' ) { 26 target.style.backgroundColor = ''; 27 } 28 } 29 </script>
至此,我們解決了第二個問題:當動態創建dom時,如果給動態創建的dom綁定上事件和相應的特效
最後,我們結合委托模式,來實戰一個微博發佈的功能,微博發佈之後,給動態創建的dom元素添加手風琴摺疊功能
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> <link href="https://cdn.bootcss.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div class="container"> <div class="row"> <div class="col-md-offset-3 col-md-6"> <form class="form-horizontal"> <div class="form-group"> <input id="title" type="text" class="form-control" placeholder="請輸入標題"> </div> <div class="form-group"> <textarea name="" id="txt" cols="30" rows="5" class="form-control"></textarea> </div> <div class="form-group"> <input type="button" value="發佈" class="btn btn-primary" id="btn-publish"> </div> </form> </div> <div class="col-md-offset-3 col-md-6" id="content"> </div> </div> </div> </body> <script> var oBtnPublish = document.getElementById("btn-publish"); var aTpl = [ '<div class="panel panel-success">', '<div class="panel-heading">', '<h4 class="panel-title">', '<a href="javascript:;">', '[title]', '</a>', '</h4>', '</div>', '<div class="panel-body">', '[content]', '</div>', '</div>' ]; var oContent = document.getElementById( "content" ), str = aTpl.join( "" ), title = '', content = '', panelParent = null; oBtnPublish.onclick = function(){ str = aTpl.join( "" ); title = document.getElementById( "title" ).value; txt = document.getElementById( "txt" ).value; str = str.replace( /\[title\]/, title ); str = str.replace( /\[content\]/, txt ); oContent.innerHTML += str; } oContent.onclick = function( ev ){ var oEvent = ev || event; var target = oEvent.target || oEvent.srcElement; if ( target.tagName.toLowerCase() == 'a' ) { panelParent = target.parentNode.parentNode.parentNode; if ( panelParent.children[1].style.display == "block" || panelParent.children[1].style.display == '' ) { panelParent.children[1].style.display = 'none'; } else { panelParent.children[1].style.display = 'block'; } } } </script> </html>