Javascript.info-浏览器2
JavaScript 中有许多属性可让我们读取有关元素宽度、高度和其他几何特征的信息。
JavaScript 中有许多属性可让我们读取有关元素宽度、高度和其他几何特征的信息。
我们在 JavaScript 中移动或定位元素时,我们会经常需要它们。
示例元素
作为演示属性的示例元素,我们将使用下面给出的元素:
<div id="example">
...Text...
</div>
<style>
#example {
width: 300px;
height: 200px;
border: 25px solid #E8C48F;
padding: 20px;
overflow: auto;
}
</style>它有边框(border),内边距(padding)和滚动(scrolling)等全套功能。但没有外边距(margin),因为它们不是元素本身的一部分,并且它们没什么特殊的属性。
这个元素看起来就像这样:

备注
注意滚动条
上图演示了元素具有滚动条这种最复杂的情况。一些浏览器(并非全部)通过从内容(上面标记为 “content width”)中获取空间来为滚动条保留空间。
因此,如果没有滚动条,内容宽度将是 300 px,但是如果滚动条宽度是 16px(不同的设备和浏览器,滚动条的宽度可能有所不同),那么还剩下 300 - 16 = 284px,我们应该考虑到这一点。这就是为什么本章的例子总是假设有滚动条。如果没有滚动条,一些计算会更简单。
这就是为啥有的时候出现滚动条会是内容位置出现变化
备注
文本可能会溢出到 padding-bottom 中
在我们的插图中的 padding 中通常显示为空,但是如果元素中有很多文本,并且溢出了,那么浏览器会在 padding-bottom 处显示“溢出”文本,这是正常现象。
几何
这是带有几何属性的整体图片:

这些属性的值在技术上讲是数字,但这些数字其实是“像素(pixel)”,因此它们是像素测量值。
让我们从元素外部开始探索属性。
offsetParent,offsetLeft/Top
这些属性很少使用,但它们仍然是“最外面”的几何属性,所以我们将从它们开始。
offsetParent 是最接近的祖先(ancestor),在浏览器渲染期间,它被用于计算坐标。
最近的祖先为下列之一:
- CSS 定位的(
position为absolute、relative、fixed或sticky), - 或
<td>,<th>,<table>, - 或
<body>。
属性 offsetLeft/offsetTop 提供相对于 offsetParent 左上角的 x/y 坐标。(是不是设置left/top对应的parent作为offsetParent来计算的)
在下面这个例子中,内部的 <div> 有 <main> 作为 offsetParent,并且 offsetLeft/offsetTop 让它从左上角位移(180):
<main style="position: relative" id="main">
<article>
<div id="example" style="position: absolute; left: 180px; top: 180px">...</div>
</article>
</main>
<script>
alert(example.offsetParent.id); // main
alert(example.offsetLeft); // 180(注意:这是一个数字,不是字符串 "180px")
alert(example.offsetTop); // 180
</script>
有以下几种情况下,offsetParent 的值为 null:
- 对于未显示的元素(
display:none或者不在文档中)。 - 对于
<body>与<html>。 - 对于带有
position:fixed的元素。
offsetWidth/offsetHeight
现在,让我们继续关注元素本身。
这两个属性是最简单的。它们提供了元素的“外部” width/height。或者,换句话说,它的完整大小(包括边框)。

对于我们的示例元素:
offsetWidth = 390—— 外部宽度(width),可以计算为内部 CSS-width(300px)加上 padding(2 * 20px)和 border(2 * 25px)。offsetHeight = 290—— 外部高度(height)。
备注
对于未显示的元素,几何属性为 0/null
仅针对显示的元素计算几何属性。
如果一个元素(或其任何祖先)具有 display:none 或不在文档中,则所有几何属性均为零(或 offsetParent 为 null)。
例如,当我们创建了一个元素,但尚未将其插入文档中,或者它(或它的祖先)具有 display:none 时,offsetParent 为 null,并且 offsetWidth 和 offsetHeight 为 0。
clientTop/clientLeft
在元素内部,我们有边框(border)。
为了测量它们,可以使用 clientTop 和 clientLeft。
在我们的例子中:
clientLeft = 25—— 左边框宽度clientTop = 25—— 上边框宽度

……但准确地说 —— 这些属性不是边框的 width/height,而是内侧与外侧的相对坐标。
有什么区别?
当文档从右到左显示(操作系统为阿拉伯语或希伯来语)时,影响就显现出来了。此时滚动条不在右边,而是在左边,此时 clientLeft 则包含了滚动条的宽度。
在这种情况下,clientLeft 的值将不是 25,而是加上滚动条的宽度 25 + 16 = 41。
这是希伯来语的例子:

clientWidth/Height
这些属性提供了元素边框内区域的大小。
它们包括了 “content width” 和 “padding”,但不包括滚动条宽度(scrollbar):

在上图中,我们首先考虑 clientHeight。
这里没有水平滚动条,所以它恰好是 border 内的总和:CSS-height 200px 加上顶部和底部的 padding(2 * 20px),总计 240px。
现在 clientWidth —— 这里的 “content width” 不是 300px,而是 284px,因为被滚动条占用了 16px。所以加起来就是 284px 加上左侧和右侧的 padding,总计 324px。
如果这里没有 padding,那么 clientWidth/Height 代表的就是内容区域,即 border 和 scrollbar(如果有)内的区域。

因此,当没有 padding 时,我们可以使用 clientWidth/clientHeight 来获取内容区域的大小。
scrollWidth/scrollHeight
这些属性就像 clientWidth/clientHeight,但它们还包括滚动出(隐藏)的部分:

在上图中:
scrollHeight = 723—— 是内容区域的完整内部高度,包括滚动出的部分。scrollWidth = 324—— 是完整的内部宽度,这里我们没有水平滚动,因此它等于clientWidth。
我们可以使用这些属性将元素展开(expand)到整个 width/height。
像这样:
// 将元素展开(expand)到完整的内容高度
element.style.height = `${element.scrollHeight}px`;
scrollLeft/scrollTop
属性 scrollLeft/scrollTop 是元素的隐藏、滚动部分的 width/height。
在下图中,我们可以看到带有垂直滚动块的 scrollHeight 和 scrollTop。

换句话说,scrollTop 就是“已经滚动了多少”。
备注
scrollLeft/scrollTop 是可修改的
大多数几何属性是只读的,但是 scrollLeft/scrollTop 是可修改的,并且浏览器会滚动该元素。
如果你点击下面的元素,则会执行代码 elem.scrollTop += 10。这使得元素内容向下滚动 10px。
将 scrollTop 设置为 0 或一个大的值,例如 1e9,将会使元素滚动到顶部/底部。
不要从 CSS 中获取 width/height
我们刚刚介绍了 DOM 元素的几何属性,它们可用于获得宽度、高度和计算距离。
但是,正如我们在 样式和类 一章所知道的那样,我们可以使用 getComputedStyle 来读取 CSS-width 和 height。
那为什么不像这样用 getComputedStyle 读取元素的 width 呢?
let elem = document.body;
alert( getComputedStyle(elem).width ); // 显示 elem 的 CSS width为什么我们应该使用几何属性呢?这里有两个原因:
-
首先,CSS
width/height取决于另一个属性:box-sizing,它定义了“什么是” CSS 宽度和高度。出于 CSS 的目的而对box-sizing进行的更改可能会破坏此类 JavaScript 操作。 -
其次,CSS 的
width/height可能是auto,例如内联(inline)元素:<span id="elem">Hello!</span> <script> alert( getComputedStyle(elem).width ); // auto </script>从 CSS 的观点来看,
width:auto是完全正常的,但在 JavaScript 中,我们需要一个确切的px大小,以便我们在计算中使用它。因此,这里的 CSS 宽度没什么用。
还有另一个原因:滚动条。有时,在没有滚动条的情况下代码工作正常,当出现滚动条时,代码就出现了 bug,因为在某些浏览器中,滚动条会占用内容的空间。因此,可用于内容的实际宽度小于 CSS 宽度。而 clientWidth/clientHeight 则会考虑到这一点。
……但是,使用 getComputedStyle(elem).width 时,情况就不同了。某些浏览器(例如 Chrome)返回的是实际内部宽度减去滚动条宽度,而某些浏览器(例如 Firefox)返回的是 CSS 宽度(忽略了滚动条)。这种跨浏览器的差异是不使用 getComputedStyle 而依靠几何属性的原因。
<div style="width: 300px;height: 300px;overflow-y: scroll;background-color: pink;">
<button onclick="alert( getComputedStyle(elem).width )">获取计算宽度</button>
</div>
<script>
const elem = document.querySelector('div')
</script>
在桌面 Windows 操作系统上,Firefox、Chrome、Edge 都为滚动条保留了空间。但 Firefox 显示的是 300px,而 Chrome 和 Edge 显示较少。这是因为 Firefox 返回 CSS 宽度,其他浏览器返回“真实”宽度。
请注意,所描述的差异只是关于从 JavaScript 读取的 getComputedStyle(...).width,而视觉上看,一切都是正确的。
总结
元素具有以下几何属性:
offsetParent—— 是最接近的 CSS 定位的祖先,或者是td,th,table,body。offsetLeft/offsetTop—— 是相对于offsetParent的左上角边缘的坐标。offsetWidth/offsetHeight—— 元素的“外部” width/height,边框(border)尺寸计算在内。clientLeft/clientTop—— 从元素左上角外角到左上角内角的距离。对于从左到右显示内容的操作系统来说,它们始终是左侧/顶部 border 的宽度。而对于从右到左显示内容的操作系统来说,垂直滚动条在左边,所以clientLeft也包括滚动条的宽度。clientWidth/clientHeight—— 内容的 width/height,包括 padding,但不包括滚动条(scrollbar)。scrollWidth/scrollHeight—— 内容的 width/height,就像clientWidth/clientHeight一样,但还包括元素的滚动出的不可见的部分。scrollLeft/scrollTop—— 从元素的左上角开始,滚动出元素的上半部分的 width/height。
除了 scrollLeft/scrollTop 外,所有属性都是只读的。如果我们修改 scrollLeft/scrollTop,浏览器会滚动对应的元素。
Window 大小和滚动
我们如何找到浏览器窗口(window)的宽度和高度呢?我们如何获得文档(document)的包括滚动部分在内的完整宽度和高度呢?我们如何使用 JavaScript 滚动页面?
对于此类信息,我们可以使用与 <html> 标签相对应的根文档元素 document.documentElement。但是还有其他方法和特性需要考虑。
窗口的 width/height
为了获取窗口(window)的宽度和高度,我们可以使用 document.documentElement 的 clientWidth/clientHeight:

警告
不是 window.innerWidth/innerHeight
浏览器也支持像 window.innerWidth/innerHeight 这样的属性。它们看起来像我们想要的,那为什么不使用它们呢?
如果这里存在一个滚动条,并且滚动条占用了一些空间,那么 clientWidth/clientHeight 会提供没有滚动条(减去它)的 width/height。换句话说,它们返回的是可用于内容的文档的可见部分的 width/height。
window.innerWidth/innerHeight 包括了滚动条。
如果这里有一个滚动条,它占用了一些空间,那么这两行代码会显示不同的值:
alert( window.innerWidth ); // 整个窗口的宽度
alert( document.documentElement.clientWidth ); // 减去滚动条宽度后的窗口宽度在大多数情况下,我们需要 可用 的窗口宽度以绘制或放置某些东西。也就是说,在滚动条内(如果有)。所以,我们应该使用 documentElement.clientHeight/clientWidth。
警告
DOCTYPE 很重要
请注意:当 HTML 中没有 <!DOCTYPE HTML> 时,顶层级(top-level)几何属性的工作方式可能就会有所不同。可能会出现一些稀奇古怪的情况。
在现代 HTML 中,我们始终都应该写 DOCTYPE。
文档的 width/height
从理论上讲,由于根文档元素是 document.documentElement,并且它包围了所有内容,因此我们可以通过使用 documentElement.scrollWidth/scrollHeight 来测量文档的完整大小。
但是在该元素上,对于整个文档,这些属性均无法正常工作。在 Chrome/Safari/Opera 中,如果没有滚动条,documentElement.scrollHeight 甚至可能小于 documentElement.clientHeight!很奇怪,对吧?
为了可靠地获得完整的文档高度,我们应该采用以下这些属性的最大值:
let scrollHeight = Math.max(
document.body.scrollHeight, document.documentElement.scrollHeight,
document.body.offsetHeight, document.documentElement.offsetHeight,
document.body.clientHeight, document.documentElement.clientHeight
);
alert('Full document height, with scrolled out part: ' + scrollHeight);为什么这样?最好不要问。这些不一致来源于远古时代,而不是“聪明”的逻辑。
获得当前滚动
DOM 元素的当前滚动状态在其 scrollLeft/scrollTop 属性中。
对于文档滚动,在大多数浏览器中,我们可以使用 document.documentElement.scrollLeft/scrollTop,但在较旧的基于 WebKit 的浏览器中则不行,例如在 Safari(bug 5991)中,我们应该使用 document.body 而不是 document.documentElement。
幸运的是,我们根本不必记住这些特性,因为我们可以从 window.pageXOffset/pageYOffset 中获取页面当前滚动信息:
alert('当前已从顶部滚动:' + window.pageYOffset);
alert('当前已从左侧滚动:' + window.pageXOffset);这些属性是只读的。
备注
我们也可以从 window 的 scrollX 和 scrollY 属性中获取滚动信息
由于历史原因,存在了这两种属性,但它们是一样的:
window.pageXOffset是window.scrollX的别名。window.pageYOffset是window.scrollY的别名。
滚动:scrollTo,scrollBy,scrollIntoView
重要
必须在 DOM 完全构建好之后才能通过 JavaScript 滚动页面。
例如,如果我们尝试通过 <head> 中的脚本滚动页面,它将无法正常工作。
1.
window.scrollTo的两种写法① 旧写法:两个数字参数
// 滚动到文档左上角(0,0) window.scrollTo(0, 0); // 滚动到 x=0, y=500 的位置 window.scrollTo(0, 500);
- 第一个参数:
x(横向滚动距离,单位 px)- 第二个参数:
y(纵向滚动距离,单位 px)这是最早的形式,兼容性最好,但不能设置平滑滚动。
② 新写法:配置对象(推荐)
window.scrollTo({ top: 500, // 必需:纵向滚动距离 left: 0, // 可选:横向滚动距离,默认 0 behavior: "smooth" // "auto" | "smooth" });参数说明:
top:目标纵向滚动位置(类似旧写法里的 y)left:目标横向滚动位置(类似旧写法里的 x)behavior:
"auto":直接跳过去(默认)"smooth":平滑滚动 ✅
2.
window.scrollBy的两种写法
scrollBy是“相对当前滚动位置”进行滚动。① 旧写法:两个数字参数
// 向下滚动 300px window.scrollBy(0, 300); // 向右滚动 100px,向下滚动 200px window.scrollBy(100, 200);
- 第一个参数:相对当前的 x 增量
- 第二个参数:相对当前的 y 增量
② 新写法:配置对象(推荐)
window.scrollBy({ top: 300, // 相对当前向下 300px left: 0, // 相对当前向右 0px behavior: "smooth" // 平滑滚动 });和
scrollTo的对象形式几乎一样,只是语义是“相对滚动”而不是“滚到绝对位置”。
3.
window.scroll是什么?还有一个容易混淆的:
window.scroll(0, 500); window.scroll({ top: 500, behavior: "smooth" });
window.scroll基本上就是window.scrollTo的别名- 也支持 (x, y) 和 options 对象 两种写法
可以通过更改 scrollTop/scrollLeft 来滚动常规元素。
我们可以使用 document.documentElement.scrollTop/scrollLeft 对页面进行相同的操作(Safari 除外,而应该使用 document.body.scrollTop/Left 代替)。
或者,有一个更简单的通用解决方案:使用特殊方法 window.scrollBy(x,y) 和 window.scrollTo(pageX,pageY)。
-
方法
scrollBy(x,y)将页面滚动至 相对于当前位置的(x, y)位置。例如,scrollBy(0,10)会将页面向下滚动10px。下面这个按钮演示了这个方法:
jswindow.scrollBy(0,10) -
方法
scrollTo(pageX,pageY)将页面滚动至 绝对坐标,使得可见部分的左上角具有相对于文档左上角的坐标(pageX, pageY)。就像设置了scrollLeft/scrollTop一样。要滚动到最开始,我们可以使用
scrollTo(0,0)。jswindow.scrollTo(0,0) behave参数? 思华滚动? 对向形式? el.scrollTo({top:0,left:0,behave:'smooth'})
这些方法适用于所有浏览器。
scrollIntoView
为了完整起见,让我们再介绍一种方法:elem.scrollIntoView(top)。
elem.scrollIntoView是 DOM 元素的方法,用来让当前元素滚动到浏览器可视区域中。
1. 基本语法
有三种常见写法:
// 1. 不带参数 element.scrollIntoView(); // 2. 布尔参数(老写法) element.scrollIntoView(alignToTop); // 3. 配置对象(推荐) element.scrollIntoView(options);其中
element就是一个 DOM 元素对象,比如通过document.querySelector()拿到的节点。
2. 布尔参数写法(旧)
element.scrollIntoView(true); // 元素滚到可视区域顶部(默认) element.scrollIntoView(false); // 元素滚到可视区域底部
true:元素的顶端对齐到滚动容器的可视区域顶端false:元素的底部对齐到可视区域底部这种写法兼容性很好,但不够灵活。
3. 配置对象写法(推荐)
element.scrollIntoView({ behavior: "smooth", // "auto" | "smooth" block: "start", // "start" | "center" | "end" | "nearest" inline: "nearest" // "start" | "center" | "end" | "nearest" });参数说明
behavior
"auto":立刻滚动(默认)"smooth":平滑滚动(常用)block(垂直方向对齐方式)
"start":让元素的顶部对齐到可视区域的顶部"center":让元素在可视区域垂直居中"end":让元素的底部对齐到可视区域底部"nearest":滚动最少的距离使其可见(默认)inline(水平方向对齐方式,含义与block类似)
"start" | "center" | "end" | "nearest"
对 elem.scrollIntoView(top) 的调用将滚动页面以使 elem 可见。它有一个参数:
- 如果
top=true(默认值),页面滚动,使elem出现在窗口顶部。元素的上边缘将与窗口顶部对齐。 - 如果
top=false,页面滚动,使elem出现在窗口底部。元素的底部边缘将与窗口底部对齐。
下面这个按钮会滚动页面,以使其自身定位在窗口顶部:
this.scrollIntoView()下面这个按钮会滚动页面,以使其自身定位在窗口底部:
this.scrollIntoView(false)禁止滚动
有时候我们需要使文档“不可滚动”。例如,当我们需要用一条需要立即引起注意的大消息来覆盖文档时,我们希望访问者与该消息而不是与文档进行交互。
要使文档不可滚动,只需要设置 document.body.style.overflow = "hidden"。该页面将“冻结”在其当前滚动位置上。
试一试:
document.body.style.overflow = ‘hidden’document.body.style.overflow = ‘’第一个按钮用于冻结滚动,第二个按钮则用于恢复滚动。
我们还可以使用相同的技术来冻结其他元素的滚动,而不仅仅是 document.body。
这个方法的缺点是会使滚动条消失。如果滚动条占用了一些空间,它原本占用的空间就会空出来,那么内容就会“跳”进去以填充它。
这看起来有点奇怪,但是我们可以对比冻结前后的 clientWidth。如果它增加了(滚动条消失后),那么我们可以在 document.body 中滚动条原来的位置处通过添加 padding,来替代滚动条,这样这个问题就解决了。保持了滚动条冻结前后文档内容宽度相同。
总结
几何:
-
文档可见部分的 width/height(内容区域的 width/height):
document.documentElement.clientWidth/clientHeight -
整个文档的 width/height,其中包括滚动出去的部分:
javascriptlet scrollHeight = Math.max( document.body.scrollHeight, document.documentElement.scrollHeight, document.body.offsetHeight, document.documentElement.offsetHeight, document.body.clientHeight, document.documentElement.clientHeight );
滚动:
读取当前的滚动:window.pageYOffset/pageXOffset。
更改当前的滚动:
window.scrollTo(pageX,pageY)—— 绝对坐标,window.scrollBy(x,y)—— 相对当前位置进行滚动,elem.scrollIntoView(top)—— 滚动以使elem可见(elem与窗口的顶部/底部对齐)。
坐标
要移动页面的元素,我们应该先熟悉坐标。
大多数 JavaScript 方法处理的是以下两种坐标系中的一个:
- 相对于窗口 —— 类似于
position:fixed,从窗口的顶部/左侧边缘计算得出。- 我们将这些坐标表示为
clientX/clientY,当我们研究事件属性时,就会明白为什么使用这种名称来表示坐标。
- 我们将这些坐标表示为
- 相对于文档 —— 与文档根(document root)中的
position:absolute类似,从文档的顶部/左侧边缘计算得出。- 我们将它们表示为
pageX/pageY。
- 我们将它们表示为
当页面滚动到最开始时,此时窗口的左上角恰好是文档的左上角,它们的坐标彼此相等。但是,在文档移动之后,元素的窗口相对坐标会发生变化,因为元素在窗口中移动,而元素在文档中的相对坐标保持不变。
在下图中,我们在文档中取一点,并演示了它滚动之前(左)和之后(右)的坐标:

当文档滚动了:
pageY—— 元素在文档中的相对坐标保持不变,从文档顶部(现在已滚动出去)开始计算。clientY—— 窗口相对坐标确实发生了变化(箭头变短了),因为同一个点越来越靠近窗口顶部。
元素坐标:getBoundingClientRect
方法 elem.getBoundingClientRect() 返回最小矩形的窗口坐标,该矩形将 elem 作为内建 DOMRect 类的对象。
主要的 DOMRect 属性:
x/y—— 矩形原点相对于窗口的 X/Y 坐标,width/height—— 矩形的 width/height(可以为负)。
此外,还有派生(derived)属性:
top/bottom—— 顶部/底部矩形边缘的 Y 坐标,left/right—— 左/右矩形边缘的 X 坐标。
例如,点击下面这个按钮以查看其窗口坐标:
button.getBoundingClientRect()如果你滚动此页面并重复点击上面那个按钮,你会发现随着窗口相对按钮位置的改变,其窗口坐标(如果你垂直滚动页面,则为 y/top/bottom)也随之改变。
下面这张是 elem.getBoundingClientRect() 的输出的示意图:

正如你所看到的,x/y 和 width/height 对矩形进行了完整的描述。可以很容易地从它们计算出派生(derived)属性:
left = xtop = yright = x + widthbottom = y + height
请注意:
- 坐标可能是小数,例如
10.5。这是正常的,浏览器内部使用小数进行计算。在设置style.left/top时,我们不是必须对它们进行舍入。 - 坐标可能是负数。例如滚动页面,使
elem现在位于窗口的上方,则elem.getBoundingClientRect().top为负数。
备注
为什么需要派生(derived)属性?如果有了 x/y,为什么还要还会存在 top/left?
从数学上讲,一个矩形是使用其起点 (x,y) 和方向向量 (width,height) 唯一定义的。因此,其它派生属性是为了方便起见。
从技术上讲,width/height 可能为负数,从而允许“定向(directed)”矩形,例如代表带有正确标记的开始和结束的鼠标选择。
负的 width/height 值表示矩形从其右下角开始,然后向左上方“增长”。
这是一个矩形,其 width 和 height 均为负数(例如 width=-200,height=-100):

正如你所看到的,在这个例子中,left/top 与 x/y 不相等。
但是实际上,elem.getBoundingClientRect() 总是返回正数的 width/height,这里我们提及负的 width/height 只是为了帮助你理解,为什么这些看起来重复的属性,实际上并不是重复的。
IE 浏览器不支持 x/y
由于历史原因,IE 浏览器不支持 x/y 属性。
因此,我们可以写一个 polyfill(在 DomRect.prototype 中添加一个 getter),或者仅使用 top/left,因为对于正值的 width/height 来说,它们和 x/y 一直是一样的,尤其是对于 elem.getBoundingClientRect() 的结果。
坐标的 right/bottom 与 CSS position 属性不同
相对于窗口(window)的坐标和 CSS position:fixed 之间有明显的相似之处。
但是在 CSS 定位中,right 属性表示距右边缘的距离,而 bottom 属性表示距下边缘的距离。
elementFromPoint(x, y)
对 document.elementFromPoint(x, y) 的调用会返回在窗口坐标 (x, y) 处嵌套最多(the most nested)的元素。
语法如下:
let elem = document.elementFromPoint(x, y);例如,下面的代码会高亮显示并输出现在位于窗口中间的元素的标签:
let centerX = document.documentElement.clientWidth / 2;
let centerY = document.documentElement.clientHeight / 2;
let elem = document.elementFromPoint(centerX, centerY);
elem.style.background = "red";
alert(elem.tagName);因为它使用的是窗口坐标,所以元素可能会因当前滚动位置而有所不同
备注
对于在窗口之外的坐标,elementFromPoint 返回 null
方法 document.elementFromPoint(x,y) 只对在可见区域内的坐标 (x,y) 起作用。
如果任何坐标为负或者超过了窗口的 width/height,那么该方法就会返回 null。
在大多数情况下,这种行为并不是一个问题,但是我们应该记住这一点。
如果我们没有对其进行检查,可能就会发生下面这个典型的错误:
let elem = document.elementFromPoint(x, y);
// 如果坐标恰好在窗口外,则 elem = null
elem.style.background = ''; // Error!用于 “fixed” 定位
大多数时候,我们需要使用坐标来确定某些内容的位置。
想要在某元素附近展示内容,我们可以使用 getBoundingClientRect 来获取这个元素的坐标,然后使用 CSS position 以及 left/top(或 right/bottom)。
例如,下面的函数 createMessageUnder(elem, html) 在 elem 下显示了消息:
let elem = document.getElementById("coords-show-mark");
function createMessageUnder(elem, html) {
// 创建 message 元素
let message = document.createElement('div');
// 在这里最好使用 CSS class 来定义样式
message.style.cssText = "position:fixed; color: red";
// 分配坐标,不要忘记 "px"!
let coords = elem.getBoundingClientRect();
message.style.left = coords.left + "px";
message.style.top = coords.bottom + "px";
message.innerHTML = html;
return message;
}
// 用法:
// 在文档中添加 message 保持 5 秒
let message = createMessageUnder(elem, 'Hello, world!');
document.body.append(message);
setTimeout(() => message.remove(), 5000);点击下面这个按钮来运行它:
Button with id=“coords-show-mark”, the message will appear under it但是请注意一个重要的细节:滚动页面时,消息就会从按钮流出。
原因很显然:message 元素依赖于 position:fixed,因此当页面滚动时,它仍位于窗口的同一位置。
要改变这一点,我们需要使用基于文档(document)的坐标和 position:absolute 样式。
文档坐标
文档相对坐标从文档的左上角开始计算,而不是窗口。
在 CSS 中,窗口坐标对应于 position:fixed,而文档坐标与顶部的 position:absolute 类似。
我们可以使用 position:absolute 和 top/left 来把某些内容放到文档中的某个位置,以便在页面滚动时,元素仍能保留在该位置。但是我们首先需要正确的坐标。
这里没有标准方法来获取元素的文档坐标。但是写起来很容易。
这两个坐标系统通过以下公式相连接:
pageY=clientY+ 文档的垂直滚动出的部分的高度。pageX=clientX+ 文档的水平滚动出的部分的宽度。
函数 getCoords(elem) 将从 elem.getBoundingClientRect() 获取窗口坐标,并向其中添加当前滚动:
// 获取元素的文档坐标
function getCoords(elem) {
let box = elem.getBoundingClientRect();
return {
top: box.top + window.pageYOffset,
right: box.right + window.pageXOffset,
bottom: box.bottom + window.pageYOffset,
left: box.left + window.pageXOffset
};
}如果在上面的示例中,我们将其与 position:absolute 一起使用,则在页面滚动时,消息仍停留在元素附近。
修改后的 createMessageUnder 函数:
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;
}评论
评论加载中……