浏览器有很多描述元素位置与长度属性,比如 width、offsetWeight、innerWidth等,本文就来归纳一下,并说明其使用方式。
window 对象下的属性
window
对象是 JavaScript 中最重要的全局对象之一,它代表浏览器的窗口。window
对象不仅包括用于浏览器窗口管理的各种方法和属性,还包含一些与浏览器环境、页面内容、事件等相关的功能。
innerHeight / innerWidth / outerHeight / outerWidth
inner 表示浏览器窗口的视口(viewport)高(宽)度(以像素为单位);如果有水平滚动条,也包括滚动条高(宽)度;outer 则表示获取窗口的外层高(宽)度,菜单栏和地址栏也会包括在内,如图所示:
screen系列
screen属性包含 screenLeft、screenX;screenTop、screenY等,都是只读属性,分别返回浏览器左边框到左边屏幕边缘、浏览器上边框到上边屏幕边缘的 CSS 像素数。
- 在现代浏览器中,
screenX
和screenY
通常更常用,因为它们是标准属性,并且在所有现代浏览器中都有支持。 screenLeft
和screenTop
主要是为了在较老的浏览器中提供兼容性而存在的。在现代浏览器中,它们通常与screenX
和screenY
具有相同的值。
可以看下面的案例: demo
小窗口下图解如下:
上图中,70px是 window.screenY, 106px 是 window.screenX.
另外还有 screen 对象, 返回当前渲染窗口中和屏幕有关的属性:
window.screen
:
screen 对象内的属性,跟浏览器的位置、大小没有关系,是浏览器所在屏幕的像素属性。
-
availWidth/availHeight 返回浏览器窗口在屏幕上最大可占用的水平/垂直空间,即最大宽/高度。
-
availTop/availLeft 返回屏幕上/左边边界的第一个像素点,大部分情况下是0,如果你在有两个屏幕的电脑上使用该属性,在下/右侧屏幕计算该属性值时,返回左侧屏幕的宽度(单位:像素),也即下/左侧屏幕左边界的 X 坐标。
-
width/height 返回屏幕的宽高。该属性返回的高度值并不是全部对浏览器窗口可用。小工具(Widgets),如任务栏或其他特殊的程序窗口,可能会减少浏览器窗口和其他应用程序能够利用的空间。
注意:上述 screen 属性在笔记本使用了扩展分屏或者 mac 浏览器采用了全屏后后会产生偏差,慎重使用
scrollX 与 scrollY
返回文档/页面水平(垂直)方向相对于window左上角的滚动像素值。
为了跨浏览器兼容性,可使用
window.pageXOffset
代替window.scrollX
,这两个是等价的。
元素属性
client 系列
只读属性 clientWidth 和 clientHeight 表示元素内部的宽(高)度。对于内联元素以及没有 CSS 样式的元素为 0,该属性包括内边距(padding),但不包括边框(border)、外边距(margin)和垂直滚动条(如果存在)。
在 border-box 的盒子中,计算方式同上。
scroll 系列
scrollHeight / scrollWidth 表示容器内可滚动区域的总高(宽)度。如图:
scrollWidth 是横向滚动的宽度
上面的图片中,scrollTop 也可以看得出来,它指的就是从最顶部开始往下滚动的绝对像素值。而 scrollLeft 类似,是横向滚动时,滚动区域的左侧边距距离容器最左侧的距离(向右滚动的绝对像素)。
offset 系列
offsetWidth / offsetHeight 与 client 系列的区别在于是否包含 border。client 注重可视区域的概念,所以不包含 border;而 offset 系列包含整个元素盒子内容物,所以会带上 border + padding, 而不包含 margin。
设置宽高
box-sizing 属性定义了 user agent 应该如何计算一个元素的总宽度和总高度,其默认值是 content-box。
- content-box
设置完宽度/高度后,其尺寸就是内容物的大小:
上图中,宽度就占用了100%,所以设置完border或者padding以后就溢出了。
- border-box
我们换边框盒子来试试。
上图中,设置宽度100%,是包含了padding和border的尺寸的,所以另外再设置padding和border,盒子就会向内挤压实际内容物的尺寸。
其他
此外还有 *top/*left等属性未做解释,这里给一张图大家理解一下:
上图中没有标识 padding
相关 web API
Element.getBoundingClientRect()
该方法方法返回一个 DOMRect 对象,其提供了元素的大小及其相对于视口的位置。他是包含整个元素的最小矩形(包括 padding 和 border-width)。该对象使用 left、top、right、bottom、x、y、width 和 height 这几个以像素为单位的只读属性描述整个矩形的位置和大小。除了 width 和 height 以外的属性是相对于视图窗口的左上角来计算的。
比如我们有一个空的 div:
css
div {
width: 400px;
height: 200px;
padding: 20px;
margin: 50px auto;
background: purple;
}
js 获取他的 rect属性:
js
let elem = document.querySelector("div");
let rect = elem.getBoundingClientRect();
for (let key in rect) {
if (typeof rect[key] !== "function") {
let para = document.createElement("p");
para.textContent = `${key} : ${rect[key]}`;
// 打印标量参数
document.body.appendChild(para);
}
}
结果:
less
x : 146
y : 50
width : 440 // 包含了 padding 的
height : 240 // 包含了 padding 的
top : 50
right : 586
bottom : 290
left : 146
Element.getClientRects()
返回值是 ClientRect 对象集合.
- 块级元素直接返回盒子模型的矩形范围。
- 行内元素(inline)根据它换行划分成多个盒子边界矩形(返回多个DOMRect)。
比如有下面的元素:
html
// 设置了宽度,元素就会换行了
<div style="width: 120px">
<span class="inline">this is inline. this is inline. this is inline. this is inline. this is inline. this is inline.</span>
<div>
我们重点看一下里边的行内元素:
而 getBoundingClientRect 返回的都是统一的一个矩形区域:
Element.scroll()
是用于在给定的元素中滚动到某个特定坐标的 Element 接口.
js
// 将元素垂直滚动到100px的位置,水平滚动到100px的位置,平滑滚动
element.scroll({
top: 100,
left: 100,
behavior: "smooth",
});
Element.scrollBy()
使得元素滚动一段特定距离,他是 scroll() 的相对位置版。
js
// 将元素垂直向下滚动100px,水平向右滚动100px,平滑滚动
element.scrollBy({
top: 100,
left: 100,
behavior: "smooth",
});
Element.scrollIntoView()
滚动元素的父容器,使被调用 scrollIntoView() 的元素对用户可见。
js
const element = document.getElementById("box");
// 默认将元素就近滚动到可视区域
element.scrollIntoView();
// 将元素的底端和其所在滚动区的可视区域的底端对齐
element.scrollIntoView(false);
// 同上
element.scrollIntoView({ block: "end" });
// 全量参数
element.scrollIntoView({ behavior: "smooth", block: "end", inline: "nearest" });
- behavior 可选 定义滚动是立即的还是平滑的动画。该选项是一个字符串,必须采用以下值之一:
scss
smooth:滚动应该是平滑的动画。
instant:滚动应该通过一次跳跃立刻发生。
auto:滚动行为由 scroll-behavior 的计算值决定。
- block 可选 定义垂直方向的对齐,
sql
start、center、end 或 nearest 之一。默认为 start。
- inline 可选 定义水平方向的对齐,
sql
start、center、end 或 nearest 之一。默认为 nearest。
scrollTo()
使界面滚动到给定元素的指定坐标位置.使用方式同 scroll:
js
element.scrollTo({
top: 100,
left: 100,
behavior: "smooth",
});
边距补充
首先明确一个概念:css布局中,inline元素的排列默认是水平的,从左往右,block元素的排列是垂直的,从上往下。
margin-block / padding-block
margin-block 定义了元素的逻辑块首和块末外边距,并根据元素的书写模式、行内方向和文本朝向对应至实体外边距。
根据 writing-mode、direction 和 text-orientation 所定义的值,此属性对应于 margin-top 和 margin-bottom,或者 margin-right 和 margin-left 属性。
写法: margin-block: margin-block-start margin-block-end
其定义垂直方向的边距:
padding 同理,这里不做过多阐述。
margin-inline / padding-inline
定义水平方向的左右边距:
padding 同理,这里不做过多阐述。
你可以试着设置一下 writing-mode: vertical-lr 看看效果。
上面这些属性,属于css逻辑属性,相似的逻辑属性有不少,包括:block-size,border-block-end,border-block-end-color,border-block-end-style,border-block-end-width,border-block-start,border-block-start-color,border-block-start-style,border-block-start-width,border-inline-end,border-inline-end-color,border-inline-end-style,border-inline-end-width,border-inline-start,border-inline-start-color,border-inline-start-style,border-inline-start-width,inline-size,inset-block-end,inset-block-start,inset-inline-end,inset-inline-start,margin-block-end,margin-block-start,margin-inline-end,margin-inline-start,max-block-size,max-inline-size,min-block-size,min-inline-size,padding-block-end,padding-block-start,padding-inline-end,padding-inline-start。
鼠标事件中获取位置
除了渲染页面元素,还有一个需要计算位置的,就是检测鼠标位置。他需要在 mouse 相关事件中获取:
js
element.addEventListener('mousemove', function(event) {
console.log(event)
})
我们看一下 event 的 属性 中关于位置坐标的参数:
- clientX:
鼠标位置距离当前body可视区域的 x 坐标,以可视区域的左上角为原点,向右为正方向。所以有横滚条后可能会不准确
- pageX:
鼠标位置距离当前body的 x 坐标,以body元素的左上角为原点,向右为正方向。有横滚条后数据仍然是撑开的宽度
- movementX:
表示自上次鼠标移动事件以来,鼠标指针在水平方向上移动的距离。
- screenX:
表示鼠标指针相对于整个显示器屏幕(浏览器以外也算)的水平坐标位置。 以屏幕左上角为原点,向右为正方向。
- offsetX:
相对于事件作用元素最左侧的偏移量。这个属性最常用,可以用来判断鼠标划过的地方在目标元素内部的位置。
- x:
与 clientX 相同。
网上淘换来一张图片以供参考:
附:盒模型
当对一个文档进行布局(lay out)的时候,浏览器的渲染引擎会根据标准之一的 CSS 基础框盒模型 (CSS basic box model),将所有元素表示为一个个矩形的盒子(box)。CSS 决定这些盒子的大小、位置以及属性(例如颜色、背景、边框尺寸等)。
每个盒子由四个部分组成:内容、内边距、边框、外边距。我们接下来分别说明。
内容区域
通过 box-sizing 属性设置盒子模型的内容区域类型。可选的有 content-box 和 border-box。
content-box 是默认值,表示专注于内容区域。如果你设置一个元素的宽为 100px,那么这个元素的内容区会有 100px 宽:
css
/** 父容器内容宽 100px */
box-sizing: content-box;
width: 100px;
border: solid #5B6DCD 10px;
padding: 5px;
这个设置的内容区域不包含边框和内边距,如果设置了边框和内边距,那么盒子就会被撑开,因为内容已经100%了。
border-box 表示带着边框和padding的内容识别区域。你将一个元素的 width 设为 100px,那么这 100px 会包含它的 border 和 padding,内容区的实际宽度是 width 减去 (border + padding) 的值:
css
/** 父容器内容宽 100px */
box-sizing: border-box;
width: 100px;
border: solid #5B6DCD 10px;
padding: 5px;
内边距区域
内边距的粗细可以由 padding-top
、padding-right
、padding-bottom
、padding-left
,和简写属性 padding
控制。
边框区域
边框的粗细由 border-width
和简写的 border
属性控制。
假如框盒上设有背景(background-color
或 background-image
),背景将会一直延伸至边框的外沿(默认为在边框下层延伸,边框会盖在背景上)。此默认表现可通过 CSS 属性 background-clip
来改变。
外边距区域
外边距区域的大小由 margin-top
、margin-right
、margin-bottom
、margin-left
,和简写属性 margin
控制。在发生外边距合并的情况下,由于盒之间共享外边距,外边距的计算会有不同。
外边距合并与BFC(块盒格式化上下文)
关于 BFC 的概念,官方说明比较复杂,可以做简要查阅。
BFC一开始是由W3C制定的用于帮助页面块盒布局的一个标准,但是现在更多的是带给开发者不小的困扰,它可以理解为为了规避原先的设计缺陷而制定的一个规则。
这里总结一下块盒(没有开启BFC)的基本特性:
- 水平方向上,撑满整个包含块的宽度;垂直方向上,依次无缝堆放
- 在垂直方向上相邻的元素,上下有紧挨着的margin,会产生合并(两者取最大的,开启BFC可以解决)
- 父子包含块中,可能会出现margin塌陷;父元素无视浮动元素时会出现高度塌陷(开启BFC可以解决)
- 兄弟块之间,正常元素可能会被浮动元素覆盖(开启BFC可以解决)
BFC 说白了就是块盒以及块盒所影响的范围之内一系列独立渲染的上下文空间的总称,这个是根据浏览器的特性决定的。BFC 的特征如下:
- 是一块独立渲染的区域
- 隔绝了内外联系,不会影响到外部
- BFC之间相互不干扰
那么该如何开启这个特性空间呢?
父子元素开启 BFC
- HTML根元素:天然的独立渲染的区域,他是默认开启BFC的,但对于实际开发作用不大,因为大多数元素不可能都直接堆在根元素下渲染。
- 父容器设置 float 属性:你也浮动,我也浮动,那我就不会被你覆盖了[doge]
看这个元素:
html
<style>
.outer {
width: 500px;
background-color: bisque;
}
.inner {
width: 100px;
height: 100px;
margin: 20px;
}
.box1 {
background-color: aquamarine;
}
.box2 {
background-color: orange;
}
.box3 {
background-color: yellowgreen;
}
</style>
<div class="outer">
<div class="inner box1"></div>
<div class="inner box2"></div>
<div class="inner box3"></div>
</div>
给三个堆叠的div设置20px的margin,可以看到上下的margin合并了:
其不但兄弟之间合并了,父子容器之间也合并了。
我们现在给 outer 容器加上浮动属性:
css
.outer {
float: left;
}
可以看到父子之间没有合并了:
我们还原一下,同时给子元素设置浮动:
css
.inner {
float: left;
...
}
可以看到子元素正常排列了,但是父容器高度塌陷了:
再给父元素添加浮动后,查看:
- 父容器设置 position 为absolute或fixed (使用同上)
- 父容器设置 overflow 不为 visible (使用同上)
- 父容器设置 inline-block (使用同上)
- 父容器设置 display为flow-root (使用同上)
- 设置父元素为 flex 盒子
不但可以清除父容器的合并问题,还可以清除子元素的问题。
- 设置容器的 column-count: 1
css
.outer {
column-count: 1;
}
看看效果:
解决了父子容器的边距合并。
- 使用表格元素(table及其内部元素 th/tr 等)
其实不一定非得是table标签,也可以让块级元素表现得是table:
css
.outer {
display: table;
}
可以达到同样的效果。
- 父容器设置 column-span:all
兄弟元素开启BFC
- 给子元素设置浮动,这样脱离文档流,就不存在垂直外边距问题了,此时兄弟块之间不会产生覆盖了:
- 使用 padding 代替 margin
- 在各个inner内部在使用一层div来包裹内容物,并将inner元素通过上面的方法开启BFC (推荐)
- 如果有的子元素设置了浮动,未设置的兄弟元素会被盖住,那就要设置浮动清除了。
假设有第四个盒子,没有设置浮动:
html
<div class="outer">
<div class="inner box1"></div>
<div class="inner box2"></div>
<div class="inner box3"></div>
<div class="clearfix"></div>
</div>
如果你不设置清除,就会是这样的:
我们可以告诉第四个元素自己来清除浮动影响:
css
.clearfix {
clear: both; // 其实这里只要 claer: left即可
}
clear 的意思是从这个元素开始不要给我浮动了,它解决的是布局中浮动元素的堆叠问题,而不是触发父容器重新计算高度,所以父元素并没有形成BFC。
在看看:
此时,父元素的高度坍塌也顺便解决了。如果你所有的子元素都想设置为浮动,那么就要在最后加一个空的子元素块来设置清除浮动了。
只带文本的块盒或者纯文本天然有clear作用,形成环绕文字效果:<div>text</div>
更优雅的写法是在子元素末尾加一个伪元素after来达到相同的效果:
css
.outer::after {
content: '';
display: block; // 记得设置为块盒,不然不生效
clear: both;
}
完!