![[Pasted image 20240428170934.png]]
![[Pasted image 20240428170954.png]]
display: none、不在文档中),几何信息基本是 0number 类型,单位像素(offsetParent 不是几何信息)
*Width/Height 会对值取整(四舍五入),需要小数使用 [[#获取元素坐标|elem.getBoundingClientRect()]]*Left/Top 会保留小数scrollLeft 和 scrollTop::before、::after 等伪元素属于 contentborder 和 padding 之间offsetParent:最接近的祖先(ancestor),在浏览器渲染期间,它被用于计算坐标
Element 或 nulltable、td、th、body 元素position 不为 static)offsetParent 值为 null 的情况:
<body> 与 <html>display: none 或不在文档中)position: fixed 的元素offsetLeft、offsetTop:相对于 offsetParent 左上角的 X/Y 坐标offsetWidth、offsetHeight:元素的“外部”宽高
border、padding、content、滚动条![[Pasted image 20240428180141.png|500]]
![[Pasted image 20240428180230.png|500]]
clientLeft、clientTop:元素“内侧”与“外侧”的相对坐标
border、滚动条clientWidth、clientHeight:元素的“内部”宽高
padding、content<html> 和怪异模式下的 <body>,返回视口宽高(不包含滚动条)![[Pasted image 20240428175620.png|300]]
![[Pasted image 20240428180317.png|500]]
scrollLeft、scrollTop:就像 clientLeft、clientTop,但它们还包括滚动出(隐藏)的部分
border、滚动条scrollWidth、scrollHeight:就像 clientWidth、clientHeight,但它们还包括滚动出(隐藏)的部分
padding、content![[Pasted image 20240428185034.png|500]]
![[Pasted image 20240428185000.png|500]]
使用 document.documentElement.clientWidth/clientHeight
![[Pasted image 20240428192331.png|500]]
而不是 window.innerWidth/innerHeight,因为其包含滚动条
在大多数情况下,我们需要可用的窗口宽度以绘制或放置某些东西
从理论上讲,可以使用 document.documentElement.scrollWidth/scrollHeight 来测量文档的完整大小,但由于历史原因,这无法正常工作。
为了可靠地获得完整的文档宽高,应该采用以下这些属性的最大值:
const scrollHeight = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
)
const scrollWidth = Math.max(
document.body.scrollWidth, document.documentElement.scrollWidth,
document.body.offsetWidth, document.documentElement.offsetWidth,
document.body.clientWidth, document.documentElement.clientWidth
)
document.documentElement.scrollLeft/scrollTop 在大多数浏览器中都是正确的document.body 而不是 document.documentElementwindow.pageXOffset/pageYOffset
window.pageXOffset 是 window.scrollX 的别名;window.pageYOffset 是 window.scrollY 的别名。window:
window.scrollTo():滚动到文档中的某个坐标(绝对位置)
scrollTo(x: number, y: number)
x:X 轴坐标y:Y 轴坐标scrollTo({ left: number; top: number; behavior?: string })
left:同 x-coord,X 轴坐标top:同 y-coord,Y 轴坐标behavior:滚动行为,取值如下: ^1
"auto":默认值
window 由浏览器决定elem 由 CSS scroll-behavior 的计算值决定"smooth":平滑滚动"instant":瞬间滚动window.scroll():同 window.scrollTo()window.scrollBy():按指定的偏移量滚动文档(相对于当前位置)
window.scrollTo()window.scrollByLines(lines: number):按给定的行数滚动文档
lines:要滚动的行数,可以是正整数(向下),也可以是负整数(向上)window.scrollByPages(pages):在当前文档页面按照指定的页数翻页
pages:要滚动的页数,可以是正整数(向下),也可以是负整数(向上)// 该方法没有任何意义,且有 BUG:在上一次方法动画没执行完前再次调用,页面就会乱动
// 请使用 window.scrollTo({ left: number; top: number; behavior: "smooth" })
/**
* @description: 平滑滚动文档到指定位置
* @param {number} x 滚动到的 X 轴坐标
* @param {number} y 滚动到的 Y 轴坐标
*/
function smoothScrollTo(x, y) {
// 当前位置
let scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft
let scrollTop = document.documentElement.scrollTop || document.body.scrollTop
function _step() {
// 距离目标距离
const distanceX = x - scrollLeft
const distanceY = y - scrollTop
// 目标位置
scrollLeft = scrollLeft + distanceX / 5
scrollTop = scrollTop + distanceY / 5
if (Math.max(Math.abs(distanceX), Math.abs(distanceY)) < 1) {
window.scrollTo(x, y)
} else {
window.scrollTo(scrollLeft, scrollTop)
requestAnimationFrame(_step)
}
}
_step()
}
elem:
elem.scrollTo():同 window.scrollTo()elem.scrollBy():同 window.scrollBy()elem.scrollIntoView():会滚动元素的父容器,使 elem 元素出现到可视区域
scrollIntoView(alignToTop?: boolean)
true:默认值,元素的顶端将和其所在滚动区的可视区域的顶端对齐false:元素的底端将和其所在滚动区的可视区域的底端对齐scrollIntoView({ inline?: string; block?: string; behavior?: string })
inline:定义水平方向的对齐,默认值 "nearest",取值如下:
"start""center""end""nearest"block:定义垂直方向的对齐,默认值 "start",取值同 inlinebehavior:滚动行为,取值 [[#^1|同上]]document.body.style.overflow = "hidden"document.body.style.overflow = ""可以使用相同的技术来冻结其他元素的滚动,而不仅仅是 document.body
这个方法的缺点是会使滚动条消失。如果滚动条占用了一些空间,它原本占用的空间就会空出来,那么内容就会“跳”进去以填充它。
解决方法是对比冻结前后的 clientWidth。如果它增加了(滚动条消失后),就在 document.body 滚动条原来的位置处添加 padding。保持了滚动条冻结前后文档内容宽度相同
大多数 JS 方法处理的是以下两种坐标系中的一个:
position: fixed,从窗口的顶部/左侧边缘计算得出
clientX/clientYposition: absolute 类似,从文档的顶部/左侧边缘计算得出
pageX/pageY![[Pasted image 20240429105404.png]]
当文档滚动了:
pageY:元素在文档中的相对坐标保持不变,从文档顶部(现在已滚动出去)开始计算clientY:窗口相对坐标发生了变化,因为同一个点越来越靠近窗口顶部elem.getBoundingClientRect() 方法返回一个 DOMRect 对象,提供了元素的最小矩形大小及相对于==窗口(视口)==的位置。
主要的 DOMRect 属性:
x/y —— 矩形原点相对于窗口的 X/Y 坐标width/height —— 矩形的 width/height,包含 border、padding、content、滚动条此外,还有派生(derived)属性:
top/bottom —— 顶部/底部矩形边缘的 Y 坐标left/right —— 左/右矩形边缘的 X 坐标![[Pasted image 20240429110100.png|500]]
x/y 和 width/height 对矩形进行了完整的描述。可以很容易地从它们计算出派生(derived)属性:
left = xtop = yright = x + widthbottom = y + height为什么需要派生(derived)属性:
width/height 可以为负数,从而允许“定向(directed)”矩形
width/height 值表示矩形从其右下角开始,然后向左上方“增长”elem.getBoundingClientRect() 总是返回正数的 width/height注意:
elem 位于窗口的上方,则 top 为负数right/bottom 与 CSS position 属性不同
getBoundingClientRect:所有坐标都从窗口左上角开始计数document.elementFromPoint(x, y) 方法返回在窗口坐标 (x, y) 处嵌套最多/深(the most nested)的元素(z-index 最大的元素)
注意:
nullpageY = clientY + 文档的垂直滚动出的部分的高度pageX = clientX + 文档的水平滚动出的部分的宽度// 获取元素的文档坐标
function getCoords(elem) {
const box = elem.getBoundingClientRect()
return {
top: box.top + window.pageYOffset,
right: box.right + window.pageXOffset,
bottom: box.bottom + window.pageYOffset,
left: box.left + window.pageXOffset
}
}
[[001.浏览器渲染流程#什么是 Reflow?|Reflow]]
不要使用像 [[006.class 和 style#window.getComputedStyle()|getComputedStyle]] 这样的 API 获取元素宽高
原因:
width / height 取决于另一个属性:box-sizingmin/max-width/height 影响width / height 可能是 auto,例如内联(inline)元素width / height 取值,不同浏览器的差异较大scrollTop 是一个非整数,而 scrollHeight 和 clientHeight 是四舍五入的,因此确定滚动区域是否滚动到底的唯一方法是查看滚动量是否足够接近某个阈值(在本例中为 1):
Math.abs(element.scrollHeight - element.clientHeight - element.scrollTop) < 1
window.getComputedStyle(element).overflowY === "visible"
window.getComputedStyle(element).overflowY !== "hidden"
// 在 elem 下显示 html 内容
function createMessageUnder(elem, html) {
let message = document.createElement('div')
// 在这里最好使用 CSS class 来定义样式
message.style.cssText = "position: fixed; color: red"
// 分配坐标
let coords = elem.getBoundingClientRect()
message.style.left = coords.left + "px"
message.style.top = coords.bottom + "px"
message.innerHTML = html
return message
}
// 使用:
let message = createMessageUnder(elem, 'Hello, world!')
document.body.append(message)
setTimeout(() => message.remove(), 5000)
上面方法使用的是相对于窗口的坐标(getBoundingClientRect),页面滚动会导致“分离”
要改变这一点,需要使用基于文档(document)的坐标和 position: absolute 样式:
// 在 elem 下显示 html 内容
function createMessageUnder(elem, html) {
let message = document.createElement('div')
message.style.cssText = "position: absolute; color: red" // 相对定位
let coords = getCoords(elem) // 使用相对于文档的坐标
message.style.left = coords.left + "px"
message.style.top = coords.bottom + "px"
message.innerHTML = html
return message
}
其中 getCoords 方法 [[#获取文档坐标|见上]]