首頁 >web前端 >css教學 >getCompatedStyle:好的、壞的和醜陋的部分

getCompatedStyle:好的、壞的和醜陋的部分

WBOY
WBOY原創
2024-07-28 10:42:23789瀏覽

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