事件及事件绑定 && 事件对象及事件传播——基础知识(脑图梳理)

事件:是元素天生自带的默认操做行为,不论咱们是否给其绑定了方法,当咱们操做的时候,也会把对应的事件触发;javascript

事件绑定:是给元素的某个行为绑定一个方法,目的是当事件行为触发的时候,能够作一些事情css

1、浏览器经常使用的事件行为

思惟导图

一、鼠标事件

鼠标点击:鼠标按下弹起算一次点击html

  • onclick:点击(移动端click被识别为单击)
  • ondblclick:双击(大概是在 300ms 之间点击两次)
  • oncontextmenu:右键点击

鼠标按下:不分左右键或者滚轮,只要按下/抬起就会触发java

  • onmousedown:鼠标按下
  • onmouseup:鼠标抬起

鼠标滚动浏览器

  • onmousewhell:鼠标滚轮滚动

鼠标移动:鼠标尖端移动触发函数

  • onmousemove:鼠标移动

鼠标通过性能

  • onmouseout:鼠标滑出
  • onmouseover:鼠标滑过(在表面通过便可)

鼠标进入动画

  • onmouseenter:鼠标进入(进入到里面)
  • onmouseleave:鼠标离开

思考:onmouseover 和 onmouseenter 的区别

  • mouseover/mouseout 存在冒泡机制。划过和划出(鼠标在谁身上,至关于划过谁)
  • mouseenter/mouseleave 不存在冒泡传播机制。进入和离开。

如何选用:项目中,若是一个容器中有后代元素,想要鼠标进入和离开作啥事,咱们通常都用mouseenter和mouseleave。ui

二、键盘事件

能绑定键盘事件的有:inputtextareawindowdocument.bodythis

  • 若是想给不能编辑的元素绑定键盘事件,须要给这个元素加一个:contenteditable = "true"
  • onkeydown:按下某个键
  • onkeyup:抬起某个键
  • onkeypress:除Shift/Fn/CapsLock键之外,其它键按住(连续触发)

三、表单事件

使用范围:input ......

  • onfocus:获取焦点(光标进入input时,触发事件)
  • onblur:失去焦点(光标离开input时,触发事件)
  • oninput:内容改变(只要内容发生改变就会触发)
  • onchange:内容改变(而且失焦的时候才会触发)

四、音视频事件

使用范围:音频、视频

  • canplay:能够播放(资源没有加载完,播放中可能会卡顿)
  • canplaythrough:能够播放(资源已经加载完,播放中不会卡顿)
  • play:开始播放
  • playing:播放中
  • pause:暂停播放

五、系统事件

其它经常使用事件

  • window.onscroll:页面滚动
  • window.onresize:页面大小发生改变的时候触发
  • window.onload:页面资源加载完毕以后触发
  • img.onload :图片加载完成
  • window.onbeforeunload:当前页面关闭以前
  • window.onerror:资源加载失败

六、移动端事件

单手指事件模型 Touch

  • ontouchstart:手指碰到屏幕(手指按下)
  • ontouchmove:手指在屏幕上移动
  • ontouchend:手指离开屏幕(手指松开)
  • ontouchcancel:操做取消(通常应用于非正常状态下操做结束)

多手指事件模型 Gesture

  • ongesturestart:手指碰到屏幕(手指按下)
  • ongesturechange / ongestureupdate:手指在屏幕上移动
  • ongestureend:手指离开屏幕(手指松开)
  • ongesturecancel:操做取消(通常应用于非正常状态下操做结束)

七、其余事件

  • transitionend:动画过渡完成
  • onreadystatechange:AJAX请求状态改变事件
  • ......

事件行为还有不少,这里咱们暂时列举这些;更多的内容能够参考 MDN,事件参考; 或者能够查看元素的属性(属性中onxxx就是元素拥有的事件行为)

2、DOM0 和 DOM2 事件绑定

思惟导图

一、DOM0 事件绑定

  • 语法:元素.on事件行为=function(){}
  • 原理:给元素的私有属性赋值,当事件触发,浏览器会帮咱们把赋的值执行,可是这样也致使 “只能给当前元素某一个事件行为绑定一个方法”
box.onclick = function () {
	console.log('哈哈哈~~');
}
box.onclick = function () {
	console.log('呵呵呵~~');
} 
复制代码

只输出后面的:

  • 移除
box.onclick = function () {
	console.log('哈哈哈~~');
	//=>移除事件绑定:DOM0直接赋值为null便可
	box.onclick = null;
}
复制代码

二、DOM2 事件绑定

  • 语法:
    • 元素.addEventListener(事件行为,function(){},true/false)
      • true/false 能够省略,默认是false
      • IE6~8中:元素.attachEvent('on事件行为',function(){})
  • 原理:
    • 基于原型链查找机制,找到EventTarget.prototype上的方法而且执行,此方法执行,会把给当前元素某个事件行为绑定的全部方法,存放到浏览器默认的事件池中(绑定几个方法,会向事件池存储几个);
    • 当事件行为触发,会把事件池中存储的对应方法,依次按照顺序执行 “给当前元素某一个事件行为绑定多个不一样方法”

    事件池特色:

    • 基于addEventListener向事件池增长方法,存在去重的机制 “同一个元素,同一个事件类型,在事件池中只能存储一遍这个方法,不能重复存储”
box.addEventListener('click', function () {
	console.log('哈哈哈~~');
}, false);
box.addEventListener('click', function () {
	console.log('呵呵呵~~');
}, false);
复制代码

两个都能输出:

  • 绑定注意点:DOM2事件绑定的时候,咱们通常都采用实名函数
    • 目的:这样能够基于实名函数去移除事件绑定
    • 语法:box.addEventListener('click', fn, false);
function fn1(){ console.log(1); }
function fn2(){ console.log(2); }
function fn3(){ console.log(3); }
box.addEventListener('click', fn2, false); 
box.addEventListener('click', fn3, false); 
box.addEventListener('click', fn1, false); 
//=>基于addEventListener向事件池增长方法,存在去重的机制 “同一个元素,同一个事件类型,在事件池中只能存储一遍这个方法,不能重复存储”
box.addEventListener('click', fn1, false); // 因此此步跳过,再也不存储
box.addEventListener('mouseover', fn1, false); 
复制代码

  • 移除绑定:从事件池中移除,因此须要指定好事件类型、方法等信息(要和绑定的时候同样才能够移除)
    • 语法:box.removeEventListener('click', fn, false)
function fn() {
	console.log('哈哈哈~~');
	//=>移除事件绑定:从事件池中移除,因此须要指定好事件类型、方法等信息(要和绑定的时候同样才能够移除)
	box.removeEventListener('click', fn, false);
}
box.addEventListener('click', fn, false); 
复制代码

三、特别注意的几点

  • -1)DOM0和DOM2能够混在一块儿用:执行的顺序以绑定的顺序为主
box.addEventListener('click', function () {
	console.log('哔咔哔咔~~');
});
box.onclick = function () {
	console.log('哇咔咔~~');
}
box.addEventListener('click', function () {
	console.log('call~~');
});
复制代码

  • -2)DOM0 比 DOM2 快
  • -3)DOM0中能作事件绑定的事件行为,DOM2都支持;DOM2里面一些事件,DOM0不必定能处理绑定,例如:transitionend、DOMContentLoaded...
box.style.transition = 'opacity 1s';
box.ontransitionend = function () {
	console.log('哇咔咔~~');
}

box.addEventListener('transitionend', function () {
	console.log('哇咔咔~~');
}); 

window.addEventListener('load', function () {
	//=>全部资源都加载完成触发
	console.log('LOAD');
});
window.addEventListener('DOMContentLoaded', function () {
	//=>只要DOM结构加载完就会触发
	console.log('DOMContentLoaded');
}); 

//=>$(document).ready(function(){})
$(function () {
	//=>JQ中的这个处理(DOM结构加载完触发)采用的就是DOMContentLoaded事件,而且依托DOM2事件绑定来处理,因此同一个页面中,此操做能够被使用屡次
});
/* JQ中的事件绑定采用的都是DOM2事件绑定,例如:on/off/one */
复制代码

四、window.onload$(document).ready()的区别

  • -1)$(document).ready()
    • 采用的是DOM2事件绑定,监听的是DOMContentLoaded这个事件,因此只要DOM结构加载完成就会被触发执行,
    • 并且同一个页面中可使用屡次(绑定不一样的方法,由于基于DOM2事件池绑定机制完成的)
  • -2)window.onload
    • 必须等待全部资源都加载完成才会被触发执行,采用DOM0事件绑定,同一个页面只能绑定一次(一个方法),
    • 想绑定多个也须要改成window.addEventListener('load', function () {})DOM2绑定方式

五、DOM0 和 DOM2 的传播的区别

  • DOM0 绑定的方法,只能在目标阶段和冒泡阶段触发执行
  • DOM2绑定的方法,咱们能够控制在捕获阶段执行
    • 元素.addEventListener(事件行为,function(){},true/false)
    • 第三个参数:不写 默认是 false
      • false:表明在冒泡阶段执行此方法
      • true:表明在捕获阶段执行此方法(基本没用过)

3、事件对象

思惟导图

一、定义:

  • 给元素的事件行为绑定方法,当事件行为触发方法会被执行,不只被执行,并且还会把当前操做的相关信息传递给这个函数 =>传递过来相关信息就叫作事件对象

事件对象是由事件当前自己产生的,和执行什么函数没有关系

二、原理:

事件对象和函数以及给谁绑定的事件没啥必然关系,它存储的是当前本次操做的相关信息,操做一次只能有一份信息,因此在哪一个方法中获取的信息都是同样的;第二次操做,存储的信息会把上一次操做存储的信息替换掉...;

  • 每一次事件触发,浏览器都会这样处理一下:
    • 1.捕获到当前操做的行为(把操做信息获取到),经过建立MouseEvent等类的实例,获得事件对象EV
    • 2.通知全部绑定的方法(符合执行条件的)开始执行,而且把EV当作实参传递给每一个方法,因此在每一个方法中获得的事件对象实际上是一个
    • 3.后面再从新触发这个事件行为,会从新获取本次操做的信息,用新的信息替换老的信息,而后继续以前的步骤...

三、事件对象类型

-1)鼠标事件对象

若是是鼠标操做,获取的是MouseEvent类的实例(这个实例就是 =>鼠标事件对象)

box.onclick = function (ev) {
    console.log(ev);
}
复制代码

  • 原型链:
    • 鼠标事件对象 -> MouseEvent.prototype -> UIEvent.prototype -> Event.prototype -> Object.prototype
  • 经常使用属性:
    • clientX/clientY:当前鼠标触发点距离当前窗口左上角的X/Y轴坐标
    • pageX/pageY:触发点距离当前页面左上角的X/Y轴坐标

-2)键盘事件对象

若是是键盘操做,获取的是KeyboardEvent类的实例 =>键盘事件对象

  • 经常使用属性:
    • code & key:存储的都是按键,code更细致
    • keyCode & which:存储的是键盘按键对应的码值
  • 键盘经常使用码值:
    • 方向键:37 38 39 40 =>左上右下
    • 空格SPACE:32
    • 回车ENTER:13
    • 回退BACK:8
    • 删除DEL:46
    • SHIFT:16
    • CTRL:17
    • ALT:18

window键盘码值:

苹果键盘码值:

-2)除了以上还有:

  • 普通事件对象(Event)、
  • 手指事件对象(TouchEvent)等

这里不详细介绍了

四、事件对象event的属性

除了上面,只有鼠标和键盘中有的属性外,还有一些公共的全部事件对象都有的属性

  • type:触发事件的类型
  • target:事件源(操做的是哪一个元素,哪一个元素就是事件源)
    • 在不兼容的浏览器中可使用srcElement获取,也表明的是事件源
  • preventDefault():用来阻止默认行为的方法
    • 不兼容的浏览器中用ev.returnValue=false也能够阻止默认行为
  • stopPropagation():阻止冒泡传播
    • 不兼容的浏览器中用ev.cancelBubble=true也能够阻止默认行为

4、事件传播机制

思惟导图

事件传播机制:当某个元素的相关事件行为触发时,浏览器会作三件事情:(能够经过dir(Event)产看Event类上的信息)

一、冒泡传播机制

Event.prototype:Event 原型上记录了冒泡传播的顺序

  • 一、捕获阶段:=>CAPTURING_PHASE:1

    从最外层向最里层事件源依次进行查找

    • 目的:是为冒泡阶段事先计算好传播的层级路径
  • 二、目标阶段:=>AT_TARGET:2

    • 当前元素的相关事件行为触发
  • 三、冒泡传播:=>BUBBLING_PHASE:3

    • 触发当前元素的某一个事件行为,不只它的这个行为被触发了, 并且它全部的祖先元素(一直到window)相关的事件行为都会被依次触发(从内到外的顺序)

二、阻止冒泡传播

ev.stopPropagation()

  • 不兼容的浏览器中用ev.cancelBubble=true也能够阻止默认行为

小案例:实现放大镜效果(简易)

效果图:

一、HTMl

<div id="fd">
    <div class="lit_box">
        <div class="mask"></div>
        <img src="images/banner1.jpg" alt="">
    </div>
    <div class="big_box">
        <img src="images/banner1.jpg" alt="">
    </div>
</div>
复制代码

二、CSS

#fd {
        margin: 20px auto;
    }

    .lit_box {
        width: 200px;
        height: 200px;
        border: 1px solid #333;
        position: relative;
        top: 0;
        left: 0;
    }

    .lit_box img {
        width: 100%;
        height: 100%;
    }

    .lit_box .mask {
        width: 100px;
        height: 100px;
        background: rgba(238, 132, 10, 0.8);
        position: absolute;
        top: 0;
        left: 0;
        display: none;
        cursor: move;
    }

    .big_box {
        width: 400px;
        height: 400px;
        border: 1px solid #333;
        position: relative;
        left: 210px;
        top: -201px;
        overflow: hidden;
        display: none;
    }

    .big_box img {
        position: absolute;
        width: 800px;
        height: 800px;
    }
复制代码

三、JS

let lit = document.querySelector('.lit_box'),
    mask = document.querySelector('.mask'),
    big = document.querySelector('.big_box'),
    bigImg = big.querySelector('img');

lit.onmouseenter = function () {
    mask.style.display = 'block';
    big.style.display = 'block';
}
lit.onmouseleave = function () {
    mask.style.display = 'none';
    big.style.display = 'none';
}
lit.onmousemove = function (e) {
    let o = offset(this);
    let l = e.pageX - o.left - mask.clientWidth / 2,
        t = e.pageY - o.top - mask.clientHeight / 2;
 
    let maxL = this.clientWidth - mask.clientWidth,
        maxT = this.clientHeight - mask.clientHeight;
    l = l < 0 ? 0 : (l > maxL ? maxL : l);
    t = t < 0 ? 0 : (t > maxT ? maxT : t);

    mask.style.left = l + 'px';
    mask.style.top = t + 'px';

    let n = big.clientHeight / mask.clientHeight;
    bigImg.style.left = -l * n + 'px';
    bigImg.style.top = -t * n + 'px';
}

function offset(ele) {
    let l = ele.offsetLeft,
        t = ele.offsetTop;
    let parent = ele.offsetParent;
    while (parent) {
        l += parent.clientLeft + parent.offsetLeft;
        t += parent.clientTop + parent.offsetTop;
        parent = parent.offsetParent;
    }
    return {
        top: t,
        left: l
    }
}
复制代码

5、事件委托

又叫作事件代理

  • 核心:
    • 基于事件的冒泡传播机制完成
  • 原理:
    • 利用事件的冒泡传播机制,只给最外层容器的相关事件行为绑定方法,这样无论触发容器内部哪个后代元素的相关事件行为,都会传播到容器上,触发它的对应事件行为,
    • 在执行的方法中,能够基于ev.target来判断事件源,从而作不一样的事情;避免给后台元素一个个的注册事件绑定,性能有很大的提升;

一、应用场景一

若是一个容器中不少元素都要在触发某一事件的时候作一些事情的时候,

  • 原始方案:给元素每个都单独进行事件绑定
  • 基于事件委托:
    • 咱们只须要给当前容器的这个事件行为绑定方法,
    • 这样不管是触发后代中哪个元素的相关事件行为,因为冒泡传播机制,当前容器绑定的方法也都要被触发执行
    • 想知道点击的是谁(根据是谁作不一样的事情),只须要基于事件对象中的ev.target事件源获取便可

二、应用场景二

数据是动态绑定渲染的,要给每一条数据绑定事件行为时,选用事件委托,就能够实现动态点击的处理了,不用在逐一获取绑定了

三、应用场景三

除某某事件源之外的其它事件源,操做的时候统一作某事的,基本上都要基于事件委托解决

四、优势

  • =>基于事件委托实现,总体性能要比一个个的绑定方法高出50%左右
  • =>若是多元素触发,业务逻辑属于一体的,基于事件委托来处理更加好
  • =>某些业务场景只能基于事件委托处理

思惟导图

相关文章
相关标签/搜索