理解JavaScript9种鼠标事件

1,988 阅读4分钟

一、9种鼠标事件

DOM3级事件中定义了9个鼠标事件。

  1. mousedown
  2. mouseup
  3. click
  4. dblclick
  5. mouseover
  6. mouseout
  7. mouseenter
  8. mouseleave
  9. mousemove

页面中的所有元素都支持鼠标事件。除了mouseentermouseleave,所有鼠标事件都会冒泡,都可以被取消。

可以对9种鼠标事件分为三类:

  • mousedownmousemovemouseup: 按下,移动,释放鼠标。
  • clickdblclick: 单击,双击鼠标。
  • mouseovermouseout:鼠标从一个元素上移入/移出(冒泡)。
  • mouseentermouseleave: 鼠标从一个元素上移入/移出(不冒泡)。

二、mousedownmousemovemouseup

  1. mousedown: 按下任意鼠标键时触发(左键或者右键)。
  2. mousemove: 鼠标光标在元素内移动时不断触发。
  3. mouseup: 鼠标按钮被释放弹起时触发。

mousedownmousemovemouseup 都不能通过键盘触发。

2.1、使用Vue指令实现ElementUI el-drawer抽屉的拖拽调整宽度效果

因为ElementUI的抽屉没有拖拽调整宽度的效果,可以通过mousedownmousemovemouseup这三个事件自定义Vue指令来实现。

  • 定义CSS属性
/* 拖动CSS样式 左右 */
.drawer-resize-lr {
  position: absolute;
  width: 10px;
  height: 100%;
}

/* 拖动CSS样式 上下 */
.drawer-resize-tb {
  position: absolute;
  width: 100%;
  height: 10px;
}
  • 定义Vue指令

/**
 * elementUI抽屉拖拽指令 drawer-move.js
 * ltr 从左往右
 * rtl 从右往左
 * ttb 从上往下
 * btt 从下往上
 */
export default {
  // 指令第一次绑定到元素时调用
  bind(el, binding) {
    if (!el.querySelector('.el-drawer')) return
    // 定义传给指令的参数,默认从右往左
    const dire = binding.arg || 'rtl'
    // 自定义左右拖拽最小宽度
    const dragMinWidth = 400
    // 自定义上下拖拽最小高度
    const dragMinHeight = 300
    const parent = el.querySelector('.el-drawer')
    // 定义位置样式
    const posi = {
      ltr: ';right:0;cursor:e-resize;',
      rtl: ';left:0;cursor:w-resize;',
      ttb: ';bottom:0;cursor:s-resize;',
      btt: ';top:0;cursor:n-resize;'
    }
    // 创建span元素并设置样式,添加到parent元素下
    const dragEl = document.createElement('span')
    dragEl.className = dire === 'ltr' || dire === 'rtl' ? 'drawer-resize-lr' : 'drawer-resize-tb'
    dragEl.style.cssText += posi[dire]
    parent.appendChild(dragEl)

    // 鼠标按下
    dragEl.onmousedown = (e) => {
      // x轴坐标
      const disX = e.clientX
      // y轴坐标
      const disY = e.clientY
      // .el-drawer元素宽度
      const parentWidth = parent.offsetWidth
      // .el-drawer元素高度
      const parentHeight = parent.offsetHeight

      // 通过事件委托定义鼠标移动事件
      document.onmousemove = (ev) => {
        // 定义拖拽最大宽度
        const maxW = document.documentElement.clientWidth - 10
        // 定义拖拽最大高度
        const maxH = document.documentElement.clientHeight - 10
        // 左移动距离
        const moveLeft = ev.clientX - disX
        // 上移动距离
        const moveTop = ev.clientY - disY
        // 左右抽屉
        if (dire === 'rtl' || dire === 'ltr') {
          // 根据打开方向计算拖拽宽度
          let w = dire === 'rtl' ? parentWidth - moveLeft : parentWidth + moveLeft
          // 边界处理
          if (w < dragMinWidth) w = dragMinWidth
          if (w > maxW) w = maxW
          parent.style.width = `${w}px`
        }
        // 上下抽屉
        if (dire === 'ttb' || dire === 'btt') {
          // 根据打开方向计算拖拽高度
          let h = dire === 'btt' ? parentHeight - moveTop : parentHeight + moveTop
          // 边界判断
          if (h < dragMinHeight) h = dragMinHeight
          if (h > maxH) h = maxH
          parent.style.height = `${h}px`
        }
      }
      // 鼠标释放
      document.onmouseup = () => {
        document.onmousemove = null
        document.onmouseup = null
      }
    }
  }
}
  • 使用方式
<el-drawer size="520px" :direction="direction" v-drawer-move:[direction] :visible="true"></el-drawer>
    
data() {
  return {
    direction: 'rtl' // ltr,rtl,ttb,btt
  }
}

三、mouseovermouseout

  1. mouseover: 当鼠标移入某元素时触发。
  2. mouseout: 当鼠标移出该元素时触发。

mouseovermouseout事件不能通过键盘触发。这两个事件会冒泡,所以移入和移出其子元素时也会触发。

如下图定义父子元素,并添加事件:

<body id="body">
   <div id="parent">
     parent
     <div id="child">child</div>
   </div>
  parent.addEventListener('mouseover', (e) => {
    console.log('mouseover---->', e.target.firstChild.textContent.trim())
  })

  parent.addEventListener('mouseout', (e) => {
    console.log('mouseout---->', e.target.firstChild.textContent.trim())
  })
</body>

parent元素到child元素,再从child回到parent然后离开,元素移动到另一个元素(包括后代元素),它将离开前一个元素,所以先离开后进入。所以控制台输出如下:

可以把一个元素的移入和移出比做一对双胞胎一样,他们两个总是前后在一起出现。

3.1、 relatedTarget属性

mouseovermouseout这两个事件都涉及从一个元素的边界之内把光标移到另一个元素的边界之内。这些事件很特别,因为它们具有relatedTarget属性。此属性是对target的补充。当鼠标从一个元素离开并去往另一个元素时,其中一个元素就变成了target,另一个就变成了relatedTarget

DOM通过event对象的relatedTarget属性提供了相关元素的信息。这个属性只有在mouseovermouseout 事件发生时才包含值,其他所有事件的这个属性的值都是null

对于mouseover而言:

  • event.target:是鼠标移过的那个元素。
  • event.relatedTarget:是鼠标来自的那个元素(relatedTarget → target)。
<body id="body">
   <div id="parent">
     parent
     <div id="child">child</div>
   </div>
  parent.addEventListener('mouseover', (e) => {
    console.log('target---->', e.target.id)
    console.log('relatedTarget---->', e.relatedTarget.id)
  })
</body>

从外到内移入:从body元素移入到parent元素再到child元素,控制台输出如下:

对于mouseout而言:

  • event.target:鼠标离开的元素。
  • event.relatedTarget:离开目标元素时,鼠标进入的元素。
<body id="body">
   <div id="parent">
     parent
     <div id="child">child</div>
   </div>
  parent.addEventListener('mouseout', (e) => {
    console.log('target---->', e.target.id)
    console.log('relatedTarget---->', e.relatedTarget.id)
  })
</body>

从内到外移出:从child元素移出到parent元素再到body元素,控制台输出如下:

结合mouseovermouseout监听移动:

<body id="body">
   <div id="parent">
     parent
     <div id="child">child</div>
   </div>
    box.addEventListener('mouseover', (e) => {
      console.log('target---->', e.target.id)
      console.log('relatedTarget---->', e.relatedTarget.id)
      console.warn('mouseover----> END')
      console.log('\n')
    })

    box.addEventListener('mouseout', (e) => {
      console.warn('mouseout----> START')
      console.log('target---->', e.target.id)
      console.log('relatedTarget---->', e.relatedTarget.id)
      console.warn('mouseout----> END')
      console.log('\n')
    })
</body>

控制台输出如下:

四、mouseentermouseleave

  1. mouseenter: 当鼠标移入某元素时触发。

  2. mouseleave: 当鼠标移出某元素时触发。

mouseentermouseleave 这两个事件不冒泡。

如下图定义父子元素:

<body id="body">
   <div id="parent">
     parent
     <div id="child">child</div>
   </div>
  parent.addEventListener('mouseenter', (e) => {
    console.log('mouseenter---->', e.target.firstChild.textContent.trim())
  })

  parent.addEventListener('mouseleave', (e) => {
    console.log('mouseleave---->', e.target.firstChild.textContent.trim())
  })
</body>

parent元素到child元素,再从child回到parent然后离开,控制台输出如下:

五、click,dblclick事件

  1. click: 单击鼠标主键(通常是左键)或按键盘回车键时触发,键盘和鼠标都可以触发onclick事件。
  2. dblclick: 双击鼠标主键(通常是左键)时触发。

六、 参考链接

zh.javascript.info/mousemove-m…

zh.javascript.info/mouse-event…

book.douban.com/subject/351…