# 关于窗口宽高
我们可能经常需要迷惑于获取各种宽度和高度问题, 所以统一系统整理一下看看。
对于盒模型和box-sizing, 就暂且按下不表了。我们主要看一下下面几个概念, 心中有个大概的概念最好
# clientWidth
一般我们理解为元素的可见内容宽度;
clientWidth = content + padding
内联元素以及没有 CSS 样式的元素的 clientWidth
属性值为 0。Element.clientWidth
属性表示元素的内部宽度,以像素计。该属性包括内边距 padding,但不包括边框 border、外边距 margin 和垂直滚动条(如果有的话)。
WARNING
该属性值会被四舍五入为一个整数。如果你需要一个小数值,可使用 element.getBoundingClientRect()
(opens new window)。
比如下面的例子, container元素的clientWidth = 300 - scrollbar + 40(padding)
<style>
* {
padding: 0;
margin: 0;
}
.container {
width: 300px;
height: 200px;
border: 10px solid red
padding: 20px;
margin: 30px;
overflow: auto;
}
.content {
width: 100%;
height: 600px;
}
</style>
<div class="container">
<div class="content"></div>
</div>
盒模型为,宽度只有285是因为Chrome的滚动条默认宽度是15px
# offsetWidth
返回一个元素的布局宽度,一般我们可以理解为一个整体的宽度
offsetWidth = border + padding + scrollbar + content
上述盒模型 offsetWidth = 285 + 15 + 20 * 2 + 10 * 2 = 360
WARNING
这个属性将会 round(四舍五入)为一个整数。如果你想要一个fractional(小数)值,请使用element.getBoundingClientRect()
(opens new window).
# scrollWidth、scrollHeight
这两个属性一般就可以理解为盒子内容的宽高
WARNING
这个属性会进行四舍五入并返回整数,如果你需要小数形式的值,使用
element.getBoundingClientRect()
(opens new window).在实际测试过程中,谷歌获取的
Element.scrollWidth
和 IE,火狐下获取的Element.scrollWidth
并不相同
因为一些差异的关系,经测试FF会包含padding,Chrome不会, 而且在内容不超出容器高度时,表现也不一致
所以我们简化一下我们的模型,去除容器元素的内外边距,那么就可以理解为内容的宽高, 同时不包含滚动条
<style>
* {
padding: 0;
margin: 0;
}
.container {
width: 300px;
height: 200px;
overflow: auto;
}
.content {
width: 100%;
height: 600px;
}
</style>
<div class="container">
<div class="content">hello</div>
</div>
container
content
container.scrollWidth = 285
container.scrollHeight = 600
TIP
scrollHeight用在上拉 ·加载更多·的情况中较多
# scrollTop、scrollLeft
两者一样的属性,我们理解scrollTop即可
Element.scrollTop
属性可以获取或设置一个元素的内容垂直滚动的像素数。一个元素的 scrollTop
值是这个元素的内容顶部(卷起来的)到它的视口可见内容(的顶部)的距离的度量。
获取像素值
const scrollTop = container.scrollTop
设置像素值
container.scrollTop = intValue
示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
<style>
* {
padding: 0;
margin: 0;
}
.container {
width: 300px;
height: 200px;
border: 10px solid red;
padding: 20px;
overflow: auto;
}
.content {
width: 100%;
height: 600px;
}
</style>
</head>
<body>
<div class="container">
<div class="content">hello</div>
</div>
<script>
const container = document.querySelector('.container');
container.addEventListener('scroll', (e) => {
console.log(container.scrollTop);
});
</script>
</body>
</html>
通过设定scrollTop的值,同样的操作我们可以联想到Element.scrollTo
,参考MDN Element.scrollTo (opens new window)
还可以联想到MDN Element.scrollIntoView (opens new window)
# 上拉加载更多
通过上述的知识我们可以把上拉加载更多的实现思路理出来
const threshold = container.scrollHeight - container.scrollTop - container.offsetHeight
threshold
就是还没有滚动出来, 看不见的内容距离,通过监听scroll事件配合节流函数, 我们判断阈值是否小于某个值,如果满足条件则加载更多内容到container
可以参考我借鉴Element实现的无限滚动InfiniteScroll-Vue2-Directives (opens new window)
# Window.innerWidth、innerHeight
innerWidth是Window对象的只读属性,返回以像素为单位的窗口的内部宽度。如果垂直滚动条存在,则这个属性将包括它的宽度。
警告
一般的Element是没有innerWidth属性的。
如果你需要获取除去滚动条和边框的窗口宽度,请使用根元素 <html>
的clientWidth
属性。
# Element.getBoundingClientRect
该方法返回元素的大小以及相对视口的位置。
如果是标准盒子模型,元素的尺寸等于width/height
+ padding
+ border-width
的总和。如果box-sizing: border-box
,元素的的尺寸等于 width/height
。
返回一个DomRect对象
{
bottom: 630;
left: 30;
right: 315;
top: 30;
width: 285;
height: 600;
x: 30;
y: 30;
}
# ScrollX、pageOffsetX ScrollY、pageOffsetY
ScrollX返回文档/页面水平方向滚动的像素值。都是来自于window的只读属性
TIP
pageXOffset
属性是 scrollX
属性的别名
const x = window.scrollX
// window.pageXOffset == window.scrollX; // 总是 true
WARNING
为了跨浏览器兼容性,请使用 window.pageXOffset
代替 window.scrollX
。另外,旧版本的 IE(<9)两个属性都不支持,必须通过其他的非标准属性来解决此问题
如果body可以滚动的话, 可以获取body.scrollLeft
const x = (window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft;
const y = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
# offsetLeft、offsetTop
HTMLElement.offsetLeft
返回当前元素左上角相对于 HTMLElement.offsetParent
(opens new window) 节点的左边界偏移的像素值。
如何理解 HTMLElement.offsetParent
, 它返回一个指向最近的(指包含层级上的最近)包含该元素的定位元素或者最近的 table
,td
,th
,body
元素。 说重点: 定位
所以我们可以通俗的理解为offsetLeft 返回相对最近的定位的父级元素的偏移。
可以对比Element.style.top
# PageX、PageY
PC上可能是MouseEvent.pageX
, 移动端可能是Touch.pageX
这个属性将基于文档的边缘,考虑任何页面的水平方向上的滚动。举个例子,如果页面向右滚动 200px 并出现了滚动条,这部分在窗口之外,然后鼠标点击距离窗口左边 100px 的位置,pageX 所返回的值将是 300。
# clientX、clientY
PC上可能是MouseEvent.clientX
, 移动端可能是Touch.clientX
MouseEvent.clientX
是只读属性, 它提供事件发生时的应用客户端区域的水平坐标 (与页面坐标不同)。例如,不论页面是否有水平滚动,当你点击客户端区域的左上角时,鼠标事件的 clientX
值都将为 0
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
* {
padding: 0;
margin: 0;
}
html,
body {
width: 100%;
height: 100%;
}
.container {
width: 200%;
height: 200%;
background-image: radial-gradient(closest-side,#3f87a6, #ebf8e1,#f69d3c);
}
</style>
</head>
<body>
<div class="container"></div>
<script>
const container = document.querySelector('.container');
container.addEventListener('click', (e) => {
console.log(`pageX=${e.pageX}; clientX=${e.clientX}`);
});
container.addEventListener('mousemove', (e) => {
console.log(`pageX=${e.pageX}; clientX=${e.clientX}`);
});
</script>
</body>
</html>
TIP
这些clientX,pageX的作用可以用于实现签字逻辑等功能
pageX和clientX的区别在于前者考虑滚动, 后者不考虑滚动