Javascript与HTML之间的交互是通过事件来实现的。事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间。可以使用侦听器来预定事件,以便事件发生时执行相应的代码。

事件流

事件流 是指从页面接收事件的顺序。有意思的是,IE和网景提出不同的事件流概念。IE支持的事件流是事件冒泡,网景公司支持的事件流是事件捕获。

事件冒泡

IE的事件流叫做事件冒泡,即事件 由内向外 找监听函数。我们来看下面的代码:

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <title>Document</title>
  5. </head>
  6. <body>
  7. <div id="div1">click Me</div>
  8. </body>
  9. </html>

如果单击了页面的

元素,click事件会按照以下顺序传播:

  • document

传播的过程是从div到document传播,逐级向上传递。

事件捕获

网景团队的事件流叫 事件捕获 ,即事件 由外向内 找监听函数。以前面的代码为例,监听的顺序是从document向

传播。顺序如下:

  • document
  • DOM事件流

    “DOM2级事件”规定的事件流包括三个阶段: 事件捕获阶段,处于目标阶段、事件冒泡阶段 。首先发生的是事件捕获,为截取事件提供机会;然后是实际的目标接收到事件,最后是冒泡阶段。
    image.png
    接下来,我们来做个事件先捕获再冒泡的小练习吧:
    html代码:
    1. <!DOCTYPE html>
    2. <html>
    3. <head>
    4. <meta charset="utf-8">
    5. <title></title>
    6. </head>
    7. <body>
    8. <div class="level1 x">
    9. <div class="level2 x">
    10. <div class="level3 x">
    11. <div class="level4 x">
    12. <div class="level5 x">
    13. <div class="level6 x">
    14. <div class="level7 x"></div>
    15. </div>
    16. </div>
    17. </div>
    18. </div>
    19. </div>
    20. </div>
    21. </body>
    22. </html>
    css代码
    1. *{
    2. box-sizing:border-box;
    3. }
    4. div[class^=level]{
    5. border:1px solid;
    6. border-radius:50%;
    7. display:inline-flex;
    8. }
    9. .level1{
    10. padding:10px;
    11. background:purple;
    12. }
    13. .level2{
    14. padding:10px;
    15. background:blue;
    16. }
    17. .level3{
    18. padding:10px;
    19. background:cyan;
    20. }
    21. .level4{
    22. padding:10px;
    23. background:green;
    24. }
    25. .level5{
    26. padding:10px;
    27. background:yellow;
    28. }
    29. .level6{
    30. padding:10px;
    31. background:orange;
    32. }
    33. .level7{
    34. width:50px;
    35. height:50px;
    36. border:1px solid;
    37. background:red;
    38. border-radius:50%;
    39. }
    40. .x{
    41. background:transparent;
    42. }
    Javascript代码 ```javascript const level1=document.querySelector(‘.level1’) const level2=document.querySelector(‘.level2’) const level3=document.querySelector(‘.level3’) const level4=document.querySelector(‘.level4’) const level5=document.querySelector(‘.level5’) const level6=document.querySelector(‘.level6’) const level7=document.querySelector(‘.level7’)

let n=1; let fn=((e)=>{ const t=e.currentTarget setTimeout(()=>{ t.classList.remove(‘x’) },n1000) n+=1 }) let fn2=((e)=>{ const t=e.currentTarget setTimeout(()=>{ t.classList.add(‘x’) },n1000) n+=1 }) level1.addEventListener(‘click’,fn,true) level1.addEventListener(‘click’,fn2) level2.addEventListener(‘click’,fn,true) level2.addEventListener(‘click’,fn2) level3.addEventListener(‘click’,fn,true) level3.addEventListener(‘click’,fn2) level4.addEventListener(‘click’,fn,true) level4.addEventListener(‘click’,fn2) level5.addEventListener(‘click’,fn,true) level5.addEventListener(‘click’,fn2) level6.addEventListener(‘click’,fn,true) level6.addEventListener(‘click’,fn2) level7.addEventListener(‘click’,fn,true) level7.addEventListener(‘click’,fn2)

  1. 最终的结果如下所示:<br />![捕获冒泡.gif](https://cdn.nlark.com/yuque/0/2020/gif/2519598/1602079164890-fce24e0f-b86e-4e55-8eb6-67dcbca6a7a7.gif#align=left&display=inline&height=266&margin=%5Bobject%20Object%5D&name=%E6%8D%95%E8%8E%B7%E5%86%92%E6%B3%A1.gif&originHeight=266&originWidth=266&size=102070&status=done&style=none&width=266)<br />可以看出,图示是先捕获再冒泡的。
  2. <a name="ZzAvo"></a>
  3. ## 事件代理
  4. "事件代理"即是把原本需要绑定的事件委托给父元素,让父元素担当事件监听的职务<br />,事件代理的原理是DOM元素的事件冒泡,使用事件代理的好处是可以提高性能。<br />事件代理的使用场景如下:<br />场景一: 要给100个按钮添加点击事件,怎么办?<br />答:监听这100个按钮的祖先,等冒泡的时候判断target是不是这100个按钮中的一个。<br />代码实现:<br />HTML代码如下:
  5. ```html
  6. <!DOCTYPE html>
  7. <html>
  8. <head>
  9. <meta charset="utf-8">
  10. <title></title>
  11. </head>
  12. <body>
  13. <div id="div1">
  14. <span>span1</span>
  15. <button>click1</button>
  16. <button>click2</button>
  17. <button>click3</button>
  18. <button>click4</button>
  19. <button>click5</button>
  20. <button>click6</button>
  21. <button>click7</button>
  22. //一直到<button>click100</button>
  23. <button>click100</button>
  24. </div>
  25. </body>
  26. </html>

javascript代码如下:

  1. div1.addEventListener('click',(e)=>{
  2. const t=e.target
  3. if(t.tagName.toLowerCase()==='button'){
  4. console.log('botton被点击了')
  5. console.log('button内容是'+t.textContent)
  6. }
  7. })

使用场景二:要监听目前不存在的元素的点击事件,怎么办?
监听祖先,等点击的时候看看是不是想要监听的元素。
HTML代码如下:

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="utf-8">
  5. <title></title>
  6. </head>
  7. <body>
  8. <div id="div1"></div>
  9. </body>
  10. </html>

js代码如下:

  1. setTimeout(()=>{
  2. const button=document.createElement('button')
  3. button.textContent='click 1'
  4. div1.appendChild(button)
  5. },1000)
  6. div1.addEventListener('click',(e)=>{
  7. const t=e.target
  8. if(t.tagName.toLowerCase()==='button'){
  9. console.log('button 被click')
  10. }
  11. })