JavaScript 位置属性
JavaScript 位置与大小属性
学习文章:https://blog.youkuaiyun.com/qq_44603011/article/details/117768212
https://blog.youkuaiyun.com/weixin_61597234/article/details/134878221
https://juejin.cn/post/6961383795866435591
https://juejin.cn/post/7146441070828584968
元素自身Element有关的位置属性
element.offsetParent 获取包含element的祖先元素中,层级最近的定位元素
获取到的元素需要包含三个条件
1.是Element
的祖先元素
2.最靠近Element
3.是定位元素,即position
属性不为static
案例
box:有3个祖先元素:
1.层级为3的定位元素 position-outer
2.层级为2个定位元素 position-inner
3.层级为1定位元素 not-position
=> positon-inner才同时满足上面关于offsetParent的3个条件
<div class="position-outer" style="position: relative;">
<div class="position-inner" style="postion: relative;">
<div class="not-position">
<div class="box">
</div>
</div>
</div>
</div>
offsetLeft / offsetTop 只读属性 获取元素距离带定位父元素的位置
offsetLeft :element相对于offsetParent左边界的偏移值
offsetTop :element相对于offsetParent上边界的偏移值
滚动条相关属性
element.scrollTop属性
element.scrollTop
: “元素中的内容” 超出 “元素上边界” 部分的高度,也可以理解为滚动条滚动了多少距离(滚动距离)
如果失效需要排查是否设置给能滚动的元素,比如设置了
overflow: auto
的元素
Element.scrollTo()方法
语法1:Element.scrollTo(x-coord, y-coord)
x-coord:滚动 据Element左上角,X方向x-coord像素
y-coord:滚动 据Element左上角,Y方向y-coord像素
语法2:Element.scrollTo(options)
options对象
属性 | 描述 |
---|---|
left属性 | 等同于x-coord |
top属性 | 等同于y-coord |
behavior动画效果 | smooth 表示平滑滚动并产生过渡效果 instant 表示滚动会直接跳转到目标位置,没有过渡效果 auto 或缺省值表示浏览器会自动选择滚动时的过渡效果 |
element.getBoundingClientRect()返回DOMRect 对象
位置是相对于浏览器窗口当前可以看见的区域(不包含浏览器中看不见/滚动条没滚到的区域和工具栏)
top:图中红色线表示top的取值区域,最外层边框到窗口顶部的距离。 — 以下案例是90
left:图中蓝色线表示left的取值范围,可知是由最外层边框到窗口左侧的距离。 – 以下案例是120
bottom:图中紫色线表示bottom的取值范围,可知是元素由最外层边框到窗口顶部的距离。 --以下案例是90+10+10+300+10+10 =
430 = height(340)+y(90)
right:图中绿色线表示right的取值范围,可知是元素由最外层边框到窗口顶部的距离。 – 以下案例是120+10+10+300+10+10 = 460 = x+width
元素自身Element有关的大小属性
属性 | 描述 | 特点 |
---|---|---|
Element.clientWidth | 内边距(padding)+ 内容 | 属性值会四舍五入为一个整数 |
HTMLElement.offsetWidth | 边框(border)+padding+内容+ 滚动条宽度(如有) | 属性值会四舍五入为一个整数,不带单位 |
Element.scrollWidth | 包括由于溢出而无法在屏幕上显示的部分(包含伪元素) + clientWidth | 属性值会四舍五入为一个整数 |
Element.getBoundingClientRect().width | padding + border-width + 内容 | 标准盒子= width/height + padding + border-width box-sizing: border-box 盒子 = width/height |
scrollWidth
不包含边框border
clientWidth为什么为0
-
元素不显示时
display: none
获取为0
猜测原因渲染树中只包含可见的节点,忽略所有不可见(这里应该指display: none)的节点。由于visibility: hidden的元素只是看不见,但还是会占据位置所以需要计算节点的大小位置信息所以在渲染树中,所以可以获取到offsetWidth -
行内元素获取时为0,行内元素
width
属性不生效
常见的行内元素:span、a、 img、 input、textarea、select、label
常见的块级元素:div、p、table、form
offset系列和style的区别
offset | style |
---|---|
可以得到任意样式表中的样式值 | 只能得到行内样式表中的样式值 |
获得的数值是没有单位的 | style.width得到的值是带有单位的字符串offsetWidth包含padding+border+width |
offsetWidth是只读属性 | style.width可读写 |
想要获取元素大小位置,用offset更合适 | 想要给元素更改值,则需要用style |
案例
1.判断元素是否滚动到底
scrollTop + scrollHeight + clientHeight
scrollTop
是一个非整数,而 scrollHeight
和 clientHeight
是四舍五入值,因此确定滚动区域是否滚动到底的唯一方法是查看滚动量是否足够接近某个阈值
scrollHeight = 由于溢出而无法在屏幕上显示的部分(也就是滚动距离) + clientHeight
所以判断是否到底的公式是,这里的阈值取1
Math.abs(element.scrollHeight - element.clientHeight - element.scrollTop) < 1
存在问题:不同分辨率有误差
2.内容超出后显示滚动条,自动滚动到最底部
判断是否存在滚动条的逻辑:出现滚动条意味着元素空间
将大于其内容显示区域
el.scrollHeight > el.clientHeight
设置滚动到最底部
直接设置的dom元素的js属性,所以如果设置过渡动画transition
是没有效果的
container.scrollTop = container.scrollHeight;
事件源event位置相关属性
属性 | 相对区域 | 说明 |
---|---|---|
offsetX offsetY | 相对于事件源元素 | 假设点击黄色盒子打印offsetX/offsetY,此时的原点为黄色盒子的左上角,X轴正向朝右,Y轴正向朝下 |
clientX clientY | 相对于浏览器窗口当前可以看见的区域(不包含浏览器中看不见/滚动条没滚到的区域和工具栏) | 原点为灰色区域的左上角,X轴正向朝右,Y轴正向朝下 |
pageX pageY | 相对于整个页面(document、html区域?) | pageX = ev.clientX + 横向滚动条滚动的距离 |
screenX screenY | 鼠标相对于用户显示器屏幕 | 原点在显示器屏幕的左上角,X轴正向朝右,Y轴正向朝下 |
Intersection Observer API 判断元素相交性
Intersection Observer API
提供了一种异步检测目标元素与祖先元素或视口(可统称为根元素)相交情况变化的方法。
说明:API是异步的,触发时机是当浏览器空闲时执行观察器。不会随着目标元素的滚动同步触发,原因是IntersectionObserver API是通过requestIdleCallback()实现。
使用场景:某个元素是否可见或相交
概念
目标元素target
:需要监听的元素
根元素root
:
交叉比intersection ratio
:目标元素与根元素的交集相对于目标元素百分比,取值范围 0.0-1.0,没办法具体到xpx的像素。
阈值threshold
:回调函数触发的条件。
IntersectionObserver()构造器
语法:new IntersectionObserver(callback[, options])
参数
callback(entries,observer)
回调函数:当交叉比超过阈值会触发entries
: 由IntersectionObserverEntry
对象组成的数组observer
:被调用回调的IntersectionObserver
实例
options
可选配置对象root
根元素:必须是目标元素的父级或祖先,默认为浏览器的可视窗口(指定null也是)rootMargin
根元素的扩缩边距:值格式同margin
,用于控制根元素的哪个位置开始算与目标元素相交, 默认值为"0px 0px 0px 0px"。threshold
阈值:回调函数触发的条件,取值范围为0.0-1.0,默认为0.0。当传入数值类型时,只会触发一次。当传入数值类型时,只会触发一次。
注意点:当IntersectionObserver
对象被创建时,就会被指定所监听的根元素、阀值等信息,并且被创建后无法更改。
IntersectionObserverEntry对象
IntersectionObserverEntry
:接口描述了目标元素与其根元素容器在某一特定过渡时刻的交叉状态。
IntersectionObserverEntry 对象的七个只读属性
属性 | 说明 |
---|---|
target | 返回目标元素,表示目前正监听的元素 |
isIntersecting | 目标元素刚出现在根元素可视区时返回 true 目标元素从根元素可视区消失返回 false 出现和消失都会触发 callback 函数 |
boundingClientRect | 返回目标元素的矩形区域的信息,返回结果与element.getBoundingClientRect()相同 |
rootBounds | 返回根元素的矩形区域的信息,getBoundingClientRect()方法的返回值,如果没有根元素(即直接相对于视口滚动),则返回 null |
intersectionRect | 返回目标元素与视口(或根元素)交叉区域的信息 |
intersectionRatio | 返回目标元素的可见比例,即intersectionRect占boundingClientRect的比例,完全可见时为 1,完全不可见时小于等于 0 |
time | 返回一个记录从IntersectionObserver的时间原点到交叉被触发的时间的时间戳 |
IntersectionObserver实例对象
三个可读属性:与构造器方法第二个可选对象的属性名一致。
四个方法
方法名 | 描述 |
---|---|
observe(target) | 开始监听指定目标元素 |
unobserve(target) | 停止监听指定的目标元素 |
takeRecords() | 返回所有观察目标的 IntersectionObserverEntry 对象数组 – 唯一获取 IntersectionObserverEntry 对象的方法 |
disconnect() | 使 IntersectionObserver 对象停止全部监听工作 |
案例
1.判断元素是否滚动到底 = 元素是否全部可见
之前的案例存在问题:不同分辨率有误差
解决办法:使用Intersection Observer API
实现
如果元素(在这种情况下是整个body)完全出现在视窗内(即未被遮挡),isIntersecting 属性为 true;如果元素完全不在视窗内(即被遮挡),isIntersecting 属性为 false。
let isScrollBottom = false;
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) { // 正相交
isScrollBottom = false;
} else { // 没有相交,全部展现
isScrollBottom = true;
}
});
}, {threshold: 0});
observer.observe(document.querySelector('body'));
if (isScrollBottom) {
console.log('滚到底部');
} else {
console.log('未滚到底部');
}