DOM事件模型和事件委托(下) 事件委托 当监听子元素时,事件冒泡会通过目标元素向上传递到父级,直到document,如果子元素不确定或者动态生成,可以通过监听祖先素来取代监听子元素。
通俗理解:儿子办不成或难办成的事找爹办,爹也办不成的找爷爷办,总之谁有能力办成就找谁办。
情形一:给多个按钮添加点击事件,具体哪个按钮不确定 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 情形一: <div class ="div1" > <button > click 1</button > <button > click 2</button > <button > click 3</button > <button > click 4</button > <button > click 5</button > <button > click 6</button > <button > click 7</button > <button > click 8</button > <button > click 9</button > <button > click 10</button > <button > click 11</button > <button > click 12</button > <button > click 13</button > <button > click 14</button > <button > click 15</button > <button > click 16</button > <button > click 17</button > <button > click 18</button > <button > click 19</button > <button > click 20</button > </div >
e.target是button,但操作哪个button并不确定,此时可以监听父元素div
document .querySelector('.div1' ).addEventListener('click' , (e ) => { const t = e.target; if (t.tagName.toLowerCase() === 'button' ){ console .log('button被点击了' ); console .log('button内容是:' + t.textContent); } });
情形二:监听原本不存在的元素
假设div的子元素button要在一段时间之后才生成,那也不能够直接监听button,此时也可以使用事件委托。
setTimeout (() => { const button = document .createElement('button' ); button.textContent = 'click 21' ; div2.appendChild(button); }, 500 ); div2.addEventListener('click' , (e ) => { const t = e.target; if (t.matches('button' )) { console .log('新添加的button被点击' ); } });
情形三:使用自定义函数监听不存在的元素
自定义函数on()可以实现事件委托。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 function on (eventType, element, selector, fn ) { if (!(element instanceof Element)) { element = document .querySelector(element); } element.addEventListener(eventType, e => { let el = e.target; while (!el.matches(selector)) { if (element === el) { el = null ; break ; } el = el.parentNode; } el && fn.call(el, e, el); }) return element; }setTimeout (() => { const button = document .createElement('button' ); const span = document .createElement('span' ); span.textContent = 'click 22' ; button.appendChild(span); div3.appendChild(button); }, 500 ); on.call('' , 'click' , '#div3' , 'button' , () => { console .log('自定义函数on()被触发' ); });
可以将on封装到jQuery中,使用更方便:
以下为封装jQuery的基本方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 window .$ = window .jQuery = function (selectorOrArrayOrTemplate ) { let elements; if (typeof selectorOrArrayOrTemplate === 'string' ) { if (selectorOrArrayOrTemplate[0 ] === '<' ) { elements = [createElement(selectorOrArrayOrTemplate)]; } else { elements = document .querySelectorAll(selectorOrArrayOrTemplate); } } else if (selectorOrArrayOrTemplate instanceof Array ) { elements = selectorOrArrayOrTemplate; } function createElement (string ) { if (typeof string === 'string' ) { const container = document .createElement('template' ); container.innerHTML = string.trim(); return container.content.firstChild; } else { return 'Error! please enter a string.' ; } } const api = Object .create(jQuery.prototype); Object .assign(api, { elements : elements, oldApi : selectorOrArrayOrTemplate.oldApi, }); return api; }; jQuery.fn = jQuery.prototype = { constructor : jQuery, jQuery : true , on (eventType, element, selector, fn ) { if (!(this .element instanceof Element)) { this .element = document .querySelector(element); } this .element.addEventListener(eventType, e => { let el = e.target; while (!el.matches(selector)) { if (element === el) { el = null ; break ; } el = el.parentNode; } el && fn.call(el, e, el); }) return this ; } }setTimeout (() => { const button = document .createElement('button' ); const span = document .createElement('span' ); span.textContent = 'click 22' ; button.appendChild(span); div4.appendChild(button); }, 500 ); $('#div4' ).on('click' , '#div4' , 'button' , () => { console .log('自定义jQuery的on()函数被触发' ); });
优点:
TL;DR 面试题 请简述事件委托。
答:
事件委托就是把事件监听放在祖先元素(如父元素、爷爷元素)上。 好处是:1 节约监听数量 2 可以监听动态生成的元素。