HTML坐标系与鼠标事件坐标

在开发中处理鼠标事件时,经常会碰到 offset、scroll、client 这几个关键字,每次处理页面元素的坐标和偏移前,都要网上去搜资料,还会踩一些坑,影响开发效率。这里总结一下,一劳永逸。

一、图解HTML的四个坐标系统

  HTML有四个常见的坐标系统:screen,page,client和offset,用于描述DOM元素的Box尺寸和MouseEvent中的位置。
  如下图所示:
图解html坐标系


1. screen坐标系
  参照点:用户显示器屏幕左上角。
  screenX:鼠标点击位置相对于电脑屏幕左上角的水平偏移量。
  screenY:鼠标点击位置相对于电脑屏幕左上角的垂直偏移量。
  screen坐标的最大范围是 (screen.width, screen.height),最大值不会超过屏幕分辨率。

  补充:

    // 屏幕宽度。
    screen.width

    // 屏幕高度。
    screen.height

    // 屏幕可用宽度。即屏幕宽度减去左右任务栏后的宽度,可表示为浏览器最大化时的宽度。
  screen.availWidth

    // 屏幕可用高度。即屏幕高度减去上下任务栏后的高度,可表示为浏览器最大化时的高度。
  screen.availHeight

    // 任务栏高/宽度。如:任务栏高/宽度 = 屏幕高/宽度 - 屏幕可用高/宽度。
  screen.height - screen.availHeight

2. page坐标系
  参照点:整个页面的左上角(整个页面的意思就是你整个网页的全部,按照整个html文档的长度和宽度来计算)。
  pageX:鼠标点击位置相对于网页左上角的水平偏移量,也就是 clientX + 水平滚动条滚动的距离。
  pageY:鼠标点击位置相对于网页左上角的垂直平偏移量,也就是 clientY + 垂直滚动条滚动的距离。
  * 因此,坐标系上某一个元素的pageX/pageY 不会 随着滚动条滚动而改变。
  page坐标的最大范围是 (document.body.clientWidth + 垂直滚动条宽度, document.body.clientHeight + 水平滚动条高度)。
  * 比如说网页很宽很长,宽2000px,高3000px,那 pageX/pageY 的最大值就是它们了。

  补充:

    // 浏览器宽度。
    window.outerWidth

    // 浏览器高度。
    window.outerHeight

    // 浏览器内页面可用宽度,此宽度包含了垂直滚动条的宽度(若存在)。可表示为浏览器当前宽度去除浏览器边框后的宽度。
  window.innerWidth

    // 浏览器内页面可用高度,此高度包含了水平滚动条的高度(若存在)。可表示为浏览器当前高度去除浏览器边框、工具条后的高度。
  window.innerHeight

    // 工具栏高/宽度,包含了地址栏、书签栏、浏览器边框等范围。如:工具栏高度 = 浏览器高度 - 页面可用高度。
  window.outerHeight - window.innerHeight

3. client
  参照点:浏览器内容区域左上角(即浏览器中用户所看到区域的左上角,内容区域不包括工具栏和滚动条)。
  clientX:鼠标点击位置相对于浏览器可视区域的水平偏移量(不会计算水平滚动的距离)。
  clientY:鼠标点击位置相对于浏览器可视区域的垂直偏移量(不会计算垂直滚动的距离)。
  * 因此,坐标系上某一个元素的clientX/clientY 随着滚动条滚动而改变。
  client坐标的最大范围是 (window.innerWidth - 垂直滚动条宽度, window.innerHeight - 水平滚动条高度)。
  * 计算这个坐标时,由于是基于浏览器窗口中用来显示网页的可视区域,那么也就是说需要拖动滚动条才能看到的区域不算;当你将浏览器窗口缩小时,clientX/clientY 的最大值也会缩小,但始终,它们的最大值不会超过你浏览器可视区域。

  补充:

    // body展示的宽度,表示body在浏览器内显示的区域宽度。
    document.body.clientWidth

    // body展示的高度,表示body在浏览器内显示的区域高度。
    document.body.clientHeight

4. offset
  参照点:父级中最近的一个带有CSS定位(position为absolute/relative)的父元素,如果当前元素的父级元素中没有进行CSS定位,那么就是body。
  offsetX:鼠标点击位置相对于触发事件对象的水平距离。
  offsetY:鼠标点击位置相对于触发事件对象的垂直距离。
  offset坐标的最大范围是 (document.body.offsetWidth, document.body.offsetHeight)。

  补充:

    // body总宽度。
    document.body.offsetWidth

    // body总高度。
    document.body.offsetHeight

二、图解鼠标事件坐标

  鼠标事件都是在特定位置发生的,我们可以通过event事件对象的各种属性来获得事件发生的坐标位置。
  常见的鼠标事件有下面这几种:
1. onclick
  鼠标点击事件

  let el = document.querySelector('#element-id');
  el.onclick = function(e){
      console.log(e)
  }

2. onmousedown
  鼠标按下事件

  let el = document.querySelector('#element-id');
  el.onmousedown = function(e){
      console.log(e)
  }

3. onmouseup
  鼠标松开事件

  let el = document.querySelector('#element-id');
  el.onmouseup = function(e){
      console.log(e)
  }

4. onmousemove
  鼠标移动事件

  let el = document.querySelector('#element-id');
  el.onmousemove = function(e){
      console.log(e)
  }

5. onmouseover
  鼠标经过事件

  let el = document.querySelector('#element-id');
  el.onmouseover = function(e){
      console.log(e)
  }

6. onmouseout
  鼠标划出事件

  let el = document.querySelector('#element-id');
  el.onmouseout = function(e){
      console.log(e)
  }

  根据以上打印的e的信息,大致为:
鼠标点击打印的MouseEvent信息


  由鼠标事件(MouseEvent)可以发现:其中包含了许多的坐标,且每个坐标的含义都不一样。下面我们来挨个介绍常用的坐标,以及它们的含义。
  如图所示,假设页面中灰色圆点是鼠标点击处,棕色区域是鼠标触发事件对象。
图解鼠标事件坐标


  结合前面的图,我们可以看到:e.x,e.y分别和e.clientX,e.clientY相等。

  事实上,e.layerX/e.layerY 与 e.x/e.y 这两个属性比较特殊考虑到它们不那么常见(因为受浏览器种类影响),而且使用时需要考虑的情况相对复杂(layerX/layerY对于absolute绝对定位元素,参考点是当前点击元素的左上角;对于relative相对定位元素,通常与pageX/pageY的值是相同的),这里不展开讨论(实用主义,感兴趣的可以单独拎出来尝试)。

三、不同浏览器对这些属性的支持

Chrome Firefox IE8 - IE9 IE10 +
offsetX ×
clientX
pageX ×
screenX
layerX ×
x ×

  上面列举的这些浏览器都在不断迭代版本,而且市面上还有很多其它浏览器,所以这部分内容需要实时参考MDN上的浏览器兼容性表格

四、总结

  上面那些属性都是很容易搞混的,常用的记在这里方便以后速查。一些特殊的属性尤其要注意不同的浏览器中可能存在的差别,使用的时候测试一下就能更准确的应用了。

  整理这篇文章的契机,就是我在编写cceditor这个项目的时候。

  如有异议,可以在留言区评论,或直接发送您宝贵的意见至我的邮箱。


参考
MDN
若干文章


  目录