Rodrick

vuePress-theme-reco Rodrick    2022
Rodrick Rodrick

Choose mode

  • dark
  • auto
  • light
Home
Category
  • CS基础
  • 数据库
  • 前端
  • 其他
Tag
About
Timeline
D&T
  • 官方文档

    • Vue
    • Vue3
    • Webpack
    • MDN
    • Node中文网
    • React
    • 小程序
    • FineReport
  • 学习面试

    • 现代JavaScript教程
    • ES6
    • 阿西河
    • LeetCode
    • 牛客网
  • 工具

    • bejson
Contact
  • Github
  • Gitee
author-avatar

Rodrick

62

Article

18

Tag

Home
Category
  • CS基础
  • 数据库
  • 前端
  • 其他
Tag
About
Timeline
D&T
  • 官方文档

    • Vue
    • Vue3
    • Webpack
    • MDN
    • Node中文网
    • React
    • 小程序
    • FineReport
  • 学习面试

    • 现代JavaScript教程
    • ES6
    • 阿西河
    • LeetCode
    • 牛客网
  • 工具

    • bejson
Contact
  • Github
  • Gitee
  • JS

    • ES6核心语法
    • 模块化&Webpack
    • Promise&异步函数async
    • WebSocket原理浅析
    • axios的使用
    • ES5的变量提升和ES6的暂时性死区
    • call、apply、bind的用法
    • 对象的原始值转换
    • 可选链"?."
    • 随机数方法
    • 深浅拷贝实现
    • Array常用处理
    • 防抖与节流
    • postMessage窗口间通讯
    • 解构赋值
    • Map&Set
    • 日期和时间
    • 对象属性
    • Toast组件简单封装
    • 原型链
    • 类 Class
    • Generator与异步迭代
    • 偏函数Partial和柯里化Currying
    • DOM操作
    • 事件处理
    • 事件循环EventLoop
    • 网络请求
    • 浏览器中存储数据
    • 正则表达式
  • CSS

  • 其他

事件处理

vuePress-theme-reco Rodrick    2022

事件处理

Rodrick 2020-12-07 js

# 一般事件处理

# 常用方式⭐

  • onclick() —— HTML 中写法
  • elem.onclick = f —— DOM 属性调用
  • elem.addEventListener(event, f[, options]) —— 事件监听, options 对象有如下属性:
    • once:如果为 true,那么会在被触发后自动删除监听器。
    • capture:事件处理的阶段,参考 冒泡和捕获 一章中的介绍。由于历史原因,options 也可以是 false/true,它与 {capture: false/true} 相同。
    • passive:如果为 true,那么处理程序将不会调用 preventDefault()
  • elem.removeEventListener(event,f[,options]) —— 移除事件,注意这里的 f 必须和 addEventListener 指向同一个函数

上述所有的 f 都可以有一个 event 参数,可以通过 type 获取事件类型(如: click )、 currentTarget 获取处理事件的元素,相当于当时的 this 对象

# handleEvent

addEventListener(event,f) ,第二个参数不仅可以传入一个函数,也可以传入一个对象 obj,事件发生时,会自动调用对象内的 handleEvent(event) 函数,我们可以这样使用:

<button id="elem">Click me</button>

<script>
  let obj = {
    handleEvent(event) {
      alert(event.type + " at " + event.currentTarget);
    }
  };

  elem.addEventListener('click', obj);
</script>

或者可以使用一个类:

<button id="elem">Click me</button>

<script>
  class Menu {
    handleEvent(event) {
      switch(event.type) {
        case 'mousedown':
          elem.innerHTML = "Mouse button pressed";
          break;
        case 'mouseup':
          elem.innerHTML += "...and released.";
          break;
      }
    }
  }

  let menu = new Menu();
  elem.addEventListener('mousedown', menu);
  elem.addEventListener('mouseup', menu);
</script>

本质上是一样,看实际业务场合使用。

# 冒泡事件

# 概念

简单来说:

<style>
  body * {
    margin: 10px;
    border: 1px solid blue;
  }
</style>

<form onclick="alert('form')">FORM
  <div onclick="alert('div')">DIV
    <p onclick="alert('p')">P</p>
  </div>
</form>

点击 p 的时候,会依次弹出 [p、div、form] ,因为当一个事件发生在一个元素上,它会首先运行在该元素上的处理程序,然后运行其父元素上的处理程序,然后一直向上到其他祖先上的处理程序。

# 阻止冒泡⭐

  • event.stopPropagation() 可以阻止冒泡,但是只能阻止当前元素不要继续冒泡,但是只有当前的这个 event 会停止冒泡,其他的 event2/3/4 还会继续冒泡
  • event.stopImmediatePropagation() 可以用于停止冒泡,并阻止当前元素上的处理程序运行。使用该方法之后,其他处理程序就不会被执行。

# event.target

在冒泡过程中, event.target 指向的是当前冒泡到那个元素,而 this 是 event 发生的元素,注意区分。

# 事件委托⭐

事件委托 是一种处理这种情况: 在一个容器中,会有多个相同或类似的目标元素对同样的事件做一些处理,比如有一个 9x9 的表格,每一个 td 鼠标放上去的时候修改颜色。 处理方式一般是通过 container.addEventListener(event,(e)=>{xxxx}) ,根据 e.target 获取当前活跃元素来进行操作。 这是一种处理模式,具体的一些例子见课程  。

# 阻止浏览器默认事件

  1. 在 on<event> 中使用 return false
  2. 在 addEventListener 中使用 event.preventDefault()
  3. 在 addEventListener 这个方法的第三个参数 options 对象中设置 passive:true 表示程序 不会 调用 event.preventDefault() ,注意,是不会!
  4. 判断默认事件是否被阻止,使用 event.defaultPrevented ,返回一个 bool 值

# 鼠标事件

# 常用事件

  • mousedown/mouseup  在元素上点击/释放鼠标按钮。
  • mouseover/mouseout  鼠标指针从一个元素上移入/移出。
  • mousemove  鼠标在元素上的每个移动都会触发此事件。
  • click  如果使用的是鼠标左键,则在同一个元素上的 mousedown 及 mouseup 相继触发后,触发该事件。
  • dblclick  在短时间内双击同一元素后触发。如今已经很少使用了。
  • contextmenu  在鼠标右键被按下时触发。还有其他打开上下文菜单的方式,例如使用特殊的键盘按键,在这种情况下它也会被触发,因此它并不完全是鼠标事件。
  • oncopy  复制事件

当我们单击时,触发的顺序是: mousedown => mouseup => click

# 按钮判断

鼠标按键状态 event.button
左键 (主要按键) 0
中键 (辅助按键) 1
右键 (次要按键) 2
X1 键 (后退按键) 3
X2 键 (前进按键) 4

# 鼠标移动事件

这里我们主要介绍两套移入移除方法:

  • mouseover/mouseout :鼠标移入/移出

他们都有 target 和 relatedTarget 两个属性:

  • 对于 mouseover , target 是移入对象, relatedTarget 是来自的对象
  • 对于 mouseout , target 是离开对象, relatedTarget 是离开后移入的对象

2020-12-08_111132.png

不管是移入移除, relatedTarget 可以为 null

  • 父元素移入子元素的时候,会触发父元素的 mouseout ,然后触发子元素的 mouseover ,注意子元素的 mouseover 是会 冒泡 的,也就是这时候会再触发父元素的 mouseover ,我们可以借用这个特性做 事件委托

image.png

  • mouseenter/mouseleave 这也是一套移入移除方法,但是他和 mouseover/mouseout 最大的区别在于:

    1. 进入父元素后,触发父元素的 mouseenter ,不管你在这个父元素的里面如何进出任何子元素上,都不会触发父元素的 mouseleave ,只会触发子元素的 mouseenter/mouseleave  ,查看示例代码  如下
    2. 事件 mouseenter/mouseleave 不会冒泡。

# 拖放事件⭐

拖放事件我们有一套基础的拖放算法如下所示:

  1. 在 mousedown 上 —— 根据需要准备要移动的元素(也许创建一个它的副本,向其中添加一个类或其他任何东西)。
  2. 然后在 mousemove 上,通过更改 position:absolute 情况下的 left/top 来移动它。
  3. 在 mouseup 上 —— 执行与完成的拖放相关的所有行为。

这里用一个拖放小球的例子作为学习,注意看 js 中的注释

再来看一个滑动条(slider)的封装

这里说明一下一下为什么 mouseup 和 mousemove 都要加在 document 上,因为

  • mousemove 是一定要在 document 上的,如果放在 thumb 上,你鼠标移动快到离开了 thumb 那么 mousemove 事件就立刻失效了
  • mouseup 如果你拖动的时候鼠标离开了 thumb 然后松开,那么 mouseup 就失效

# 总结一下【拖放】⭐

  1. 遵循 mousedown 里绑定 mousemove 和 mouseup
  2. mousemove 一般都放在 document ,因为鼠标拖动时候快了就会离开元素
  3. mouseup 想清楚鼠标离开元素是否需要触发 mouseup 再考虑绑定在哪
  4. 一半都会要计算 shiftX/Y ,一般都是 event.clientX - ball.getBoundingClientRect().left 这种形式
  5. document.elementFromPoint 可以获取鼠标指针下当前嵌套最深的元素,但是记得 hidden 你鼠标拖动的元素,获取了之后再解除 hidden ,不然可能返回的永远都是它

# 指针事件

指针事件兼容 IE 10

我们可以在现代浏览器使用 pointer<event> 来代替 mouse<event> :

指针事件 类似的鼠标事件
pointerdown mousedown
pointerup mouseup
pointermove mousemove
pointerover mouseover
pointerout mouseout
pointerenter mouseenter
pointerleave mouseleave
pointercancel -
gotpointercapture -
lostpointercapture -
  • 在触屏设备中,指针事件会更加好用
  • 在拥有所有鼠标事件属性的情况下,还有如下事件:
    • pointerId —— 触发当前事件的指针唯一标识符。 浏览器生成的。使我们能够处理多指针的情况,例如带有触控笔和多点触控功能的触摸屏(下文会有相关示例)。

    • pointerType —— 指针的设备类型。必须为字符串,可以是:“mouse”、“pen” 或 “touch”。 我们可以使用这个属性来针对不同类型的指针输入做出不同响应。

    • isPrimary —— 当指针为首要指针(多点触控时按下的第一根手指)时为 true。

关于多点触控介绍详见教程

# 键盘事件

# keydown & keyup

顾名思义,不解释

# event.code & event.key

Key event.key event.code
Z z(小写) KeyZ
Shift+Z Z(大写) KeyZ
F1 F1 F1
Backspace Backspace Backspace
Shift Shift ShiftRight 或 ShiftLeft

观察上面我们会发现,同一个按键 Z,可以与或不与 Shift 一起按下。我们会得到两个不同的字符:小写的 z 和大写的 Z。 event.key  正是这个字符,并且它将是不同的。但是, event.code  是相同的。 event.code  准确地标明了哪个键被按下了。例如,大多数键盘有两个 Shift 键,一个在左边,一个在右边。 event.code  会准确地告诉我们按下了哪个键,而 event.key  对按键的“含义”负责:它是什么(一个 “Shift”)。

# 组合键

所有的鼠标事件都包含有关按下的组合键的信息。 事件属性:

  • shiftKey :Shift
  • altKey :Alt(或对于 Mac 是 Opt)
  • ctrlKey :Ctrl
  • metaKey :对于 Mac 是 Cmd

使用方式:

document.addEventListener('keydown', function(event) {
  if (event.code == 'KeyZ' && (event.ctrlKey || event.metaKey)) {
    alert('Undo!')
  }
});

# 滚动事件⭐

scroll  事件允许对页面或元素滚动作出反应。我们可以在这里做一些有用的事情。 这里举几个 现代js教程  的例子:

  1. 上拉加载更多,注意 getBoundingClientRect().bottom 是浏览器视窗顶部到文档底部的距离,且它永不为0,因为当他等于 clientHeight 的时候就滚不动了。
  1. 回到顶部
  1. 图片懒加载,基本原理:src 占位图片,data-src是实际地址,在判断图片头或尾显示出的时候,把src替换为data-src

# 选择与范围

有一些特殊的场合下,我们也许会需要操作选择范围(鼠标拖黑网页内容),或者读取下、用户选择范围这样的操作,可以使用到 Selection 和 Range 对象,由于应用场合比较少,不展开细说,细节参考教程

欢迎来到 Rodrick
看板娘