前端-JS学习笔记-事件监听

三种事件监听方式,事件流,事件对象,EventTarget类,事件委托,鼠标事件,键盘事件,表单元素事件,Document事件,CSS事件

1. 认识事件(Event)

  • Web页面需要经常和用户进行交互,而交互的过程中可能想要捕捉这个交互的过程

    • 比如用户点击了某个按钮、用户在输入框里面输入了某个文本、用户鼠标经过了某个位置
    • 浏览器需要搭建一条JavaScript代码和事件之间的桥梁
    • 当某个事件发生时,让JavaScript可以相应(执行某个函数),所以需要针对事件编写处理程序(handler)
  • 如何进行事件监听呢?

    • 事件监听方式一:在script中直接监听
    • 事件监听方式二:通过元素的on来监听事件
    • 事件监听方式三:通过EventTarget中的addEventListener来监听

2. 事件监听

2.1. 在script中直接监听

1
2
3
4
5
6
7
8
<div onclick="console.log('被点击')"></div>

<div onclick="divClick()"></div>
<script>
function divClick(){
console.log("被点击");
}
</script>

2.2. 通过元素的on来监听事件

  • 事件 唯一:同一个元素同一个事件只能设置一个处理函数,最后注册的处理函数将会覆盖前面注册的处理函数
1
2
3
btn.onclick = function(){

}

2.3. 通过addEventListener来监听

  • 事件 不唯一:同一个元素同一个事件可以设置多个处理函数
  • 调用格式eventTarget.addEventListener(type,Listener,useCapture)
    • type:事件类型字符串,比如:click,mouseover
    • listener:事件处理函数,事件发生时,会调用该监听函数
    • useCapture:可选参数,是一个布尔值,默认是false
1
btn.addEventListener("click",function(){})

3. 解绑事件

  • 可以采用两种方法
    • btn.onclick=null
    • btn.removeEventListener(type,Listener,useCapture)
      • 这里的事件处理函数不能使用匿名函数

4. 认识事件流

4.1. 认识事件流的由来

  • 事实上对于事件有一个概念叫做事件流,为什么会产生事件流呢?
    • 在浏览器上对着一个元素点击时,点击的不仅仅是这个元素本身
    • 这是因为HTML元素是存在父子元素叠加层级的
    • 比如一个span元素是放在div元素上的,div元素是放在body元素上的,body元素是放在html元素上的
    • 所以,事件流就是页面中接收事件的顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<div class="box">
<span class="word">Hello World</span>
</div>
<script>
var box = document.querySelector(".box");
var span = box.querySelector(".word");

span.addEventListener("click",function(){
console.log("span被点击");
});
box.addEventListener("click",function(){
console.log("div被点击");
});
document.body.addEventListener("click",function(){
console.log("body被点击");
});
</script>

4.2. 事件冒泡和事件捕获

  • 默认情况下事件是从最内层的span向外依次传递的顺序,这个顺序称之为事件冒泡(Event Bubble)
  • 事实上,还有另外一种监听事件流的方式就是从外层到内层(body -> span),这种称之为事件捕获(Event Capture)
    • 捕获:document -> html -> body -> span
    • 冒泡:span -> body-> html -> document
  • 为什么会产生两种不同的处理流呢?
    • 这是因为早期浏览器开发时,不管是IE还是Netscape公司都发现了这个问题,但是他们采用了完全相反的事件流来对事件进行了传递
    • IE采用了事件冒泡的方式,Netscape采用了事件捕获的方式
  • addEventListener(type,Listener,useCapture)
    • 第三个参数如果是true,表示在捕获阶段调用事件处理程序
    • 如果是false,或者不填,表示在冒泡阶段调用事件处理程序
  • ⚠️注意
    1. js代码只能执行捕获或冒泡其中的一个阶段
      • 如果同时有事件冒泡和时间捕获的监听,那么会优先监听到事件捕获
    2. 实际开发中少用事件捕获,更关注事件冒泡
    3. 有些事件没有冒泡,onblur,onmouseenter,onmouseleave

4.3. 事件捕获和冒泡的过程

  • 捕获阶段(Capturing phase)

    • 事件(从 Window)向下走近元素
  • 目标阶段(Target phase)

    • 事件到达目标元素
  • 冒泡阶段(Bubbling phase)

    • 事件从元素上开始冒泡
  • 事实上,可以通过event对象来获取当前的阶段

    • eventPhase

5. 事件对象event

  • 当一个事件发生时,就会有和这个事件相关的很多信息

    • 比如事件的类型是什么,点击的是哪一个元素,点击的位置是哪里等等相关的信息
    • 这些信息会被封装到一个Event对象中,这个对象由浏览器创建,称之为event对象
    • 该对象提供了想要的一些属性,以及可以通过该对象进行某些操作
  • 如何获取这个event对象呢?

    • event对象会在传入的事件处理(event handler)函数回调时,被系统传入
    • 可以在回调函数中拿到这个event对象
    1
    2
    3
    4
    span.addEventListener("click",function(event){
    // PointerEvent {isTrusted: true, pointerId: 1, width: 1, height: 1, pressure: 0, …}
    console.log("span被点击",event);
    });

5.1. 常见的属性

1
2
3
btn.onclick=function(e){
e = e || window.event;
}
  • window.event ie678使用
  • e对象代表事件的状态,比如键盘按键的状态,鼠标的位置,鼠标按钮的状态
  • e变量中存放了跟事件相关的一系列信息数据的集合
事件对象属性 说明
this 绑定事件的对象
srcElement 触发事件的对象,ie678,非标准
type 对象的类型,click,mouseover,…,不带on
target 触发事件的对象,标准
currentTarget 当前处理事件的元素
eventPhase 事件所处的阶段:捕获1,冒泡3,目标2
offsetX、offsetY 事件发生在元素内的位置
clientX、clientY 事件发生在客户端内的位置
pageX、pageY 事件发生在客户端相对于document的位置,滚动情况下和client值不同
screenX、screenY 事件发生相对于屏幕的位置

5.2. 常见的方法

事件对象方法 说明
preventDefault() 阻止默认事件,标准
stopPropagation() 阻止冒泡,标准
cancelBubble 阻止冒泡,ie678
returnValue 阻止默认事件,ie678,非标准

兼容

1
2
3
4
5
6
7
8
9
10
11
12
var btn = document.querySelector("button");
//传统注册方式
btn.onclick=function(e){
//普通浏览器
e.preventDefault();
//ie678
e.returnValue;
//没有兼容性问题,但会阻止其下代码的执行
return false;
//在return false情况下不会执行
alert(123);
}

5.3. 事件处理中的this

  • 在函数中,可以通过this来获取当前的发生元素

    1
    2
    3
    span.addEventListener("click",function(event){
    console.log(this === event.currentTarget);
    });
  • 这是因为在浏览器内部,调用event handler是绑定到当前的target上的

6. EventTarget类

  • 所有的节点、元素都继承自EventTarget

    • 事实上Window也继承自EventTarget
  • 那么这个EventTarget是什么呢?

    • EventTarget是一个DOM接口,主要用于添加、删除、派发Event事件
  • Window继承自EventTarget,所以会继承其中的属性和方法

    • addEventListener:注册某个事件类型以及事件处理函数
    • removeEventListener:移除某个事件类型以及事件处理函数
    • dispatchEvent:派发某个事件类型到EventTarget上
  • 默认事件监听:https://developer.mozilla.org/zh-CN/docs/Web/Events

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const clickHandler = function() {
console.log("window发生了点击");
};
window.addEventListener("click", clickHandler);
window.removeEventListener("click", clickHandler);


const btn = document.querySelector("button");
btn.onclick = function () {
// 派发某个事件类型到EventTarget上
window.dispatchEvent(new Event("why"));
};
window.addEventListener("why", function() {
console.log("监听到why事件");
});

7. 事件委托

  • 事件冒泡在某种情况下可以实现强大的事件处理模式 – 事件委托模式(event delegation)

  • 那么这个模式是怎么样的呢?

    • 因为当子元素被点击时,父元素可以通过冒泡可以监听到子元素的点击
    • 并且可以通过event.target获取到当前监听的元素
  • 案例:一个ul中存放多个li,点击某一个li会变成红色

    • 方案一:监听每一个li的点击,并且做出相应

    • 方案二:在ul中监听点击,并且通过event.target拿到对应的li进行处理

      • 因为这种方案并不需要遍历后给每一个li上添加事件监听,所以它更加高效
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      <style>
      .active {
      color: red;
      }
      </style>

      <ul>
      li*6{$}
      </ul>

      <script>
      var ul = document.querySelector(".list");
      var currentActive = null;

      ul.addEventListener("click",function(event){
      if (currentActive && event.target !== this) {
      currentActive.classList.remove("active");
      }
      if (event.target !== this) {
      event.target.classList.add("active");
      currentActive = event.target;
      }
      });
      </script>
  • 事件委托的标记

    • 某些事件委托可能需要对具体的子组件进行区分,这个时候可以使用data-*对其进行标记
    • 比如多个按钮的点击,区分点击了哪一个按钮
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <div class="list">
    <div data-action = "new">新建</div>
    <div data-action = "search">搜索</div>
    <div data-action = "delete">删除</div>
    <div>其他</div>
    </div>
    <script>
    var list = document.querySelector(".list");
    list.addEventListener("click",function(event){
    var action = event.target.dataset.action;
    switch(action) {
    case "new":
    console.log("新建");
    break;
    case "search":
    console.log("搜索");
    break;
    case "delete":
    console.log("删除");
    break;
    default:
    console.log("action")
    }
    });

8. 常见的事件类型

8.1. 鼠标事件 MouseEvent

  • 常见的鼠标事件(不仅仅是鼠标设备,也包括模拟鼠标的设备,比如手机、平板电脑)
属性 说明
click 当用户点击某个对象时调用的事件句柄
contextmenu 在用户点击鼠标右键打开上下文菜单时触发
dbclick 当用户双击某个对象时调用的事件句柄
mousedown 鼠标按下触发
mouseup 鼠标按键被松开触发
mouseover 鼠标移到某元素之上触发,会冒泡,影响父盒子
mouseout 鼠标从某元素移开触发,会冒泡,影响父盒子
mouserenter 鼠标移到某元素之上触发,不会冒泡,不影响父盒子
mouserleave 鼠标从某元素移开触发,不会冒泡,不影响父盒子
mousemove 鼠标移动触发
  • mouseenter和mouseleave
    • 不支持冒泡
    • 进入子元素依然属于在该元素内,没有任何反应
  • mouseover和mouseout
    • 支持冒泡
    • 进入元素的子元素时
      • 先调用父元素的mouseout
      • 再调用子元素的mouseover
      • 因为支持冒泡,所以会将mouseover传递到父元素中

8.2. 键盘事件 keyEvent

属性 说明
keyup 某个键盘按键被松开时触发
keydown 某个键盘按键被按下时触发
keypress 某个键盘按键被按下时触发,不识别功能键,ctrl,shift,箭头等
  • 执行顺序:keydown,keypress,keyup

    • down事件先发生
    • press发生在文本被输入
    • up发生在文本输入完成
  • 可以通过key和code来区分按下的键

    • code:“按键代码”(“KeyA”,”ArrowLeft” 等),特定于键盘上按键的物理位置
    • key:字符(“A”,”a” 等),对于非字符(non-character)的按键,通常具有与 code 相同的值)
  • 案例:搜索

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    var inputEl = document.querySelector("input");

    // 搜索
    inputEl.onkeyup = function(event) {
    if(event.code === 'Enter') {
    console.log("进行搜索功能", inputEl.value);
    }
    }

    // 按键s - 搜索框进行自动聚焦
    document.onkeyup = function(event) {
    if(event.code === 'KeyS') {
    inputEl.focus();
    }
    }

8.3. 表单元素事件

属性 说明
change 该事件在表单元素的内容改变时触发( input,keygen,select,textarea)
input 元素获取用户输入时触发
focus 元素获取焦点时触发
blur 元素失去焦点时触发
reset 表单重置时触发
submit 表单提交时触发

8.4. window事件

属性 说明
DOMContentLoaded 浏览器已完全加载 HTML,并构建了 DOM 树,但像 img元素和样式表之类的外部资源可能尚未加载完成。ie9+支持
load 浏览器不仅加载完成了 HTML,还加载完成了所有外部资源:图片,样式等
pageshow 页面显示时触发,无论页面是否来自缓存
resize 调整窗口大小
hashchange 地址栏hash值改变

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div class="box">hello world</div>
<img src="./cover.png" alt="">

<script>
window.addEventListener("DOMContentLoaded",function(){
var img = document.querySelector("img");
console.log("页面内容加载完毕", img.offsetWidth, img.offsetHeight);
});

window.addEventListener("load",function(){
var img = document.querySelector("img");
console.log("页面所有内容加载完毕", img.offsetWidth, img.offsetHeight);
});
</script>

8.5. CSS事件

属性 说明
transitionend 当一个 CSS 动画完成时
transitionstart 当一个 CSS 动画开始时
本文结束  感谢您的阅读