js 事件笔记

一、事件简述

1、事件概念

在Web中, 事件在浏览器窗口中被触发,执行事先绑定的事件处理器(也就是事件触发时会运行的代码块),对事件做出响应。 用户在浏览器的任何一个操作都会去触发一个事件,JavaScript采用异步事件驱动编程模型,当文档、浏览器、元素或与之相关对象发生特定事情时,浏览器会产生事件。

2、常见的事件

事件是某个行为或者触发,比如点击、鼠标移动、提交表单,滚动菜单等等

二、事件流

1、事件流的作用

事件流描述的是从页面中接收事件的顺序,比如有两个嵌套的div,点击了内层的div,这时候是内层的div先触发click事件还是外层先触发?

如果事件不传播,我们无法确定我们点击的对象是什么?

2、事件流三种模型

  • 2.1事件冒泡模型

事件开始时由最具体的元素接收,然后逐级向上传播到较为不具体的元素。比如点击div时,首先是div先监听到了点击事件,然后向上传播到body/html/document

  • 2.2事件捕获模型

和事件冒泡相反,事件最开始由最外层不太具体的节点先监听到,然后向下传递到最具体的元素。 比如点击div事件,先是document监听到,然后分发到html/body/div

  • 2.3DOM事件流

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

  • 事件捕获阶段
  • 处于目标阶段
  • 事件冒泡阶段

3、用代码演示dom事件流

demo

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>JS Bin</title>
<style>
.box1{
  border:1px solid black;
  padding:10px;
}
</style>
</head>
<body>
<div class="container box1">
  container
  <div class="box box1">
    box
    <div class="target box1">target</div>
  </div>
</div>
</body>
<script>
  
 var container= document.querySelector('.container')
var box= document.querySelector('.box')
var target=document.querySelector('.target')

target.addEventListener('click',function(){
  console.log('target in 捕获')
},true)
box.addEventListener('click',function(){
  console.log('box in 捕获')
},true)
container.addEventListener('click',function(){
  console.log('container in 捕获')
},true)

target.addEventListener('click',function(){
  console.log('target in 冒泡')
},false)
box.addEventListener('click',function(){
  console.log('box in 冒泡')
},false)
container.addEventListener('click',function(){
  console.log('container in 冒泡')
},false) 
  
</script>
</html>

执行结果:

![图片上传中...]

三、事件处理程序(事件侦听器(listener))

1、概念

事件处理程序:事件触发后,执行响应对应事件的程序。 事件处理程序是预先设定的,我们需要提前定义好某些事件发生了该怎么处理,这个过程叫做绑定事件处理程序

2、JavaScript指定事件处理程序

2.1原理: JavaScript指定事件处理程序就是把一个函数赋值给一个元素的事件处理程序属性(如onclick) 2.2绑定的过程: 选中元素,选中事件处理程序属性如onclick,给属性赋值一个处理函数。

<input id="btnClick" type="button" value="Click Here" />

<script >
    var btnClick = document.getElementById('btnClick');
    btnClick.onclick = function showMessage() {
        alert(this.id);
    };
</script>

2.3不足: 不能给同一个元素的同一个事件处理程序属性绑定多个事件处理函数,会产生覆盖的。

3、DOM2事件处理程序

3.1简介 DOM2事件处理程序可以解决不能绑定多个事件处理函数的问题 DOM2级事件定义了两个方法用于处理指定和删除事件处理程序的操作:

  • addEventListener
  • removeEventListener

3.2 addEventListener使用 addEventListener有三个参数

  • 事件类型
  • 事件处理方法
  • 布尔参数,如果是true表示在捕获阶段调用事件处理程序,如果是false,则是在事件冒泡阶段处理。默认时false,没有特殊需求,第三个参数可以不写

3.3举个栗子

<input id="btnClick" type="button" value="Click Here" />

<script >
    var btnClick = document.getElementById('btnClick');
    btnClick.addEventListener('click', function() {
        alert(this.id);
    }, false);
</script>

总结:addEventListener 和制定事件处理程序的不同,一个是对属性赋值,另外一个addEventListener是执行一个函数,可以多次执行

3.4 removeEventListener解绑事件 通过addEventListener添加的事件处理程序只能通过removeEventListener移除,移除时参数与添加的时候相同 添加的匿名函数无法移除

<input id="btnClick" type="button" value="Click Here" />

<script >
    var btnClick = document.getElementById('btnClick')

    var handler=function() {
        console.log("hhhhhhhhh")
    }

    btnClick.addEventListener('click', handler, false)
    btnClick.removeEventListener('click', handler, false)
</script>

四、事件对象

1、事件对象的来源

在触发DOM上的某个事件的时候会产生一个事件对象event,这个对象包含着所有与事件有关的信息,包括产生事件的元素、事件类型等相关信息。

2、event的常见属性

event对象包含与创建它的特定事件有关的属性和方法,触发事件的类型不同,可用的属性和方法也不同,但是所有事件都会包含

2.1bubbles: 默认为false,表示事件对象是否冒泡。 如果该属性为false,div.addEventListener方法在冒泡阶段监听不会触发。只能写成div.addEventListener('click', callback, true)在“捕获阶段”监听这个事件。

2.2cancelable: 默认为false,表示事件是否可以被取消.只有为true的时候,才能用Event.preventDefault()取消这个事件。

2.3preventDefault 阻止默认事件

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

结果

<form action="/login">
    <input type="text" name="username">
    <input type="submit">
</form>
<script>
 document.querySelector('form').addEventListener('submit',function(e){
  e.preventDefault()
  if(document.querySelector('input[name=username]').value==='sjz'){
    this.submit()
  }
})   
</script>

结果

2.4target和currenttarget 在事件处理程序内部,this始终等同于currentTarget,currentTarget为绑定事件的元素,而target是为触发事件的实际目标。 当存在嵌套的时候,两者不一样,具体详情可以见这篇文章链接描述,或者中文版event.target 和 event.currentTarget。我这里不做赘述

2.5stopPropagation() 阻止事件在 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){
    //e.stopPropagation()
    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>

结果 没有给捕获阶段的box加e.stopPropagation()的结果

给捕获阶段的box加e.stopPropagation()之后的结果

五、阻止事件代理

哈哈哈写事件代理前,找到了这篇事件代理的文章用例子解释事件模型和事件代理,这里写事件模型的历史也写得相当棒,所以先就转载过来了。

1、事件代理的原理:

利用事件模型的传播性质,将子元素的监听函数绑定到父元素上,通过事件传播去执行监听函数。

2、举个栗子

需求:给container里面所有box都绑定点击事件,点击时输出box的值 2.1方式一:foreach 原理:选中.box所有元素,得到一个类数组对象,遍历这个类数组对象,给.box元素一一绑click事件。 代码:

 <div class="container">
    <div class="box">box1</div>
    <div class="box">box2</div>
    <div class="box">box3</div>
  </div>

<script>
function $(selector){
  return document.querySelector(selector)
}
function $$(selector){
  return document.querySelectorAll(selector)
}

// $$('.box').forEach(function(node){
//   node.onclick = function(){
//     console.log(this.innerText)
//   }
// })

结果

缺点:执行foreach选中的box时固定的,如果我们后续再加上几个box,后加的box就没有绑定上点击事件。 代码链接

2.2方式二事件代理 原理:给container绑定点击事件,通过e.target获取点击事件目标 代码:

 <div class="container">
    <div class="box">box1</div>
    <div class="box">box2</div>
    <div class="box">box3</div>
  </div>
  <button id="add">add</button>

<script>
function $(selector){
  return document.querySelector(selector)
}
function $$(selector){
  return document.querySelectorAll(selector)
}

$('.container').onclick = function(e){
  console.log(this)  
  console.log(e.target)
  if(e.target.classList.contains('box')){//contain少写s
    console.log(e.target.innerText)
  }
}

var i = 4
$('#add').onclick = function(){
  var box = document.createElement('div')
  box.classList.add('box')
  box.innerText = 'box' + (i++)
  $('.container').appendChild(box)//box加''
}

遇坑:

  • 1、contain少写s
  • 2、appendChild('box')//box加''是错的,box本身是变量

执行结果

六、常见的事件类型

常见事件类型

解析

click

单击

dblclick

双击

focus

焦点,比如表单input把光标放上去开始输入的时刻

blur

失去焦点,比如输入完成切换到下一个输入框时,就失去了焦点

keyup

按键按下松开的时候触发,

change

比如input失去焦点并且值发生了改变

submit

表单提交的时候触发

scroll

页面滚动的时候触发,注意使用函数节流

resize

页面面积变化触发,注意使用函数节流

DOMContentLoaded

DOM 结构解析完成,不用等图片解析

load

页面所有资源(图片css 等)加载完成触发,触发时间比较晚

mouseover

鼠标放上去触发,注意进入元素的子元素会重复触发

mouseout

鼠标拿出去触发,注意离开元素的子元素会重复触发

mouseenter

鼠标进入触发,进入子元素不会触发,比较常用

mouseleave

鼠标离开触发,离开子元素不会触发,比较常用

演示代码:直接复制代码到编辑器,在浏览器去测试这些事件 或者点击这个链接测试

 <button id="btn">点我</button>
  <button id="btn1">点我1</button>
  <div class="ct" style="font-size: 20px">
    <div class="box">hello</div>
  </div>    

  <div class="ct1">
    <div class="box1"></div>
  </div>  
  <input id="input-name" type="text">


  <form id="form" action="/upload">
    <input  id="username" name="username" type="text">
    <p class="msg"></p>
    <input id="btn-submit" type="submit" value="注册">
  </form>

    <img src="https://jirengu.com/data/upload/2017/0118/17/587f39fba695a.png" alt="">


  <script>

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


  $('#btn').addEventListener('click', function(){
    console.log('click')
    console.log(this)
  })
  $('#btn1').addEventListener('dblclick', function(){
    console.log('dblclick')
    console.log(this)
  })

  $('.ct').addEventListener('mouseover', function(){
    console.log('mouseover')
    console.log(this)
    // this.style.borderColor = 'blue'

    this.classList.add('hover')
  })
  $('.ct').addEventListener('mouseout', function(){
    console.log('mouseout...')
    // this.style.borderColor = 'red'
    this.classList.remove('hover')
  })

  $('.ct1').addEventListener('mouseenter', function(){
    console.log('mouseenter...')
    //this.style.borderColor = 'blue'
    this.classList.add('hover')
  })
  $('.ct1').addEventListener('mouseleave', function(){
    console.log('mouseleave...')
    //this.style.borderColor = 'blue'
    this.classList.remove('hover')
  })



   $('#input-name').addEventListener('focus', function(){
     console.log('focus...')
     console.log(this.value)
   })
   $('#input-name').addEventListener('blur', function(){
     console.log('blur...')
     console.log(this.value)
   })

   $('#input-name').addEventListener('keyup', function(e){
     console.log('keyup...')
     console.log(this.value)
     console.log(e)
     this.value = this.value.toUpperCase()
   })

   $('#input-name').addEventListener('change', function(e){
     console.log('change...')
     console.log(this.value)
     console.log(e)
     this.value = this.value.toUpperCase()
   })



  $('#form').addEventListener('submit', function(e){
    e.preventDefault();
    if(/^\w{6,12}$/.test($('#username').value)){
      $('#form').submit();
    }else{
      $('#form .msg').innerText = '出错了'
      $('#form .msg').style.display = 'block'
      console.log(' no submit...');
    } 
  })


  window.addEventListener('scroll', function(e){
    console.log('scroll..')
  })
  window.addEventListener('resize', function(e){
    console.log('resize..')
  })


  //页面所有资源加载完成
  window.onload = function(){
    console.log('window loaded')
  }

  //DOM 结构解析完成
  document.addEventListener('DOMContentLoaded', function(){
    console.log('DOMContentLoaded ')
  })

    console.log($('img').width) //0
    $('img').onload = function(){
        console.log(this.width)   //此时才能得到图片的真实大小
    }


  </script>
  <style>
  body{
    color: blue;
  }
  .ct,.ct1{
    width: 100px;
    height: 100px;
    border: 1px solid red;
    background-color: yellow;
    margin: 20px;
  }
  .box,.box1{
    width: 50px;
    height: 50px;
    background-color: blue;
  }
  .ct.hover, .ct1.hover{
    border-color: blue;
    background-color: pink;
  }

  .box3{
    list-style: none;
    background: yellow;
    margin: 0;
    padding: 0;
  }
  .box3>li{
    background: pink;
    margin: 5px;
    padding: 10px;
  }
  .box3>li.hover{
    background-color: blue;
  }
  .msg{
    display: none;
  }

  </style>

七、自定义事件

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', '你好')

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券