回顾

  • 立即执行函数:马上执行
  • 定时器函数:指定时间执行
  • DOM 事件函数:特点条件触发并执行

# 一、DOM 事件

  常用的事件使用方式:

var odiv = document.querySelector('.box')

odiv.onclick = function () {
  console.log('你点击了div')
}

  关于触发对象:

var odiv = document.querySelector('.box')

odiv.onclick = function (e) {
  console.log(this === e.target) // true
}

# 1、DOM 0 级事件

  • 单个 DOM0
box.onclick = function () {
  console.log('点击成功')
}

// 根据对象的思想,下面的会覆盖上面的
box.onclick = function () {
  console.log('你点击了div')
}

# 2、DOM 2 级事件

  • 绑定多个 DOM2
box2.addEventListener('click', function () {
  console.log('点击成功')
})

box2.addEventListener('click', function () {
  console.log('你点击了div')
})

# 3、其他

EventTarget 接口 (opens new window)

  • 事件解绑
// 1、普通方式dom0
btn.onclick = function () {
  console.log('谢谢惠顾')

  this.onclick = null
}

// 2、高级方式dom2(注意写法🎉)
function handler() {
  console.log('谢谢惠顾')

  this.removeEventListener('click', handler) // 一一对应
}
btn.addEventListener('click', handler)

# 二、事件类型

# 浏览器事件

  在 BOM 部分我们知道了浏览器事件 onloadonresizeonscroll 等,在 DOM 事件中,我们可以结合它做一些事件类型内容小测试:

window.onload = function () {
  var btn = document.getElementById('btn')
  btn.onmousedown = function () {
    // 方式1
    this.style.background = '#ffefa1'
  }
  btn.onmouseup = function () {
    // 方式2
    btn.style.background = '#f4f9f9'
  }
}

this 指向回顾

  若使用的是事件绑定方法,那方法内的 this 指向绑定事件的对象

# 1、鼠标事件

  • 点击(完整过程 --> 结果)
box.onclick = function () {
  console.log('左键单击')
}

box.ondblclick = function () {
  console.log('双击')
}

document.oncontextmenu = function () {
  console.log('右键单击')
}
  • 鼠标行为(拖拽功能)
mousedown //少
mouseup // 点击反悔👀(在其他地方松开)

mousemove // 移动时元素位置获取
  • 鼠标状态(跟随功能)
// (子盒子有效)
mouseover ✨
mouseout

// (子盒子无效) 即:不会发生冒泡行为
mouseenter
mouseleave

mousemove // 移动时元素位置获取

应用:

1、自定义菜单
2、div 盒子鼠标拖动效果

# 2、键盘事件

  通常在windowdocumentinput输入框等中使用。

<body>
  <input type="text" id="username" />
  <script>
    username.onkeydown = function () {
      console.log('按下了键盘')

      // 后续可以判断按下的是什么键
    }

    username.onkeyup = function () {
      console.log('抬起了键盘')
    }
  </script>
</body>

应用:

发表评论

# 3、表单事件

<body>
  <form action="" id="myform">
    <input type="text" id="username" />
    <input type="reset" />
    <input type="submit" value="提交" />
  </form>
  <script>
    // 1、焦点角度
    username.onfocus = function () {
      console.log('获取焦点')
    }

    username.onblur = function () {
      console.log('失去焦点')

      // 内容校验
    }

    //  2、内容角度
    username.oninput = function () {
      console.log('输入内容时:内容发生改变')
    }

    username.onchange = function () {
      console.log('失去焦点时:内容发生改变') // 💖
    }

    // 3、大局角度(form表单的事件)
    myform.onreset = function () {
      //
    }

    myform.onsubmit = function () {
      // 内容校验

      return false // 阻止提交跳转
    }
  </script>
</body>

应用:

表单校验

# 4、触摸事件(移动端)

touchstart // 手指触摸
touchmove // 滑动
touchend // 手指离开

# 三、应用场景

  事件发生以后,会产生一个事件对象,它会作为参数传给监听函数。

# 1、按键监听

keyCode 属性已经从 Web 标准中删除,这里了解即可。

username.onkeyup = function (event) {
  // console.log(event)

  // 兼容性处理
  // event = event || window.event

  if (event.keyCode === 13) {
    console.log('你按下了enter键')
  }
}

# 2、鼠标监听 ✨

鼠标监听事件中,event 对象中的有很多信息需要我们知道

代码示例

在线演示 (opens new window)

box.onclick = function (event) {
  // 1、位置
  // 相对于target的坐标距离
  console.log(event.offsetX, event.offsetY)

  // 相对于浏览器的视口距离
  console.log(event.clientX, event.clientY)

  // 相对于电脑屏幕
  console.log(event.screenX, event.screenX)

  // 相对于Document的坐标距离
  console.log(event.pageX, event.pageY)

  // 倾斜角度(陀螺仪)
  console.log(event.tiltX, event.tiltY)

  // 2、其他
  console.log(event.target)
}

图示(此处的 target 为 img 元素):

应用:

1、内容详细盒子的鼠标跟随效果
2、鼠标拖拽盒子效果

拓展:

.box {
  pointer-events: none;
}

# 3、其他

# 四、DOM 事件流 🚩

事件模型 (opens new window)

# 1、事件的传播

  典型的就是在包含父盒子、子盒子等嵌套的盒子上绑定事件时,会发现:

当我们触发子元素的事件时,若其外层元素也有 ✍ 同类型的事件,则也会自动触发。

效果演示

  观察:点击第一个 li 和 点击第二个 li 时控制台打印的内容有什么区别 —— 原生默认只有 🤔 绑定了第一个 li 元素(但是使用 jQuery 是不会出现这种情况的)。

<body>
  <ul>
    <li id="one">第一个子元素</li>
    <li id="two">第二个子元素</li>
    <li id="three">第三个子元素</li>
  </ul>

  <script>
    document.querySelector('ul').onclick = function () {
      console.log('父元素:', this)
    }

    // 默认值只为第一个li绑定click事件
    document.querySelector('ul li').onclick = function () {
      console.log(this.innerHTML)
    }

    // 绑定所有li
    // 方式1:jQuery
    // $('ul li').on('click', function () {
    //   console.log($(this).text())
    // })
    // 方式2:事件代理
    // 略(后面解决)
  </script>
</body>

  总的来说,事件传播分为三个阶段:捕获、目标、冒泡。

注意

  不是所有的事件都会经过三个阶段的传播过程,有些事件仅在目标元素上触发(并不会阻止事件传播机制的执行),常见的有:

  • loadfocusblurscrollresizeresetsubmit

在线演示 (opens new window) 🎈

  传播到最外层还有windowdocument(事件传播的 🤔 边界: 目标元素 和 document),下面看看我写的一个完整案例,体验一些事件传播:

注意

  使用 onclick 或者 addEventListener 方法绑定事件处理程序时,默认情况下事件是在冒泡阶段进行处理。

  如果需要在捕获阶段处理事件,可以使用 addEventListener 方法,并将第三个参数设置为 true。

完整案例
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8" />
    <title>事件传播示例</title>
    <style>
      #outer {
        width: 300px;
        height: 200px;
        background-color: gray;
        padding: 10px;
      }

      #inner {
        width: 200px;
        height: 100px;
        background-color: white;
        padding: 10px;
      }

      #button {
        display: block;
        margin-top: 20px;
        padding: 5px 10px;
        font-size: 16px;
        background-color: blue;
        color: white;
        border: none;
      }
    </style>
  </head>
  <body>
    <div id="outer">
      outer盒子
      <div id="inner">
        inner盒子
        <button id="button">点击我</button>
      </div>
    </div>

    <script>
      // 获取DOM元素
      var outerDiv = document.getElementById('outer')
      var innerDiv = document.getElementById('inner')
      var buttonEl = document.getElementById('button')

      // 注册事件监听器
      outerDiv.addEventListener(
        'click',
        function (event) {
          console.log('捕获阶段--outer盒子')
        },
        true
      ) // 捕获阶段(若设置为true,表示捕获阶段触发)

      innerDiv.addEventListener(
        'click',
        function (event) {
          console.log('捕获阶段--inner盒子')
        },
        true
      )

      // =================

      buttonEl.addEventListener('click', function (event) {
        console.log('按钮被点击了!')
        event.stopPropagation() // 停止button元素的事件传播
      })

      // =================

      innerDiv.addEventListener(
        'click',
        function (event) {
          console.log('冒泡阶段--inner盒子')
        },
        false
      ) // 冒泡阶段(默认值为false,表示冒泡阶段触发)

      outerDiv.addEventListener(
        'click',
        function (event) {
          console.log('冒泡阶段--outer盒子')

          console.log('------事件传播结束------')
        },
        false
      )
    </script>
  </body>
</html>

# 2、阻止事件

  有些情况下,我们可以对目标元素进行一些事件控制:

  • 阻止事件冒泡(event.stopPropagation())

可以防止触发了一些不必要的事件

经典应用:弹窗

在线演示 (opens new window)

<div class="modal-box">
  <div class="btn btn-primary sign-btn btn-lg show-modal " onclick="handleClick()">
    立即报名
    <div class="modal" id="myModal" tabindex="-1">
      <div class="modal-dialog" role="document">
        <div class="modal-content">
          <button type="button" class="close" onclick="handleOk()">
            <span aria-hidden="true">&times;</span>
          </button>
          <div class="modal-body">
            <button class="subscribe" onclick="handleOk()">OK</button>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>
function handleClick() {
  document.querySelector('#myModal').style.display = 'inline-block'
}
function handleOk() {
  document.querySelector('#myModal').style.display = 'none'
  window.event.stopPropagation()
}

注意

  如果想要用 οnclick="xxx()" 来调用点击事件时,正确写法为:

function xxx() {
  //阻止向上冒泡
  window.event.stopPropagation()
}
buttonEl.addEventListener('click', function (event) {
  // 以上面的综合案例为例
  // 效果:冒泡阶段的事件就无法触发了
  event.stopPropagation()

  // ……
})
  • 阻止默认行为(event.preventDefault())
myform.onsubmit = function () {
  event.preventDefault()

  // ……

  return false // 阻止提交跳转
}

document.oncontextmenu = function () {
  event.preventDefault()

  // ……
  // return false // 停止显示浏览器右键菜单栏
}

应用:

自定义右键菜单

# 3、事件委托/代理

  JavaScript 中的事件代理是一种利用事件冒泡机制,将事件处理程序绑定到祖先元素(通常是父元素)上,以便处理其后代元素触发的事件。

  当后代元素触发事件时,事件将冒泡到祖先元素,并且由该元素的事件处理程序进行处理

  • 父元素中的event.target

体验一下在事件冒泡机制下,父元素的事件是如何触发的

<body>
  <ul id="list">
    <li>
      <p>lencamo</p>
      <button id="inner">点击一些看看效果</button>
    </li>
  </ul>

  <script>
    list.onclick = function (event) {
      console.log('ul触发了click事件')

      // 点击了自己/子元素
      console.log('你点击了:', event.target)
      // console.log(event.target, '触发了click事件')
    }
  </script>
</body>
效果演示-案例升级

通过将 click 事件处理程序绑定到 ul 元素上,来处理其后代 li 元素的点击事件

<body>
  <ul>
    <li id="one">第一个子元素</li>
    <li id="two">第二个子元素</li>
    <li id="three">第三个子元素</li>
  </ul>

  <script>
    document.querySelector('ul').onclick = function () {
      if (event.target.tagName === 'LI') {
        console.log(event.target.innerText)
      }
      console.log('父元素:', this)
    }
  </script>
</body>

# 4、event 对象

  对应事件对象 event,在 DOM 事件流中,我们要重点关注event.targetevent.currentTarget 的区别

在线演示 (opens new window)

<div class="outer" onclick="outerClickFn()">
  <div class="inner"></div>
</div>
function outerClickFn(event) {
  console.log(window.event.target) // 触发事件的元素
  // console.log(window.event.currentTarget) // 执行(处理)事件的元素
}
更新于 : 8/7/2024, 2:16:31 PM