背景:最近在商品列表项目迭代中,须要在商品列表底部增长一个分销商品广告位,另外接收到一个产品曝光度的埋点需求,须要知道产品出如今用户视口后在进行数据统计!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上查询。
任何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()
方法。它返回一个对象,其中包含了 left
、 top
、 width
、 height
等属性。
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;
复制代码