首页  >  文章  >  web前端  >  getCompatedStyle:好的、坏的和丑陋的部分

getCompatedStyle:好的、坏的和丑陋的部分

WBOY
WBOY原创
2024-07-28 10:42:23753浏览

getCompulatedStyle 是独一无二的。它在后台做了一些有趣的事情,并且有自己独特的使用方式。在这篇文章中,我将分享我对这个苦乐参半的 API 的经验和想法。

我希望自己能编辑这篇文章,因为我现在经常与这个功能交互。我还认为它也可能有很多利基案例。顺便说一句,我会在帖子末尾添加一些对我的旅程有帮助的链接。

什么是 getCompatedStyle?

直接来自 MDN:

在应用活动样式表并解析这些值可能包含的任何基本计算后,Window.getComputedStyle() 方法返回一个包含元素的所有 CSS 属性值的对象。

各个 CSS 属性值可通过对象提供的 API 或通过 CSS 属性名称索引来访问。

纯 JavaScript 中的使用示例:

const element = document.getElementById("#box");

// Get the computed styles of an element
const styles = getComputedStyle(element);

简单地说,它返回给定元素的样式。这里有趣的一点是解决计算。假设您在 CSS 中使用 calc() 给出了 width 属性:

#box {
  width: calc(100px - 80px);
}

结果会给你20px。这意味着您实际上可以从 JavaScript 访问 CSS 计算的结果。它甚至可以计算视口和百分比单位(100vh、50% 等)并以像素为单位给出结果。太棒了!

问题: 计算值取决于属性,并且不是静态的。这意味着你不能指望它会计算出这样的结果:

#box {
  --box-width: calc(100px - 80px);
  width: var(--box-width);
}
// boxWidth variable will be set to string `calc(100px - 80px)`
// Not `20px`
const boxWidth = getComputedStyle(element)["--box-width"];

这完全有道理,因为静态计算 CSS 变量的结果是不可能的。这取决于环境和财产方面(例如百分比)。

但是,如果您尝试访问宽度属性,您将拥有 20 个像素。它最终会计算特定元素的宽度:

// 20px
const { width } = getComputedStyle(element);

这很酷,但是实际用例是什么?

将元素从 0px 转换为 auto

如果您在 calc-size() 在现代浏览器上广泛使用之后阅读这篇文章,请停止并继续使用它。它可能会优于我们这里的解决方案。如果您陷入无法过渡到汽车的现实,请继续前进!

在您的 Web 开发之旅中,您可能多次遇到过自动动画。动画库可能很适合这里,但如果我告诉你根本不需要它怎么办?

假设有一个可以打开/关闭的框,其中包含一些文本内容。文本内容将是动态的,因此我们无法预先为其指定最大高度。哦设计师也想要一个动画,它必须从 0px 慢慢过渡到自动高度。

以下是我们的 HTML 和 CSS:

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  </head>
  <style>
    #box {
      position: relative;
      display: block;
      width: fit-content;
      height: 0px;
      overflow: hidden;
      background-color: rebeccapurple;
      color: whitesmoke;
      transition: 1s;
    }
  </style>
  <body>
    <button type="button" id="toggle">toggle</button>
    <div id="box"></div>

    <script type="module" src="/src/main.ts" defer></script>
  </body>
</html>

在脚本方面,我们一开始不会做太多事情。我们只需要保持一个简单的状态来放置或删除文本内容。 (是否使用TS取决于你):

// get the wrapper div
const box = document.getElementById("box") as HTMLDivElement;
// get the trigger button
const button = document.getElementById("toggle") as HTMLButtonElement;
// state that keeps track
let toggle = false;

function changeBoxContent() {
  if (toggle) {
    box.innerText = `Lorem ipsum dolor sit, 
    amet consectetur adipisicing elit.
    Minima culpa ipsum quibusdam quia
    dolorum excepturi sequi non optio,
    ad eaque? Temporibus natus eveniet
    provident sit cum harum,
    praesentium et esse?`;
  } else {
    box.innerText = "";
  }
}

button.addEventListener("click", () => {
  // flip the toggle
  toggle = !toggle;
  // update the content
  changeBoxContent();

  // ...
});

这里的代码只是将 box 的 insideText 设置为某个伪字符串或将其恢复为空字符串。

如果您现在单击该按钮,您将看到没有任何变化。这是因为我们将框的高度设置为 0 像素并隐藏其溢出。

为了知道文本需要多少空间,我们可以将框的高度设置为 auto 并在框上调用 getCompulatedStyle 。这里的想法是让元素通过 auto 放大到所需的大小,并且这里的 getCompulatedStyle 将为我们获取该大小(以像素为单位)。

button.addEventListener("click", () => {
  // flip the toggle
  toggle = !toggle;
  // update the content
  changeBoxContent();

  // set the element's height to `auto`, this enlarges the element to fit it's content
  box.style.height = "auto";

  // we got our desired height property!
  const height = getComputedStyle(box).height;

  console.log(height);
});

当您单击按钮时,您应该看到该框具有所需的高度。我们还可以在控制台中看到以像素为单位的高度:

Result A1

这很酷,但我们还不确定要转型。

既然我们知道我们需要的高度,也许我们可以将盒子的高度设置回原来的高度。在对 requestAnimationFrame 的调用中,我们可以将其设置为从 getCompulatedStyle 获得的高度。我们来试试吧!

button.addEventListener("click", () => {
  // flip the toggle
  toggle = !toggle;
  // update the content
  changeBoxContent();

  // set the element's height to `auto`, this enlarges the element to fit it's content
  box.style.height = "auto";

  // we got our desired height property!
  const height = getComputedStyle(box).height;

  // set the height back to where it was (0px)
  box.style.height = "";

  // set the final height in next animation frame
  requestAnimationFrame(() => {
    box.style.height = height;
  });
});

既然我们知道将高度设置为 0px 并在下一个动画帧中更改它,我们应该看到它有动画,对吧?

嗯,不完全是。如果您在高端 PC 的 Chrome 中运行此程序,您应该观察过渡动画,但在低端 PC 或某些浏览器(如 Firefox)中,您看不到任何变化。

我电脑上 Firefox 和 Chrome 的比较:

getComputedStyle: The good, the bad and the ugly parts

发生这种情况的原因是布局的计算发生在样式之后。我们不能保证布局计算会在样式计算之前发生。浏览器似乎也有不同的 requestAnimationFrame 实现。 (您可以在这里了解更多)

Don't get into despair though! We have multiple solutions for this. We can:

  • Force the browser to run layout calculations immediately before styles
  • Use intertwined requestAnimationFrames to make sure the animation will be run on the next tick (also known as double rAF())

Let's try forcing the browser to calculate styles first. Remember getComputedStyle? I'm sure you do. It'll come to our rescue here too!

Right after we set height back to 0px, we'll force to recalculate the layout:

button.addEventListener("click", () => {
  // flip the toggle
  toggle = !toggle;
  // update the content
  changeBoxContent();

  // set the element's height to `auto`, this enlarges the element to fit it's content
  box.style.height = "auto";

  // we got our desired height property!
  const height = getComputedStyle(box).height;

  // set the height back to where it was (reset)
  box.style.height = "";

  // we're synchronously forcing the browser to recalculate the height of the element
  getComputedStyle(box).height;

  // set the final height in next animation frame
  requestAnimationFrame(() => {
    box.style.height = height;
  });
});

GOTCHA: You might be thinking why we're accessing height property but not assigning it to anything. Well that's because getComputedStyle computes a property on access. It's actually to make it more optimized, it'll only run layout calculations on access to top, left, bottom, right, width and height. Its not documented but good to keep in mind. Try changing it to getComputedStyle(box), you'll see nothing has changed.

So that was one way to solve it and honestly I like this way much better. It's good to know double rAF() trick too, though.

For that we simply need to wrap our requestAnimationFrame with another requestAnimationFrame:

button.addEventListener("click", () => {
  // flip the toggle
  toggle = !toggle;
  // update the content
  changeBoxContent();

  // set the element's height to `auto`, this enlarges the element to fit it's content
  box.style.height = "auto";

  // we got our desired height property!
  const height = getComputedStyle(box).height;

  // set the height back to where it was (reset)
  box.style.height = "";

  // set the final height in next animation frame
  requestAnimationFrame(() => {
    requestAnimationFrame(() => {
      box.style.height = height;
    });
  });
});

Think of it like in the next frame, we've queued an animation that'll run on the next frame. I know it sounds weird but that used to solve lots of problems since it has a bug in Chrome too.

That's the wrap! As you can see in a modern world where we have WAAPI, transitions and getComputedStyle still have their use! It's a bit nasty to understand at start but it has it's own way of doing things, what can I say!

Sources:
Transition to Height Auto With Vue.js by Markus Oberlehner
Need cheap paint? Use getComputedStyle().opacity by Webventures

以上是getCompatedStyle:好的、坏的和丑陋的部分的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn