事件

1.解释以下概念:事件传播机制、阻止传播、取消默认事件、事件代理

  • 基础

    什么是事件
    JavaScript和HTML的交互是通过事件实现的。事件是某个行为或者触发,比如点击、鼠标移动,图片加载等。
    什么是事件流
    事件流描述的是从页面中接收事件的顺序。当用户点击了一个有嵌套关系的元素时,那是先点击的是用户本身想要点击的被嵌套的元素,还是嵌套元素的父元素?这里有三种事件传播的模型:事件冒泡,事件捕获,DOM事件流。
    1.事件冒泡:事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素

    事件冒泡

    2.事件捕获:不太具体的节点更早接收事件,而最具体的元素最后接收事件,和事件冒泡相反(ie低版本没有捕获)
    事件捕获

    3.DOM事件流:DOM2级事件规定事件流包括三个阶段,事件捕获阶段,处于目标阶段,事件冒泡阶段,首先发生的是事件捕获,为截取事件提供机会,然后是实际目标接收事件,最后是冒泡阶段
    DOM事件流

  • 事件传播机制

当一个事件发生以后,它会在不同的DOM节点之间传播。这种传播分为三个阶段:


事件传播机制
  • 第一阶段,“捕获阶段”。:事件从document开始向下传,一直传到触发的目标节点上,在这个过程中会依次检测是否有节点绑定了事件的监听函数,如果有就会执行。
  • 第二阶段,目标阶段:事件到达目标节点,并且触发监听函数。
  • 第三阶段,冒泡阶段:从目标节点依次上传,直到document,再依次判断是否有节点绑定了监听函数。
    这种三阶段的传播模型,会使得一个事件在多个节点上触发,比如会在捕获阶段和冒泡阶段在一个节点上执行两次。
  • 阻止传播

如果有特殊情况,需要事件在某个节点上停止传播,就可以使用stopPropagation()取消事件进一步捕获或冒
泡,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上新定义的事件监听函数

document.querySelector('#box') = function(e){
e.stopPropagation() //阻止传播
}
  • 取消默认事件

取消浏览器对当前事件的默认行为,像有些元素时有默认的事件的,比如<a>标签,在点击的时候默认会打开所含的超链接。如果想要自定义点击<a>链接的操作,就可以通过preventDefault()取消事件默认行为

document.querySelector('a') = function(e){
e.preventDefault() //阻止默认事件
}
  • 事件代理

由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数统一处理。指定一个事件处理程序,就可以管理某一类型的所有事件

document.querySelector('ul').addEventListener('click', function(event){
    if(event.target.tagName.toLowerCase() === 'li'){   //或者if(e.target.classList.contains('box'))用class来选择
        //监听ul下面的所有li
    }
})

如果之后再在ul里面添加li,监听函数也可以监听新增的li

2.写一个 Demo,演示事件传播的过程,演示阻止传播的效果

捕获阶段还是冒泡阶段,可以通过addEventListener第三个参数cancelable属性来控制。默认是falsse为冒泡阶段,可以设置为true变成捕获阶段

html+css
<style> 
    .container,
    .box,
    .target{
      border: 1px solid;
      padding: 10px;
    }  
  </style>
  <button id="btn">click</button>

  <div class="container">
    container
    <div class="box">
      box
      <div class="target">target</div>
    </div>
  </div>
js
<script>
  var $ = function(e){
    return document.querySelector(e)
  }
  $('.container').addEventListener('click',function(){
    console.log('container 捕获阶段')
  },true)
  $('.box').addEventListener('click',function(){
    console.log('box 捕获阶段')
  },true)
  $('.target').addEventListener('click',function(){
    console.log('target 捕获阶段')
  },true)
   $('.target').addEventListener('click',function(){
    console.log('target 冒泡阶段')
  },false)
  $('.box').addEventListener('click',function(){
    console.log('box 冒泡阶段')
  },false)
  $('.container').addEventListener('click',function(){
    console.log('container 冒泡阶段')
  },false)
//正常点击target  输出
//container 捕获阶段
//box 捕获阶段
//target 捕获阶段
//target 冒泡阶段
//box 冒泡阶段
//container 冒泡阶段
</script>
如果想让事件在 box冒泡阶段 停止
<script>
  var $ = function(e){
    return document.querySelector(e)
  }
  $('.container').addEventListener('click',function(){
    console.log('container 捕获阶段')
  },true)
  $('.box').addEventListener('click',function(){
    console.log('box 捕获阶段')
  },true)
  $('.target').addEventListener('click',function(){
    console.log('target 捕获阶段')
  },true)
   $('.target').addEventListener('click',function(){
    console.log('target 冒泡阶段')
  },false)
  $('.box').addEventListener('click',function(e){
    e.stopPropagations()  //这里不能用this 
    console.log('box 冒泡阶段')
  },false)
  $('.container').addEventListener('click',function(){
    console.log('container 冒泡阶段')
  },false)
//container 捕获阶段
//box 捕获阶段
//target 捕获阶段
//target 冒泡阶段
//box 冒泡阶段
</script>

3.解释DOM2事件传播机制

如果用单独的事件绑定,比如

document.querySelector('.button').onclick = function(){
  console.log('1')
}
document.querySelector('.button').onclick = function(){
  console.log('2')
}
//2

想让.button在被点击的时候触发两个事件,此时第二个监听函数会覆盖第一个,最后的输出 结果只有2
这个时候不要把onclick当成一个触发时间,要把它看成一个“属性”,那么后面的“属性”会覆盖掉前面的“属性”

<ul>
  <li class="box">box1</li>
  <li class="box">box2</li>
</ul>
document.querySelectorAll('.box').onclick = function(){
    console.log(this.innerText)
  }
//一个都输出不了

因为选择的是一个数组,没有onclick,除非用数组的forEach去遍历每一个数组,但是写起来又特别的麻烦

DOM2级事件定义了两个方法用于处理指定和删除事件处理程序的操作:
addEventListener
removeEventListener
所有的DOM节点都包含这两个方法,并且它们都接受三个参数:
1.事件类型
2.事件处理方法
3.布尔参数,如果是true表示在捕获阶段调用事件处理程序,如果是false,则是在事件冒泡阶段处理
这时,想要实现上面的效果就很容易了

document.querySelector('.button').addEventListener('click', function(){
  console.log('1')
},false)
document.querySelector('.button').addEventListener('click', function(){
  console.log('2')
},false)
//1
//2

4.补全代码

要求:

  • 当点击按钮开头添加时在<li>这里是</li>元素前添加一个新元素,内容为用户输入的非空字符串;当点击结尾添加时在最后一个 li 元素后添加用户输入的非空字符串.
  • 当点击每一个元素li时控制台展示该元素的文本内容。
<ul class="ct">
  <li>这里是</li>
  <li>饥人谷</li>
  <li>任务班</li>
</ul>
<input class="ipt-add-content" placeholder="添加内容"/>
<button id="btn-add-start">开头添加</button>
<button id="btn-add-end">结尾添加</button>
<script>
//你的代码
</script>
<script>
  var $ = function(e){
    return document.querySelector(e)
  }
  
  var ct = $('.ct')
  var addstr = $('#btn-add-start')
  var addend = $('#btn-add-end')
  var input = $('.ipt-add-content')
  
  addstr.onclick = function(){
    if(input.value !== ''){
      var box = document.createElement('li')
      box.innerText = input.value
      ct.insertBefore(box, ct.firstChild)
    }
  }
  
  addend.onclick = function(){
    if(input.value !== ''){
      var box = document.createElement('li')
      box.innerText = input.value
      ct.appendChild(box)
    }
  }
  
  $('.ct').addEventListener('click', function(s){   //监听点击
    if(s.target.tagName.toLowerCase() === 'li'){
      console.log(s.target.innerText)
    }
  })
</script>

5.onlick与addEventListener的区别

  • onclick事件在同一时间只能指向唯一对象
  • addEventListener给一个事件注册多个listener
  • addEventListener对任何DOM都是有效的,而onclick仅限于HTML
  • addEventListener可以控制listener的触发阶段(捕获/冒泡)。对于多个相同的事件处理器,不会重复触发,不需要手动使用removeEventListener清除
  • IE9使用attachEvent和detachEvent
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,591评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,448评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,823评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,204评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,228评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,190评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,078评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,923评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,334评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,550评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,727评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,428评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,022评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,672评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,826评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,734评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,619评论 2 354

推荐阅读更多精彩内容

  • 以下文章为转载,对理解JavaScript中的事件处理机制很有帮助,浅显易懂,特分享于此。 什么是事件? 事件(E...
    jxyjxy阅读 3,035评论 1 10
  • 事件是一种异步编程的实现方式,本质上是程序各个组成部分之间的通信。DOM支持大量的事件,本节介绍DOM的事件编程。...
    许先生__阅读 938评论 0 3
  • 事件是一种异步编程的实现方式,本质上是程序各个组成部分之间的通信。DOM支持大量的事件,本节介绍DOM的事件编程。...
    周花花啊阅读 593评论 0 3
  • 一、问答 1. dom对象的innerText和innerHTML有什么区别? innerHTML: 也就是从对象...
    饥人谷_罗伟恩阅读 596评论 0 2
  • 前言 本文主要介绍: DOM事件级别 DOM事件流 DOM事件模型 事件代理 Event对象常见的方法和属性 一、...
    xyyojl阅读 1,205评论 0 3