DOM事件

1.事件

JavaScript和HTML的交互是通过事件实现的。JavaScript采用异步事件驱动编程模型,当文档、浏览器、元素或与之相关对象发生特定事情时,浏览器会产生事件。如果JavaScript关注特定类型事件,那么它可以注册当这类事件发生时要调用的句柄:
如事件是某个行为或者触发,比如点击、鼠标移动;当用户点击鼠标时;当网页已加载时

2 DOM2事件传播机制

当事件发生在某个文档节点上时(即事件目标),目标的事件处理程序就会被触发。此外目标的每个祖先节点也有机会处理该事件。
2级DOM的事件传播包含三个阶段:
1.事件捕捉阶段(capturing),事件具体的节点更早接收事件,而最具体的元素最后接收事件;从顶级文档树节点一级一级向下遍历,直到到达该事件的目标节点。
2.到达事件的目标节点,执行目标节点的时间处理程序。
3.事件冒泡(bubbling),事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素;从目标节点一级一级向上上溯,直到顶级文档树节点

<!DOCTYPE html >
<html>
<head>
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <title>Test Page</title>
</head>
<body>
    <div>
        Click Here</div>
</body>
</html>
1.png

事件冒泡模型


2.png

事件捕获模型


3.png

DOM事件流
<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>

  <script>

  function $(selector){
    return document.querySelector(selector)
  }

  var btn = $('#btn')
  btn.onclick = function (e){
   console.log(e) 
  }
  btn.addEventListener('click', function(evt){
    console.log(this)
    console.log(btn)
    console.log(evt.target)
  })

  $('.container').addEventListener('click', function(e){
    console.log('contianer click.. in 捕获阶段')
  }, true)
  $('.box').addEventListener('click', function(e){
    console.log('box click.. in 捕获阶段')
  }, true)
  $('.target').addEventListener('click', function(e){
    console.log('target click.. in 捕获阶段')
  }, true)

  $('.container').addEventListener('click', function(e){
    console.log('contianer click.. in 冒泡阶段')
  }, false)
  $('.box').addEventListener('click', function(e){
    console.log('box click.. in 冒泡阶段')
  }, false)
  $('.target').addEventListener('click', function(e){
    console.log('target click.. in 冒泡阶段')
  }, false)    


  </script>
4.png

2.1DOM2事件处理程序

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

    var ul=document.querySelector('.ct');
    ul.addEventListener('click',function(e){
        console.log(e.target.innerText);
    });

2.3 阻止默认事件:

preventDafault方法取消浏览器对当前事件的默认行为,比如点击链接后,浏览器跳转到指定页面,或者按一下空格键,页面向下滚动一段距离。该方法生效的前提是,事件的cancelable属性为true如果为fales,则调用该方法没有任何效果。
比如我们可以阻止链接导航这一默认行为

<a href="http://baid.com">baidu</a>
<script>
  document.querySelector('a').onclick= function(e){
    e.preventDefault()
    console.log(this.href)
    if(/baidu.com/.test(this.href)){
      location.href = this.href
    }
  }
</script>

2.4 阻止传播

stopPropagation方法阻止事件在DOM中继续传播,即取消进一步的事件捕获或冒泡,防止再触发定义在别的节点上的监听函数,但是不包括在当前节点上新定义的事件监听函数。
我们可以在button的事件处理程序中调用stopPropagation()从而避免注册在body上的事件发生

<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>

  <script>

  function $(selector){
    return document.querySelector(selector)
  }

  var btn = $('#btn')
  btn.onclick = function (e){
   console.log(e) 
  }
  btn.addEventListener('click', function(evt){
    console.log(this)
    console.log(btn)
    console.log(evt.target)
  })

  $('.container').addEventListener('click', function(e){
    console.log('contianer click.. in 捕获阶段')
  }, true)
  $('.box').addEventListener('click', function(e){
    console.log('box click.. in 捕获阶段')
  }, true)
  $('.target').addEventListener('click', function(e){
    console.log('target click.. in 捕获阶段')
  }, true)

  $('.container').addEventListener('click', function(e){
    console.log('contianer click.. in 冒泡阶段')
  }, false)
  $('.box').addEventListener('click', function(e){
    e.stopPropagation();
    console.log('box click.. in 冒泡阶段')
  }, false)
  $('.target').addEventListener('click', function(e){
    console.log('target click.. in 冒泡阶段')
  }, false)    


  </script>

5.png

范例:
1.点击按钮会弹出面板
2.点击面板列表会在标题展示点击的内容
3.点击面板以外的区域面板会消失

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
  <style>
    section {
      width: 800px;
      margin: 0 auto;
      position: relative;
    }
    ul,li {
      margin: 0;
      padding: 0;
      list-style: none;
    }
    .card {
      position: absolute;
      border: 1px solid #ccc;
      width: 200px;
      display: none;
    }
    .card li {
      border-bottom: 1px solid #ccc;
      padding: 10px;
      cursor: pointer
    }
  
  </style>
</head>
<body>
  <section>
    <h1>你选择了 <span class="choice"></span></h1>
    <button class="btn">点我</button>
    <ul class="card">
      <li>菜单1</li>
      <li>菜单2</li>
      <li>菜单3</li>
    </ul>
  </section>
  
  <script>
    let choice = document.querySelector('.choice')
    let btn = document.querySelector('.btn')
    let card = document.querySelector('.card')
    let items = document.querySelectorAll('.card li')
    
    btn.onclick = function(e) {
       card.style.display = "block"
       e.stopPropagation()
    }
    
    items.forEach(item => {
      item.onclick = function(e) {
        choice.innerText = this.innerText
        e.stopPropagation()
      }
    })
    
    document.onclick = function() {
      card.style.display = "none"
    }
    
    
    
  </script>
</body>
</html>

效果:http://js.jirengu.com/jebew

2.5 事件代理

由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数统一处理多个子元素的事件。这种方法叫做事件的代理
定义:事件代理就是利用事件冒泡,只指定一个事件处理程序,就可以管理某一类型的所有事件。(delegation)。

var ul = document.querySelector('ul');
ul.addEventListener('click', function(event){
    if(event.target.tagName.toLowerCase() === 'li'){
        //...
    }
})

范例1:
当点击li的时候会在console.log打印输出

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
</head>
<body>

<ul class="ct">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
<script>
    var ul=document.querySelector('.ct');
    ul.addEventListener('click',function(e){
       console.log(e.target.innerText);//"1"
        console.log(this.innerText) //" 1 2 3"
    });
</script>
</body>
</html>

范例2:
1.当勾选 checkbox 时, 标题的我要学后要展示对应勾选的内容
2.当新增标签后,可勾选新增的标签,切勾选后仍展示到标题上

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title></title>
</head>
<body>
  <h1>我要学<span class="txt">____</span></h1>
  <ul class="box">
    <li>前端 <input type="checkbox" value="前端"></li>
    <li>Java <input type="checkbox" value="Java"></li>
    <li>PHP <input type="checkbox" value="PHP"></li>
  </ul>
  <div>
    <input type="text" class="ipt"> <button class="add"> 新增</button>
  </div>
  <script>
    let  checkboxs = document.querySelectorAll('.box [type=checkbox]')
    let addBtn = document.querySelector('.add')
    let text = document.querySelector('.txt')
    let input = document.querySelector('.ipt')
    let box = document.querySelector('.box')
    let values = []
      
    addBtn.onclick = function() {
      let node = document.createElement('li')
      node.innerText = input.value
      let checkbox = document.createElement('input')
      checkbox.type = "checkbox"
      checkbox.value = input.value
      box.appendChild(node)
      node.appendChild(checkbox)
      
    }
    box.onclick = function(e) {
      console.log(e.target.value)
        text.innerText = e.target.value
      //添加所有选中元素
//       text.innerText = [...document.querySelectorAll('.box [type=checkbox]')].filter(checkbox=>checkbox.checked)
//            .map(checkbox=>checkbox.value).join(',')
    }
  </script>
</body>
</html>

效果:http://js.jirengu.com/baqul

注意:使用事件代理(委托),之后,要有效的操作实际触发的dom节点,可使用.target方法,而不能用this,在这里this指绑定事件的元素,而不是实际触发的元素;

作用:以后再添加子节点,监听函数依然有效

3.1 自定义事件

var EventCenter = {
  on: function(type, handler){
    document.addEventListener(type, handler)
  },
  fire: function(type, data){
    return document.dispatchEvent(new CustomEvent(type, {
      detail: data
    }))
  }
}

EventCenter.on('hello', function(e){
  console.log(e.detail)
})

EventCenter.fire('hello', '你好')

3.2写一个通用的事件侦听器函数

// event(事件)工具集,来源:github.com/markyun
   markyun.Event = {

       // 视能力分别使用dom0||dom2||IE方式 来绑定事件
       // 参数: 操作的元素,事件名称 ,事件处理程序
       addEvent : function(element, type, handler) {
           if (element.addEventListener) {
               //事件类型、需要执行的函数、是否捕捉
               element.addEventListener(type, handler, false);
           } else if (element.attachEvent) {
               element.attachEvent('on' + type, function() {
                   handler.call(element);
               });
           } else {
               element['on' + type] = handler;
           }
       },
       // 移除事件
       removeEvent : function(element, type, handler) {
           if (element.removeEventListener) {
               element.removeEventListener(type, handler, false);
           } else if (element.datachEvent) {
               element.detachEvent('on' + type, handler);
           } else {
               element['on' + type] = null;
           }
       },
       // 阻止事件 (主要是事件冒泡,因为IE不支持事件捕获)
       stopPropagation : function(ev) {
           if (ev.stopPropagation) {
               ev.stopPropagation();
           } else {
               ev.cancelBubble = true;
           }
       },
       // 取消事件的默认行为
       preventDefault : function(event) {
           if (event.preventDefault) {
               event.preventDefault();
           } else {
               event.returnValue = false;
           }
       },
       // 获取事件目标
       getTarget : function(event) {
           return event.target || event.srcElement;
       }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,386评论 6 479
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,939评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,851评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,953评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,971评论 5 369
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,784评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,126评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,765评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,148评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,744评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,858评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,479评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,080评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,053评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,278评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,245评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,590评论 2 343

推荐阅读更多精彩内容

  • 一、问答 1. dom对象的innerText和innerHTML有什么区别? innerHTML: 也就是从对象...
    饥人谷_罗伟恩阅读 586评论 0 2
  •   JavaScript 与 HTML 之间的交互是通过事件实现的。   事件,就是文档或浏览器窗口中发生的一些特...
    霜天晓阅读 3,473评论 1 11
  • 一、dom对象的innerText和innerHTML有什么区别? innerHTML返回的是从对象起始位置到终止...
    __Qiao阅读 405评论 0 0
  • 1、keypath keypath的使用 调用了metamacro_argcount(详情可见ReactiveCo...
    林炜超阅读 343评论 0 0
  • 为了培养学生学习英语的兴趣,提高学生的学习积极性,南村校区举行了英语文化节活动,这是英语文化节活动(3)。英语单词...
    金子发光阅读 209评论 0 0