如何精准获取网页元素尺寸?

在前端开发中,获取元素的尺寸(如宽度、高度)是常见的操作,特别是在动态布局、响应式设计、界面交互中。

下面按照浏览器渲染的步骤依次说明:

1. 构建 DOM 树(DOM Construction)

浏览器首先解析 HTML 并构建 DOM 树,DOM 树中包含页面的所有元素。此时未进行任何样式计算和布局计算。

1.1 dom.style.xxx

dom.style.width 和 dom.style.height:如果页面中有设置内联样式,能直接读取或设置这些值。

举个 🌰

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  </head>
  <body>
    <div class="box" style="background-color: pink; width: 200px; height: 200px"></div>
  </body>
</html>

📢注意 

如果没有在 HTML 元素中设置内联样式,使用 dom.style.xxx访问尺寸,得到的值是空字符串。其仅获取内联样式,它不会考虑任何外部或内部 CSS 文件中的规则。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      .box {
        width: 100px;
        height: 100px;
      }
    </style>
  </head>
  <body>
    <div class="box"></div>
  </body>
</html>

该方法获取的是 DOM 树中的内容,不会导致回流,性能很高。 

适用场景:

  • 动态交互:如果在交互过程中需要调整元素的宽度,并且这种调整是瞬时的、局部的(例如点击按钮时改变元素宽度),使用 dom.style.width 非常合适。
  • 动画效果:通过 JS 动态修改元素的宽度,配合 requestAnimationFrame 可以创建平滑的动画效果。
  • 小范围控制:如果页面中的元素需要进行局部样式调整而不需要修改全局样式,dom.style.xxx提供了简洁的方式。

🌰:动态改变元素宽度

// 当点击按钮时改变元素宽度
const button = document.querySelector('button');
const box = document.querySelector('.box');

button.addEventListener('click', () => {
  box.style.width = '300px'; // 点击时,box 宽度变为 300px
});

Tip

1、返回的是字符串值:如果获取 dom.style.width 的值,返回的是字符串(例如 '100px'),需要进行类型转换(如 parseInt() 或 parseFloat())来进行数值运算。

2、设置 dom.style.width 只会影响内联样式,不会更改外部样式表或 <style> 标签中的定义。如果元素的样式是由类名控制的,那么使用 dom.style.width 不会起作用。

3、dom.style.width 不会影响被计算后的宽度。即使通过 dom.style.width 设置了元素宽度,offsetWidth 或 clientWidth 等方法返回的尺寸可能仍然是计算后的值,而不单纯是内联样式的值。例如,如果元素有 padding 或 border,计算后的宽度会包括这些额外的空间。

2. 构建渲染树(Render Tree Construction)

在构建渲染树时,浏览器会根据样式表(CSS)计算出每个元素的样式,形成渲染树。此时,浏览器会考虑外部和内部样式、内联样式、继承的样式等。

2.1 window.getComputedStyle(xx)

浏览器会开始计算元素的宽度、高度等相关样式,这时 window.getComputedStyle(xx) 方法才会返回完整的计算样式(一个对象)。这是获取 最终样式(包括由 CSS 计算出来的所有属性,如宽度、高度、边距、内边距等)的最佳方法。

注意📢: 

getComputedStyle 这个函数获取的值,不可能是 %、em、rem 这种相对单位数据,一定是计算后的数据拼接 px。

举个 🌰:仅定义一个 div,那么它的宽度是什么?

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  </head>
  <body>
    <div class="box"></div>
  </body>
</html>

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      .box {
        width: 2em;
        height: 100px;
        background-color: pink;
      }
    </style>
  </head>
  <body>
    <div class="box" style="width: 3em"></div>
  </body>
</html>

Tip:getComputedStyle 是一个计算过程,性能相对较差,尤其是对动态改变的样式进行频繁访问时。返回的是字符串值,需要解析为数值。

3. 布局计算(Layout / Reflow)

当渲染树和样式计算完成,浏览器进行布局计算(也叫 Reflow),根据元素的尺寸、位置、内外边距等信息计算每个元素在页面中的确切位置和尺寸。

获取布局树的信息:元素在页面上的布局信息,它在页面上占据多少空间。数值是没有单位的。

3.1 clientWidth 和 clientHeight

clientWidth 和 clientHeight 返回元素的可视区域的宽度和高度(包括内边距),不包括边框、外边距和滚动条。对于元素来说,这些属性值是只读的。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .box {
        width: 100px;
        height: 100px;
        border: 1px solid palevioletred;
        padding: 10px;
        margin: 10px;
        background-color: pink;
      }
    </style>
  </head>
  <body>
    <div class="box"></div>
  </body>
</html>

Tip:访问非常快速。不包括滚动条和边框,适用于需要知道内容区尺寸的场景。

布局树的信息不等于计算后的样式信息。

3.2 offsetWidth 和 offsetHeight

offsetWidth 和 offsetHeight 返回元素的宽度和高度(包括内边距、边框和滚动条,但不包括外边距)。它们是一个整数值。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      * {
        margin: 0;
        padding: 0;
      }
      .box {
        width: 120px;
        height: 100px;
        border: 1px solid palevioletred;
        padding: 10px;
        margin: 10px;
        background-color: pink;
        overflow: scroll;
      }
      .item {
        margin: auto;
        padding: 5px;
        border: 1px solid palegoldenrod;
        width: 100px;
        height: 1000px;
        background-color: peachpuff;
      }
    </style>
  </head>
  <body>
    <div class="box">
      <div class="item"></div>
    </div>
  </body>
</html>

Tip:获取该值时,浏览器会考虑到当前渲染的实际效果。

3.3 scrollWidth 和 scrollHeight

scrollWidth 和 scrollHeight 返回元素的完整内容尺寸(包括超出可视区域的部分),也就是说,它们考虑了内容的溢出部分。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      .container {
        width: 200px;
        height: 100px;
        overflow: auto;
        background-color: antiquewhite;
        border: 1px solid yellow;
      }
      .content {
        width: 300px;
        height: 400px;
        background-color: lightblue;
      }
    </style>
  </head>
  <body>
    <div class="container" id="container">
      <div class="content">
        <p>这是一个超出可视区域的内容。</p>
        <p>这是第二行内容。</p>
        <p>这是第三行内容。</p>
        <p>这是第四行内容。</p>
        <p>这是第五行内容。</p>
        <p>这是第六行内容。</p>
      </div>
    </div>
  </body>
</html>

适用于需要获取元素内容的总尺寸(包括滚动区域)。 对于动态内容或大于可视区域的内容非常有用。

3.4 window.innerWidth 和 window.innerHeight

获取浏览器窗口的内宽度和高度,不包括工具栏、滚动条等外部区域。适用于响应式布局和窗口缩放时,确定视口的大小(具有实时性)。

但是不能直接获取元素的尺寸,只能获取视口的尺寸。

总结:在布局计算阶段,方法如 offsetWidth 和 offsetHeight 会返回元素的实际尺寸。因为这些属性基于浏览器的布局计算结果,考虑元素的内外边距、边框、滚动条等。返回页面实际渲染后的尺寸,反映浏览器在布局计算后的结果

​4. 绘制(Painting)

在布局计算完成后,浏览器会根据渲染树的结构将每个元素的视觉表现绘制到屏幕上。此时,所有元素的位置和尺寸已确定。

4.1 getBoundingClientRect() 

视觉尺寸

getBoundingClientRect() 方法返回的是一个包含元素的 大小相对于视口(viewport)的位置 的 DOMRect 对象(包含 top、left、right、bottom、x、y、width 和 height 属性)。简而言之,返回的是元素在当前视口中的位置和尺寸,而不是整个文档或页面中的位置。

常用于获取元素相对于视口的矩形区域,如用于计算滚动位置。可以处理 偏移(如 CSS 的 transform 属性)。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      .box {
        width: 100px;
        height: 100px;
        padding: 10px;
        margin: auto;
        border: 4px solid palevioletred;
        background-color: pink;
        margin-bottom: 100px;
      }
      .scale {
        width: 100px;
        height: 100px;
        padding: 10px;
        margin: auto;
        border: 4px solid lavender;
        background-color: lightcyan;
        /* 放大两倍 */
        transform: scale(2);
      }
    </style>
  </head>
  <body>
    <div class="box"></div>
    <div class="scale"></div>
  </body>
</html>

注意:视觉信息不等于布局信息。

Tip:getBoundingClientRect 返回的值是相当于视口的,通过加上滚动偏移量,将元素的坐标转换为相对于整个页面的位置。

视口与页面坐标系的区别

1、视口坐标系

  1. 视口坐标系是浏览器窗口的坐标系,即用户当前可以看到的区域。
  2. 视口坐标系的原点通常是窗口的左上角 (0, 0),并且它随着滚动条的滚动而变化。
  3. getBoundingClientRect 返回值是相当于视口坐标系的,即元素的位置和尺寸相对于当前视口的位置,忽略了滚动条的影响

2、页面坐标系(文档坐标系)

  1. 页面坐标系是整个页面的坐标系,原点  (0, 0) 在页面的左上角,不受视口滚动的影响。
  2. 如果页面有滚动条,元素的位置会随着页面的滚动发生变化,因此页面坐标系的位置是相对文档的。

举个 🌰

页面上有一个元素,可以使用 getBoundingClientRect() 获取它相对于视口的位置,并在页面滚动的情况下,通过调整来获取它相对于整个页面的坐标。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      body {
        height: 2000px; /* 设置页面足够高,便于滚动 */
      }
      #myElement {
        width: 100px;
        height: 100px;
        background-color: violet;
        margin-top: 1000px; /* 假设元素距离页面顶部有 1000px */
        color: white;
      }
    </style>
  </head>
  <body>
    <div id="myElement">我是一个元素</div>

    <script>
      const element = document.getElementById('myElement');

      // 获取元素相对于视口的边界信息
      const rect = element.getBoundingClientRect();
      console.log('元素相对于视口的位置:');
      console.log('top:', rect.top); // 视口顶部到元素顶部的距离
      console.log('left:', rect.left); // 视口左边到元素左边的距离

      // 获取页面的滚动偏移量
      const scrollTop = window.scrollY || document.documentElement.scrollTop;
      const scrollLeft = window.scrollX || document.documentElement.scrollLeft;

      // 计算元素相对于页面的位置
      const pageTop = rect.top + scrollTop;
      const pageLeft = rect.left + scrollLeft;

      console.log('元素相对于页面的位置:');
      console.log('top:', pageTop); // 页面顶部到元素顶部的距离
      console.log('left:', pageLeft); // 页面左边到元素左边的距离
    </script>
  </body>
</html>

当页面没有滚动时,元素相当于视口的位置和相当于页面的位置一样。 

5. 补充

5.1 为什么布局树的信息不等于计算后的样式信息呢?

在浏览器渲染过程中,布局树(layout tree)计算后的样式(computed styles) 是两个不同的概念,它们的生成过程和用途也有所不同。具体来说,布局树的信息不等于计算后的样式信息,主要原因是它们的作用和形成的时机不同,且它们关注的内容也有所区别。

1、布局树(layout tree)

布局树是在 渲染树 中,经过计算元素的几何信息后(比如位置、尺寸等),最终形成的树形结构。这个树🌲包含了 可见元素 的位置和尺寸信息,是浏览器用来确定元素位置和尺寸的关键数据。

  • 布局树中不包含被隐藏的元素(如 display: none 的元素)。
  • 布局树的生成是根据 计算后的样式 来完成的,但它只关心元素的 位置和大小,而不关心元素的具体样式(比如颜色、字体、边框等)。
  • 它在浏览器的 布局阶段 生成,通常是 计算样式之后 的结果。布局树是为了确定元素的最终位置和尺寸,为绘制阶段做准备。

2、计算后的样式(Computed Styles)

计算后的样式是浏览器计算出的一套最终的样式规则,它是基于 所有CSS规则(包括外部CSS、内联样式、浏览器默认样式等)综合计算得出的。这个过程涉及到:

  • 解析样式表并应用到 DOM 元素。
  • 将所有的样式合并成一个 最终的样式,包括继承的样式、用户定义的样式、计算的值等。

计算后的样式包括了每个元素的 所有样式信息,如 颜色、边框、字体大小、margin、padding、position 等

为什么它们不相等?

1、布局树只关心位置和尺寸

布局树的构建过程关注的是元素的几何信息,即元素的 宽度、高度、位置 等,它并不关心样式属性(如颜色、字体、边框等),这些属性不会影响布局树的形成。

例如,元素的 color 或 background 等视觉样式信息,并不会影响布局树。即使元素的颜色改变,布局树的结构和位置不会受到影响。

2、计算后的样式包含所有样式信息

计算后的样式是 元素最终应用的所有样式,其中包含了所有的视觉效果、布局信息、字体设置、边框、间距等。

计算后的样式信息比布局树的信息更加详细和全面,它包括了所有对元素外观有影响的属性,而不仅仅是影响元素几何位置和尺寸的属性。

举个 🌰

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <style>
      div {
        width: 200px;
        height: 100px;
        background-color: pink;
        padding: 10px;
        border: 5px solid palevioletred;
      }
    </style>
  </head>
  <body>
    <div id="box">Hello</div>
  </body>
</html>

1、计算后样式:

  1. width: 200px
  2. height: 100px
  3. background-color: pink
  4. padding: 10px
  5. border: 5px solid palevioletred

这些信息都会被 计算后样式 考虑,它包含了元素的所有视觉和布局样式。

2、布局树

布局树只会关心:元素的 实际宽度(包括 width + padding + border)、实际高度(包括 height + padding),以及元素的 位置(如左上角的位置),而不关心 background-color 或 color 等样式属性。

总结:

布局树主要关心的是元素的 几何属性(位置、尺寸等)。用于确定页面元素的 位置、排版 等。计算后样式包含了所有应用到元素的样式信息,包括字体、颜色、边框、背景等。提供了一个 全面的样式视图,用于确定元素的 外观行为

两者的区别就在于,计算后的样式描述的是元素如何“看起来”,而布局树描述的是元素如何“占据空间”并参与到页面的渲染和布局中。

5.2 为什么视觉信息不等于布局信息呢?

布局信息在上面已经讲述,下面介绍一下视觉信息。

视觉信息涉及的是元素的外观样式,包括颜色、边框、背景、字体、阴影等样式属性,它们直接影响元素的渲染效果。视觉信息主要在绘制阶段应用,以确保元素的外观符合样式定义。

通常包含:

  • 元素的背景颜色、文字颜色、边框样式等。
  • 元素的字体、文本对齐方式、阴影效果等。
  • 透明度(eg:opacity)、过渡效果等影响外观的属性。
  • 渲染效果,如元素的渐变、透明度、图像背景等。

为什么视觉信息不等于布局信息?

1、不同的渲染阶段

浏览器的渲染过程是一个多阶段的过程:

  • 计算样式阶段:根据 CSS 样式表、内联样式和默认样式等,计算元素的最终样式属性,包括视觉信息(如颜色、字体、边框等)和布局信息(如宽高、位置、边距等)。
  • 布局阶段:计算元素的位置、尺寸以及它们如何排列在页面中。这里生成的就是布局信息,它决定了元素占据的空间。
  • 绘制阶段:将元素的视觉样式应用到页面中,包括背景颜色、边框、文本内容等。此时,浏览器将元素渲染到屏幕上。

在计算样式阶段,浏览器同时计算布局信息视觉信息。但在布局阶段,浏览器仅关注布局信息,而 视觉信息 并不影响布局过程,虽然它会影响元素最终的显示效果,但不会改变元素的位置、尺寸等布局属性。

2、视觉效果和布局信息的独立性

一些视觉样式不会改变元素的实际布局,反而可能只影响其外观。比如:

  • 透明度(opacity):它只影响元素的可见度,不会改变元素的占用空间或位置。
  • 背景颜色(background-color):它仅改变元素的背景色,不会影响元素的布局。
  • 文本颜色(color):它影响文本的颜色,但不改变文本的位置或元素的尺寸。
  • 阴影(box-shadow, text-shadow):它仅影响视觉效果,但不改变元素的布局(即元素仍然占据其原本的空间)。

举个 🌰

设置一个元素的 border: 10px solid black,它会增加元素的外部空间,从而改变布局信息,因为元素的总尺寸(包括边框)会增加。但它仍然是一个“视觉信息”的例子,因为它并不会影响元素的核心内容或功能。

3、CSS 属性的区别

  • 布局相关属性(影响元素的位置和尺寸):例如 width, height, margin, padding, position, top, left, right, bottom 等。

  • 视觉相关属性(影响元素的外观):例如 color, background-color, border, font-size, box-shadow, opacity 等。

布局属性会直接影响浏览器如何计算元素的位置和尺寸,从而影响元素在页面中的排列。而视觉属性则是根据布局结果来展示元素的外观,不会影响页面中的布局。

综上: 每种方法有其优缺点,开发者需要根据场景需求权衡选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值