如何获取页面元素的位置

背景:最近在商品列表项目迭代中,须要在商品列表底部增长一个分销商品广告位,另外接收到一个产品曝光度的埋点需求,须要知道产品出如今用户视口后在进行数据统计!javascript

基于虚拟 DOM 数据驱动的思想,最不提倡的就是 jquery 时代的 DOM 操做!可是在目前一些复杂的页面中常常仍是会用 javascript 处理一些 DOM 元素,实现一些动态效果;最多见的是用到一些元素的位置和尺寸的计算,可是其中浏览器的兼容性问题也是不可忽略的一部分,要想写出预想效果的JavaScript代码,咱们须要了解一些基本知识。java

基本概念

网页大小:一张网页的所有面积,就是它的大小。一般状况下,网页的大小由内容和 CSS 样式表决定。 浏览窗口大小:指的是在浏览器窗口中看到的那部分网页面积,又叫作 viewport (视口)。 若是网页的内容可以在浏览器中所有显示(也就不出现滚动条),那么网页的大小和浏览器窗口的大小是相等的。若是不能所有显示,则滚动浏览器窗口,能够显示出网页的各个部分。jquery

基本元素属性

在每一个HTML元素都有下列属性。浏览器

offsetWidth clientWidth scrollWidth
offsetHeight clientHeight scrollHeight
offsetLeft clientLeft scrollLeft
offsetTop clientTop scrollTop

为了理解方便这些属性,咱们须要知道 HTML 元素的实际内容有可能比分配用来容纳内容的盒子更大,所以可能会出现滚动条,内容区域是视口,当实际内容比视口大的时候,须要把元素的滚动条位置考虑进去。函数

  • clientHeight 和 clientWidth 用于描述元素内尺寸,是指元素内容+内边距大小,不包括边框(IE下实际包括)、外边距、滚动条部分
  • offsetHeight 和 offsetWidth 用于描述元素外尺寸,是指元素内容+内边距+边框,不包括外边距和滚动条部分
  • clinetTop 和 clinetLeft 返回内边距的边缘和边框的外边缘之间的水平和垂直距离,也就是左,上边框宽度
  • offsetTop 和 offsetLeft 表示该元素的左上角(边缘外边框)与已定位的父容器(offsetParent对象)左上角的距离
  • offsetParent 对象是指元素最近的定位(relative、absolute)祖先元素,递归上溯,若是没有祖先元素是定位的话,会返回 null

获取视口大小

网页上的每一个元素,都有 clientHeight 和 clientWidth属性。这两个属性指元素的内容部分再加上 padding 的所占据的视觉面积,不包括 border 和滚动条占用空间。工具

所以,document 元素的 clientHeight 和 clientWidth 属性,就表明了网页的大小ui

function getViewport() {
  if(!document) {
    return {}
  }
  if (document.compatMode === 'BackCompat') {
    return {
      width: document.body.clientWidth,
      height: document.body.clientHeight
    };
  }
  return {
    width: document.documentElement.clientWidth,
    height: document.documentElement.clientHeight
  };
}
复制代码

上面的 getViewport 函数就是能够返回浏览器窗口的高和宽。使用的时候,有三个地方须要注意:spa

  • 该函数必须在页面加载完成后才能运行,不然 document 对象尚未生成,浏览器会报错。
  • 大多数状况下,都是 document.documentElement.clientWidth 返回正确值。可是,在 IE6 的 quirks 模式中, document.body.clientWidth 返回正确的值,所以函数中加入了对文档模式的判断
  • clientWidth 和 clientHeight 都是只读属性,不能对它们赋值。

获取视口大小的另外一种方式

网页中的每个元素还有 srcollHeight 和 scrollWidth 属性,指包含滚动在内的该元素的视觉面积。 那么,document 对象的 scrollHeight 和 scrollWidth 属性就是网页的大小,意思就是滚动条滚过的全部长度和宽度。code

仿照 getViewport 函数,能够写出 getPagearea() 函数。cdn

function getPagearea() {
 if (document.compatMode == 'BackCompat') {
  return {
     width: Math.max(document.body.scrollWidth, document.body.clientWidth),
     height: Math.max(document.body.scrollHeight, document.body.clientHeight)
    };
 }
 return {
    width: Math.max(document.documentElement.scrollWidth, document.documentElement.clientWidth),
   height: Math.max(document.documentElement.scrollHeight, document.documentElement.clientHeight)
  };
}

复制代码

相对文档与视口的坐标

当咱们在计算一个 DOM 元素位置也就是坐标的时候,会涉及到两种坐标系, 文档坐标__和__视口坐标。 咱们常常用到的document就是整个页面部分,而不只仅是窗口可见部分,还包括由于窗口大小限制而出现滚动条的部分,它的左上角就是咱们所谓相对于文档坐标的原点。

视口是显示文档内容的浏览器的一部分,它不包括浏览器外壳(菜单,工具栏,状态栏等),也就是当前窗口显示页面部分,不包括滚动条。

若是文档比视口小,说明没有出现滚动,文档左上角和视口左上角相同,通常来说在两种坐标系之间进行切换,须要加上或减去滚动的偏移量(scroll offset)。

为了在坐标系之间进行转换,咱们须要断定浏览器窗口的滚动条位置。window对象的pageXoffset和pageYoffset提供这些值,IE 8及更早版本除外。也能够经过scrollLeft和scrollTop属性得到滚动条位置,正常状况下经过查询文档根节点(document.documentElement)来得到这些属性值,但在怪异模式下必须经过文档的body上查询

image.png | left | 500x374

文档坐标

任何HTML元素都拥有offectLeft和offectTop属性返回元素的X和Y坐标,对于不少元素,这些值是文档坐标,可是对于以定位元素后代及一些其余元素(表格单元),返回相对于祖先的坐标。咱们能够经过简单的递归上溯累加计算

function getElementPosition(e) {
  let x = 0;
  let y = 0;
  while (e != null) {
    x += e.offsetLeft;
    y += e.offsetTop;
    e = e.offsetParent;
  }
  return { x, y };
}
复制代码

尽管如此,这个函数也不老是计算正确的值,当文档中含有滚动条的时候这个方法就不能正常工做了,咱们只能在没有滚动条的状况下使用这个方法,不过咱们用这个原理算出一些元素相对于某个父元素的坐标。

快速方法:

网页元素的相对位置就是:

let X = element.getBoundingClientRect().left;
let Y = element.getBoundingClientRect().top;
复制代码

视口坐标

计算视口坐标就相对简单了不少,能够经过调用元素getBoundingClientRect  方法。方法返回一个有left、right、top、bottom属性的对象,分别表示元素四个位置的相对于视口的坐标。getBoundingClientRect 所返回的坐标包含元素的内边距和边框,不包含外边距。兼容性很好,很是好用

function getElementViewTop(element) {
  let actualTop = element.offsetTop;
  let current = element.offsetParent;
  let elementScrollTop;

  while (current !== null) {
    actualTop += current.offsetTop;
    current = current.offsetParent;
  }

  if (document.compatMode == 'BackCompat') {
    elementScrollTop = document.body.scrollTop;
  } else {
    elementScrollTop = document.documentElement.scrollTop;
  }

  return actualTop - elementScrollTop;
}


function getElementViewLeft(element) {
  let actualLeft = element.offsetLeft;
  let current = element.offsetParent;
  let elementScrollLeft;

  while (current !== null) {
    actualLeft += current.offsetLeft;
    current = current.offsetParent;
  }

  if (document.compatMode == 'BackCompat') {
    elementScrollLeft = document.body.scrollLeft;
  } else {
    elementScrollLeft = document.documentElement.scrollLeft;
  }

  return actualLeft - elementScrollLeft;
}
复制代码

快速方法:

使用 getboundingClientRect() 方法。它返回一个对象,其中包含了 lefttopwidthheight 等属性。

let X = element.getBoundingClientRect().left;
let Y = element.getBoundingClientRect().top;
复制代码

再加上滚动 距离,就能够获得绝对位置:

const X= element.getBoundingClientRect().left+document.documentElement.scrollLeft;
const Y =element.getBoundingClientRect().top+document.documentElement.scrollTop;
复制代码
相关文章
相关标签/搜索