夜听城嚣 夜听城嚣
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏

dwfrost

前端界的小学生
首页
  • 学习笔记

    • 《JavaScript高级程序设计》
    • 前端基建与架构
  • 专题分享

    • Git入门与开发
    • 前端面试题汇总
    • HTML和CSS知识点
  • 项目实践
  • 抓包工具
  • 知识管理
  • 工程部署
  • 团队规范
bug知多少
  • 少年歌行
  • 青年随笔
  • 文海泛舟
  • 此事躬行

    • 项目各工种是如何协作的
    • TBA课程学习
收藏
  • 第1章 什么是JavaScript
  • 第2章 HTML中的JavaScript
  • 第3章 语言基础
  • 第4章 变量、作用域与内存
  • 第5章 基本引用类型
  • 第6章 集合引用类型
  • 第7章 迭代器与生成器
  • 第8章 对象、类与面向对象编程
  • 第9章 代理与反射
  • 第10章 函数
  • 第11章 异步编程
  • 第12章 BOM
  • 第14章 DOM
  • 第15章 DOM扩展
  • 第16章 DOM2和DOM3
  • 第17章 事件
    • 第18章 动画与Canvas图形
    • 第19章 表单脚本
    • 第20章 JavaScript API
    • 第21章 错误处理与调试
    • 第23章 JSON
    • 第24章 网络请求与远程资源
    • 第25章 客户端存储
    • 第26章 模块
    • 第27章 工作者进程
    • 第28章 最佳实践
    • 《JavaScript高级程序设计》
    frost
    2022-01-23

    第17章 事件

    事件代表文档或浏览器窗口中某个有意义的时刻。可以使用仅在事件发生时执行的监听器(也叫处理程序)订阅事件。

    # 事件流

    点击按钮时,是否点击了<body>标签?是的,这里涉及到事件流的顺序。

    # 事件冒泡

    是事件从最具体的元素(文档树中最深的节点)开始触发,然后向上传播至没有那么具体的元素(文档)。对于一段html:

    <!DOCTYPE html>
    <html>
      <head> </head>
      <body>
        <button>click</button>
      </body>
    </html>
    
    1
    2
    3
    4
    5
    6
    7

    点击button后,click事件顺序如下:

    button>body>html>document。

    这就是事件冒泡,所有现代浏览器都支持该方式。

    # 事件捕获

    事件捕获刚好相反,从document最开始接收到事件,直到最具体的元素。即上面的例子事件顺序为:

    document>html>body>button。

    所有现代浏览器都支持该方式。

    # DOM事件流

    DOM2 Events规范规定,事件流分为3个阶段:事件捕获、到达目标和事件冒泡。

    注意,在事件处理时,被认为是冒泡的一部分,如果要阻止事件传播,则会在这里进行(个人理解)。

    # 事件处理程序

    是指为响应事件而调用的函数,比如单击(click)、加载(load)等。事件处理程序有3种写法。

    // 第1种
    <button onclick="console.log(event.type)">click</button>
    
    1
    2
    // 第2种
    <button onclick="say()">click</button>
    <script>
      function say() {
        console.log(event.type)
      }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    // 第3种
    <button>click</button>
    <script>
      const btn = document.querySelector('button')
      btn.onclick = say
      function say() {
        console.log(event.type)
      }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9

    如果事件处理函数中有this,则this指向元素本身。

    将事件处理程序属性的值设置为null,就可以移除事件处理程序。

    btn.onclick = null
    
    1

    除了使用onclick属性的方式,还可以使用addEventListener()和removeEventListener()方法来监听和解除监听。

     btn.addEventListener('click', function () {
        console.log(this.id)
     })
    
    1
    2
    3

    注意,上面这种方式添加的匿名函数无法移除,必须是同一个事件处理函数。

    # 事件对象

    DOM事件发生时,所有相关信息都会收集到一个event对象中。

    如何区分event.target和event.currentTarget?

    • target,事件的实际目标。
    • currentTarget,当前事件处理程序所在的元素,即this指向的元素。

    上面按钮的target和currentTarget是同一个元素,如果改成监听document.body,则情况有所不同。

      document.body.onclick = function (event) {
        console.log(event.currentTarget) // body
        console.log(event.target) // button
      }
    
    1
    2
    3
    4

    preventDefault(),用于阻止特定事件的默认行为。

    stopPropagation(),用于立即阻止事件流在DOM结构中传播,取消后续的事件捕获或冒泡。

    event对象只在事件处理程序执行期间存在,一旦执行完毕,就会被销毁。

    # 事件类型

    DOM3 Events定义了如下事件类型。

    • 用户界面事件

      • load:有2类,一类是window上页面加载完成后触发;一类是img元素上图片加载完成后触发。

        // 窗口
        window.addEventListener('load', (event) => {
          console.log('loaded')
        })
        // 图片
        let img = document.createElement('img')
        img.addEventListener('load', (event) => {
          console.log(event.target.src)
        })
        document.body.appendChild(img)
        img.src = 'example.gif'
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11

        注意,加载图片不一定要把img元素添加到文档,只要给它设置了src属性就会立即开始下载。

        除了使用DOM元素,也可以直接使用Image对象。

        let img = new Image()
        
        1
      • unload: 在window上当页面完全卸载后触发。一般是从一个页面导航到另一个页面是触发,常用于清理引用。

      • resize:当浏览器窗口被缩放到新高度或宽度时,会触发resize事件。

      • scroll:页面滚动事件。

    • 焦点事件

      • blur:当元素失去焦点时触发,不冒泡。
      • focus:当元素获得焦点时触发,不冒泡。
      • focusin:当元素获得焦点时触发,会冒泡。
      • focusout:当元素失去焦点时触发,会冒泡。
    • 鼠标和滚轮事件

      • click:用户单击鼠标左键或键盘回车键时触发。
      • dblclick:双击鼠标主键时触发。
      • mousedown:按下任意鼠标键时触发。
      • mouseenter:用户把鼠标光标从元素外部移到元素内部时触发。
      • mouseleave:用户把鼠标光标从元素内部移到元素外部时触发。
      • mousemove:鼠标光标在元素上移动时反复触发。
      • mouseout:用户把鼠标光标从一个元素移动到另一个元素上时触发。
      • mouseover:用户把鼠标光标从元素外部移到元素内部时触发。
      • mouseup:用户释放鼠标键时触发。
      • mousewheel:鼠标滚轮滚动时触发。

      区分鼠标事件中event对象的属性,如下:

      • clientX和clientY:表示事件发生时鼠标光标在视口中的坐标,与页面滚动无关。

      • pageX和pageY:表示事件发生时鼠标光标在页面上的坐标,反映的是光标到页面而非视口左边和上边的距离。

      • screenX和screenY:表示光标在屏幕上的坐标,跟浏览器的位置和大小无关。

      • shiftKey、ctrlKey、altKey、metaKey:有时鼠标事件发生时,需要键盘按键辅助,提供了4个修饰键表示是否被按下。

      • button:表示鼠标按键类型:0表示主键,1表示中键,2表示副键。

    • 键盘与输入事件

      • keydown,用户按下键盘某个键时触发,可以重复触发。
      • keypress,DOM3废弃了该事件,推荐textInput。
      • keyup,用户释放键盘上某个键时触发。
      • 键码,event.keyCode属性,表示键盘上特定的键。
      • textInput,只在可编辑区域上且新字符被插入时触发。
    • HTML5事件

      • contextmenu:显示上下文菜单(在Windows上是右击鼠标,Mac上是Ctrl+单击)时触发,网站可拦截该事件从而自定义右键菜单。

        <!DOCTYPE html>
        <html>
          <head>
            <style>
              * {
                margin: 0;
                padding: 0;
              }
              .bg {
                width: 100vw;
                height: 100vh;
                position: relative;
              }
              ul {
                visibility: hidden;
                width: 100px;
                position: absolute;
              }
              li {
                height: 30px;
                border: 1px solid;
                list-style: none;
                background-color: lightblue;
              }
            </style>
          </head>
          <body>
            <div class="bg">
              <ul class="menu">
                <li>自定义1</li>
                <li>自定义2</li>
                <li>自定义3</li>
              </ul>
            </div>
          </body>
        </html>
        
        <script>
          const bg = document.querySelector('.bg')
          const ul = document.querySelector('ul')
          const li = document.querySelector('li')
        
          bg.addEventListener('contextmenu', (event) => {
            event.preventDefault()
            console.log(event.clientX, event.clientY)
            ul.style.left = event.clientX + 'px'
            ul.style.top = event.clientY + 'px'
            ul.style.visibility = 'visible'
          })
          document.addEventListener('click', () => {
            ul.style.visibility = 'hidden'
          })
        </script>
        
        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        25
        26
        27
        28
        29
        30
        31
        32
        33
        34
        35
        36
        37
        38
        39
        40
        41
        42
        43
        44
        45
        46
        47
        48
        49
        50
        51
        52
        53
      • beforeuload:给开发者提供阻止页面被卸载的机会

          window.addEventListener('beforeunload',(event)=>{
            let message = '确认退出吗'
            event.returnValue = message
            return message
          })
        
        1
        2
        3
        4
        5
      • DOMContentLoaded:DOM树构建完成后立即触发,不需要等待外部资源加载完成,从而让用户能够更快地与页面交互。

      • hashchange:url#后的值发生变化时触发,常用于单页应用的路由导航。

    • 设备事件

      • orientationchange,判断用户设备处于垂直模式还是水平模式。
      • deviceorientation:获取设备的加速计信息,可以用于摇一摇功能。
      • devicemotion:检测设备的移动信息。
    • 触摸和手势事件

      • touchstart:手指放到屏幕上时触发。
      • touchmove:手指在屏幕上滑动时连续触发。
      • touchend:手指从屏幕上移开时触发。
      • touchcancel:系统停止跟踪触摸时触发。
      • gesturestart:一个手指已经放在屏幕上,另一个手指开始放到屏幕时触发。
      • gesturechange:任意一个手指在屏幕上的位置发生变化时触发。
      • gestureend:其中一个手指离开屏幕时触发。

    # 内存与性能

    页面中事件处理程序的数量与页面整体性能直接相关,原因有2点:

    1. 每个函数都是对象,数量越多,占用内存越大。
    2. 为事件处理程序绑定DOM后,会先去访问DOM,造成页面交互延迟。

    从这2点出发,诞生出事件委托和删除事件处理程序2个办法。

    # 事件委托

    利用事件冒泡,可以使用一个祖先节点的事件来管理一种类型的事件,而不用每个节点都绑定事件。

    # 删除事件处理程序

    事件绑定到元素后,双方就建立联系,即使DOM树被移除,由于事件绑定关系未解除,DOM节点实际并没有真正释放,就不会被垃圾回收机制清理。比如:

    <!DOCTYPE html>
    <head>
    </head>
    <body>
      <div class="myDiv">
        <button>click me</button>
      </div>
    </body>
    </html>
    
    <script>
      const btn = document.querySelector('button')
      const myDiv = document.querySelector('.myDiv')
      btn.onclick = function(){
        btn.onclick = null // 必要!删除事件处理程序
        myDiv.innerHTML = 'Loading'
      }
    </script>
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    #读书笔记
    上次更新: 2022/02/15, 07:59:55
    第16章 DOM2和DOM3
    第18章 动画与Canvas图形

    ← 第16章 DOM2和DOM3 第18章 动画与Canvas图形→

    最近更新
    01
    提交代码时修改commit消息
    04-09
    02
    如何快速定位bug
    02-20
    03
    云端web项目开发踩坑
    08-25
    更多文章>
    Theme by Vdoing | Copyright © 2021-2025 dwfrost | 粤ICP备2021118995号
    • 跟随系统
    • 浅色模式
    • 深色模式
    • 阅读模式
    ×