button添加插槽之后绑定不来事件_JavaScript(11) 事件详解

  • Post author:
  • Post category:java



注册事件/绑定事件

含义是把一个事件和一个函数关联到一起.


传统的注册事件方式

  • 使用 element.onclick (前面反复用过)

  • 使用 onXXX (例如 onclick) 属性

 <button onclick="alert('hello')">点我一下button>

问题: 同一个元素的同一个事件, 只能注册一个回调函数.

 <button>点我一下button><script>    var button = document.querySelector('button');    button.onclick = function () {        alert('hehe');    }    button.onclick = function () {        alert('haha');    }script>

可以看到, 只会执行 ‘haha’ 这个回调函数


方法监听注册方式(推荐)

使用 addEventListener 方法进行注册事件, 可以给同一个元素的同一个事件注册多个处理函数.

按照注册顺序来执行这些函数.

IE9 以前不支持. 需要使用 attachEvent 代替.

 target.addEventListener(type, listener);target.addEventListener(type, listener, useCapture);

type 表示监听事件类型的字符串。不需要带 ‘on’ 前缀

listener 当所监听的事件类型触发时,会接收到一个事件通知(实现了

Event

接口的对象)对象, 作为回调函数的参数。

listener

是一个回调函数. (也叫做 监听器).


useCapture

: 和事件流相关, 后面再说. 默认为 false

 <button>点我一下button><script>    var button = document.querySelector('button');    button.addEventListener('click', function () {        alert('hehe');    });    button.addEventListener('click', function () {        alert('haha');    });script>

注意: 关于 attachEvent

这个函数 chrome 及 IE9+ 浏览器均不支持. 大家有个了解即可.

4dc65f8eb840bc46f990d6e582eff93a.png


兼容性处理方案

封装一个函数, 来兼容各种浏览器

基本思路就是判定当前函数是否存在, 如果存在就使用, 否则就尝试下一个方式.

 function addEventListener(element, event, listener) {    if (element.addEventListener) {        element.addEventListener(event, listener);    } else if (element.attachEvent) {        element.attachEvent('on' + event, listener);    } else {        // 相当于 event.onclick = listener        element['on' + event] = listener;    }}


删除事件/解绑事件


传统的注册事件方式

 element.onclick = null;
 <button>点我一次就好button><script>    var button = document.querySelector('button');    button.onclick = function () {        alert('hello');        this.onclick = null;        // button.onclick = null; // 也可以.     }script>


方法监听删除方式

使用

removeEventListener

方法

 target.removeEventListener(type, listener[, useCapture]);

注意, 参数一定要指定要删除该事件的哪个监听器(一个事件可以有多个监听器).

因此监听器就不能使用匿名函数的方式来设定了.

IE9 以前不支持. 需要使用 detachEvent 代替.

点我一次就好

注意: 关于 detachEvent (和 attachEvent 一样).

这个函数 chrome 及 IE9+ 浏览器均不支持. 大家有个了解即可.


兼容性处理方案

function removeEventListener(element, event, listener) {
if (element.removeEventListener) {
element.removeEventListener(event, listener);
} else if (element.detachEvent) {
element.detachEvent('on' + event, listener);
} else {
// 相当于 event.onclick = null;
element['on' + event] = null;
}
}


事件流


基本概念

事件传播的过程(按照啥样的元素顺序来传播), 称为事件流.

例如当用户执行了某个动作, 比如点击页面的某个位置, 此时页面中的元素之间是按照啥样的顺序感知到这个点击操作的.

DOM 事件流分成三个阶段:

  1. 捕获阶段: 事件从最顶层元素依次往子元素方向传播.

  2. 当前目标阶段: 事件到达最顶层元素.

  3. 冒泡阶段: 事件从子元素再往顶层元素方向传递.

883072fbd82ca65d80b57cafc0a3b546.png


理解捕获和冒泡

想象咱以前上学时课间操的场景

912dfd9b9e59af0d4654b56d27d0affb.png

当主席台的领导喊话的时候, 先是站在前排的同学先听到声音, 站在后排的同学后听到声音. 这个相当于 “捕获阶段”

声音到达教学楼之后, 会反射回来. 此时后排的同学会先听到回声, 前排的同学后听到回声. 这个相当于 “冒泡阶段”


注意:

理论上来说, 无论是捕获还是冒泡, 都能保证事件顺利到达指定的元素. 那为啥还整两次呢? 这就是个历史遗留问题了.

  • IE 觉得按照冒泡的方式来进行传递事件

  • Netscape 觉得按照捕获的方式来进行传递事件.

再后来现代的浏览器为了两种方式都兼容, 决定就 “全都要” 了.

56d75c355109331fd0104b4ac6446568.png


代码示例


注意:

  1. JS 代码中只能按照一种方式获取到事件(虽然浏览器支持两种方式, 但是 JS 只能按照其中一种来获取事件)

  2. onclick 或者 attachEvent 只能得到冒泡阶段的事件.

  3. addEventListener 可以根据第三个参数 useCapture 来决定捕获哪个阶段的事件. 为 true 表示在捕获阶段获取. 为 false(默认值) 表示在冒泡阶段获取.


不加 useCapture

: 冒泡阶段的事件



点我一下



先执行 child, 后执行 parent


加 useCapture:

捕获阶段事件



点我一下



先执行 parent, 后执行 child


注意:

开发过程中主要关注冒泡阶段, 很少关注捕获阶段.

有些事件没有冒泡: onblur, onforce, onmouseenter, onmouseleave.

不会向父级传递.


事件对象

参考 https://developer.mozilla.org/zh-CN/docs/Web/API/Event

  • 事件对象是产生事件之后才会出现(浏览器自动创建的), 包含了这个事件的具体信息.

  • 事件对象会自动被传入到时间处理函数的参数中. (注意, 这个是形参, 叫啥名字都无所谓)

  • 事件对象包含的属性和事件的类型密切相关.

例如如果是一个鼠标事件, 事件对象就会包含产生鼠标事件时鼠标的位置.

例如如果是一个键盘事件, 事件对象就会包含当前是哪个键盘按键产生的.

点我一下

0a4a8153fd8aee9c310e13fe6801808b.png

低于IE9 版本的浏览器需要使用 window.event 来获取事件


兼容性写法:

var button = document.querySelector('button');
button.onclick = function (event) {
// || 意味着, event 为 undefined 的时候, 返回 window.event, 否则返回 event
event = event || window.event;
console.dir(event);
}


事件触发者: e.target

  • target: 表示是谁触发了这个事件.

注意理解 target 和 this 的区别. target 是触发事件的对象, this 是绑定了事件的对象



点我一下



2b1fc3b85d71faf649c12821ea9484ea.png

可以看到, 点击 child 的时候, parent 也会触发事件回调函数.

此时打印的 target 还是 child, 但是 this 还是 parent


注意:

e.target 存在兼容性问题. 低于 IE9 需要使用

e.srcElement

来获取

此时要写成

var target = e.target || e.srcElement;


获取事件类型

  • 通过 event.type 获取到事件的类型

点我一下


阻止默认行为

例如, 点击链接不跳转, 或者点击 form 的提交按钮也不提交.

  • 使用 preventDefault 来阻止默认行为.

百度


阻止事件冒泡(常见考点)

  • 使用 e.stopPropagation() 阻止事件冒泡. (存在兼容性问题)




点我一下



此时点击子元素, 子元素能够执行回调函数, 父元素则不会执行.

9381bf31802fee8064eb8893cb02d7ad.png

如果是 低于 IE9 版本, 需要使用

e.cancelBubble = true

阻止冒泡.


事件委托/事件代理/事件委派

理解委托: 把一件事托给别人来做.

租房子, 房东可以自己到处贴广告, 然后接待人看房, 但是比较麻烦比较辛苦, 就可以通过

把房子挂到中介, 委托中介进行出租, 自己只要坐收钱即可.

事件冒泡机制就可以用于实现委托的效果.


核心原理:

不要给每个子节点添加监听器, 而要给父节点添加监听器, 利用冒泡原理影响到每个子节点.


核心作用:

减少 DOM 操作次数, 提高性能.


代码示例:

点击 div, 弹出对话框, 显示 div 中的数字.

  • 使用 e.target 能够获取到触发事件的对象.



1



2



3



4




常用鼠标事件


鼠标事件概览

文档参考: https://developer.mozilla.org/zh-CN/docs/Web/Events

Event Name Fired When

auxclick
非主键点击 (ANY non-primary button) , 比如鼠标中键

click
在元素上按下并释放任意鼠标按键。

contextmenu
右键点击(在右键菜单显示前触发)。

dblclick
在元素上双击鼠标按钮。

mousedown
在元素上按下任意鼠标按钮。

mouseenter
指针移到有事件监听的元素内。

mouseleave
指针移出元素范围外(不冒泡)。

mousemove
指针在元素内移动时持续触发。

mouseover
指针移到有事件监听的元素或者它的子元素内。

mouseout
指针移出元素,或者移到它的子元素上。

mouseup
在元素上释放任意鼠标按键。

pointerlockchange
鼠标被锁定或者解除锁定发生时。

pointerlockerror
可能因为一些技术的原因鼠标锁定被禁止时。

select
有文本被选中。

wheel
滚轮向任意方向滚动。

注意:

  1. click 是任意鼠标按键都会触发. 右键, 中键都能.

  2. mouseenter 和 mouseover 都表示 “鼠标移动进来”. mouseenter 不冒泡, mouseover 会冒泡.

  3. mouseleave 和 mouseout 都表示 “鼠标移动出去”. mouseleave 不冒泡, mouseout 会冒泡.

  4. pointerlockchange 适用于需要鼠标锁定的场景. 鼠标锁定指的是限制鼠标在某个元素内运动.


mouseenter 和 mouseover

mouseover 会冒泡

mouseenter 不会冒泡

将鼠标从外向内移动.

mouseover: 鼠标经过父元素, 会触发一次回调; 经过子元素, 还会再触发一次回调. (鼠标经过子元素时, 子元素的 mouseover 事件会冒泡给父元素, 于是父元素就触发回调了).

mouseenter: 鼠标经过父元素, 会触发一次回调; 经过子元素不会触发.


同理

, mouseleave 不冒泡, mouseout 冒泡.


案例: 禁止复制网页内容

  • 禁止右键菜单: 先通过阻止 contextmenu 默认行为, 禁止显示右键菜单.


这是一段话


  • 禁止选中文本: 通过阻止 selectstart 默认行为的方式, 禁止被选中.


这是一段话


注意: 这种做法只是忽悠小白的. 用开发者工具还是能复制的.


获取鼠标位置

通过鼠标事件对象中的以下属性获取位置.

  • clientX, clientY: 获取鼠标点击位置的坐标. 相对于整个浏览器可视窗口的坐标

  • pageX, pageY: 获取鼠标点击位置的坐标, 相对于整个页面左上角的坐标 (>=IE9)

  • screenX, screenY: 获取鼠标点击位置的坐标, 相对于整个显示器左上角的坐标.


案例: 实现鼠标跟随效果

  • 监听 mousemove 事件.

  • 触发 mousemove 事件的时候获取当前鼠标位置

  • 设置图片的位置为鼠标当前位置.

注意:

  • img 要带定位, 否则动不了.

  • img 设置位置的时候不要忘记单位 px

此时发现鼠标是在图片的左上角.

如果想让鼠标在图片中间, 可以在设置 left 和 top 的时候再减去图片宽度的一半和高度的一半就可以了.


案例: 实现拖放效果

  • 监听 mousedown 事件(鼠标按下), 触发时则开始监听 mousemove 事件, 同时移动元素的位置.

  • 移动元素时要保证鼠标在元素内相对于元素左上角的位置始终不变.

  • 监听 mouseup 事件(鼠标抬起), 触发时则放弃监听 mousemove 事件.


拖动我试试


div {
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 200px;
background-color: red;
}
var div = document.querySelector('div');
div.onmousedown = function (e) {
var offsetX = e.clientX - this.offsetLeft;
var offsetY = e.clientY - this.offsetTop;
console.log("按下鼠标", offsetX, offsetY);
this.onmousemove = function (e) {
console.log(e.clientX, e.clientY);
this.style.left = (e.clientX - offsetX) + 'px';
this.style.top = (e.clientY - offsetY) + 'px';
}
}
div.onmouseup = function (e) {
console.log("释放鼠标");
this.onmousemove = null;
}


常用键盘事件


键盘事件概览

参考文档 https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent

  • onkeyup: 按键释放时触发.

  • onkeydown: 按键按下时触发.

  • onkeypress: 俺家按下时触发. 不能识别功能键(alt 啥的). mdn 不建议使用.

执行顺序: 先执行 down, 再执行 press, 最后执行 up


注意:

如果绑定按键事件的元素是一个文本框, 此时 keydown 事件先触发, 然后是编辑文本, 然后触发 keyup

在这个代码中如果使用 keydown , screen 中显示的内容就会始终比文本框中少一个.

因为触发 keydown 的时候字符还没有被放到输入框中, 此时获取的 value 就是上次的结果.


获取按键信息

使用键盘事件对象来获取按键信息.

  • e.keyCode 获取按键的 ASCII 码.

  • 如果是 keyup 或者 keydown, 则不区分字母大小写. 如果是 keypress 则区分字母大小写.

  • 使用 key 能获取按键名称, 不能区分左右 shift/alt/ctrl, 低于IE9 版本不支持.

  • 使用 code 也能获取按键名称, 能区分左右 shift/alt/ctrl 但是未能考虑到键盘布局, IE 就压根不支持.

document.onkeydown = function (e) {
console.log(e.key);
console.log(e.code);
}

可以在 mdn 文档的 code 的详细描述页查看兼容性支持情况

https://developer.mozilla.org/zh-CN/docs/Web/API/KeyboardEvent/code

fa7d44653594e268335cc304dd94d581.png


案例: 控制飞机移动

使用键盘的 a s d w 来控制飞机来回移动.

 <img src="plane.png" alt="">
 img {    position: absolute;    bottom: 0;    left: 50%;    /* 注意, 这里的 margin-left 如果加上了会出问题 */    /* margin-left: -50px; */    width: 100px;    height: 100px;    /* 加上平滑过渡效果 */    transition: all 0.3s linear;}
 var img = document.querySelector('img');document.onkeydown = function (e) {    var x = img.offsetLeft;    var y = img.offsetTop;    console.log(x, y);    if (e.key == 'w') {        console.log('向上');        y -= 10;    } else if (e.key == 's') {        console.log('向下');        y += 10;    } else if (e.key == 'a') {        console.log('向左');        x -= 10;    } else if (e.key == 'd') {        console.log('向右');        x += 10;    } else {        return;    }    img.style.left = x + 'px';    img.style.top = y + 'px';}


注意

: offsetLeft/offsetTop 获取的是元素真实的位置.

这个真实位置会同时受到 margin 和 left/top 的影响.

6e7c37da79e6d5c61dcc074c90b39f80.png



版权声明:本文为weixin_36325879原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。