小孩才做选择,成年人当然是尺寸、座标全都要!
昨天介绍的 ResizeObserver API 可以观察到元素的尺寸变动时执行回呼,并且会提供元素变动後的「尺寸」、「座标」等资讯,而今天介绍的 GetBoundingClientRect 虽然无法再观察元素变动了,但我们可以主动出击,直接索取目前元素的相关资讯。
GetBoundingClientRect 可以使我们取得 Element 元素的宽高以及相对於视窗可视范围(Viewport)的座标位置,对於前端老鸟来说可能不是那麽陌生,但对於新手来说,它不太会是第一批认识的 Web API,大部分可能都会先认识 clientWidth
、scrollWidth
、offsetWidth
、scrollTop
、clientTop
、offsetTop
...等等,这一大堆眼花撩乱让人容易混淆的系列属性。
为了让各位更好的理解 GetBoundingClientRect,先来帮各位整理及复习前面提到的这一大堆元素属性,
borderBox
的宽/高paddingBox
的宽/高padding
及外溢内容的宽/高borderBox
相对於 offsetParent
的垂直/水平距离paddingBox
相对於 borderBox
的垂直/水平距离paddingBox
被卷动的垂直/水平距离
其实刚刚介绍的众多属性,相信大家或多或少都有使用过,尤其在一些卷动事件中常常会出现它们的身影,不过在今天之後,你使用它们的机会可能会渐渐减少了。
getBoundingClientRect
的使用方式非常简单,它属於 Element 的原生 method,直接互叫即可:
const div = document.querySelector("div");
console.log(div.getBoundingClientRect());
执行过後便会回传一个 DOMRect 物件,该物件中就会有指定元素的相关尺寸与座标讯息:
borderBox
宽度,相当於 offsetWidth
borderBox
高度,相当於 offsetHeight
borderBox
左上角相对於视窗的水平(X)座标borderBox
左上角相对於视窗的垂直(Y)座标borderBox
左上角相对於视窗的水平(X)座标,等同 x
borderBox
左上角相对於视窗的垂直(Y)座标,等同 y
borderBox
右下角相对於视窗的水平(X)座标borderBox
右下角相对於视窗的垂直(Y)座标DOMRect 跟昨天介绍的
ResizeObserverEntry.contentRect
所回传的 DOMRectReadOnly 格式是一样的,但当中数值所代表的意义完全不同,不要被混淆罗。
有了这些资讯後,针对一些卷动事件的需求其实就会变得简单许多,像是我们常常会做的事情是「判断某元素是否进入可视范围」,就可以来看看使用 getBoundingClientRect
後的差别:
const div = document.querySelector("div");
// 不使用 getBoundingClientRect
window.addEventListener("scroll", function (e) {
if (div.offsetTop + div.offsetHeight <= window.pageYOffset) {
console.log("元素底端已离开画面");
} else if (div.offsetTop <= window.pageYOffset + window.innerHeight) {
console.log("元素顶端已进入画面");
}
});
// 使用 getBoundingClientRect
window.addEventListener("scroll", function (e) {
const { top, bottom } = div.getBoundingClientRect();
if (bottom <= 0) {
console.log("元素底端已离开画面");
} else if (top <= window.innerHeight) {
console.log("元素顶端已进入画面");
}
});
可以看到,如果不使用 getBoundingClientRect
需进行较复杂的计算,而且如果该元素的 offsetParent
不是 body
的话,这个计算就会出现 Bug。反之使用 getBoundingClientRect
的程序码简洁又容易理解,而且因为 top
和 bottom
的数值是直接相对视窗计算出来的,所以也不用去顾虑元素的 offsetParent
。
小技巧: 利用
window.pageYOffset
+getBoundingClientRect().top
就可以计算出元素相对於文件(document)的绝对座标喔。
为了让大家更能感受到 getBoundingClientRect
的强大,我们来做一个 「动态 Highlight」 的小练习,需求是「当游标滑到文章中的粗体字时会自动添加底色,且底色在不同关键字之间切换时,要有移动的过渡效果」。先看效果:
.highlight {
position: fixed;
background: yellowgreen;
}
const highlight = document.querySelector(".highlight");
const bold = document.querySelectorAll("b");
let hoverElement;
bold.forEach((el) => {
el.addEventListener("mouseenter", function () {
hoverElement = this;
highlight.style.transition = "0.3s";
setHighlight();
});
});
// 为了在视窗滚动时不会跑版,要在 scroll 进行重新定位
window.addEventListener("scroll", function () {
highlight.style.transition = "0s";
if (hoverElement) setHighlight();
});
function setHighlight() {
const { width, height, top, left } = hoverElement.getBoundingClientRect();
highlight.textContent = hoverElement.textContent;
highlight.style.width = width + "px";
highlight.style.height = height + "px";
highlight.style.top = top + "px";
highlight.style.left = left + "px";
}
整体概念就是在指定的元素上绑定 mouseenter
事件,并在事件发生时使用 getBoundingClientRect
来取得该元素的尺寸座标资讯,然後将其设定在 Highlight 元素的样式上。
不要认为这样的功能效果好像很简单,如果没有 getBoundingClientRect
的话,做起来是特别麻烦的,想要实际玩玩看的话,这边提供我已经写好的 CodePen,并且也鼓励大家发挥创意来试试看其他的应用,感受一下它的好用之处。
希望经过今天的介绍,各位已经开始爱上 getBoundingClientRect
了,我本身就蛮常使用的,比较记下一堆容易搞混的属性,只需要一行我就可以取得那些经常使用的资讯,而且还可以剩下很多麻烦的计算,何乐不为呢?
<<: [Day21]Geolocation based Speedometer and Compass
>>: 如果你真的够渴望做点什麽,任何事情你都可以持续做三十天。
今天我们要来解决空缺的部份,我们要使用的素材如下,是一张每隔五分钟就纪录温度的资料表,我结图整张表最...
一、前言 这篇要介绍的是当你的网站要申请无障碍标章时,必须要做的「无障碍检测」。一个好的网站,是...
前面的篇章大部分着重 DDD 的战术设计,这篇来说说战略设计。 功能型团队 在导入 DDD 前,我们...
前一篇文章简单介绍了Azure DevOps Artifacts,知道了它就是用来存放私有套件的套件...
本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 Udemy 和 Youtu...