>  기사  >  웹 프론트엔드  >  getComputedStyle: 좋은 부분, 나쁜 부분, 추악한 부분

getComputedStyle: 좋은 부분, 나쁜 부분, 추악한 부분

WBOY
WBOY원래의
2024-07-28 10:42:23729검색

getCompulatedStyle은 독특한 기능입니다. 백그라운드에서 몇 가지 흥미로운 작업을 수행하며 고유한 사용 방법이 있습니다. 이 게시물에서는 이 씁쓸하면서도 달콤한 API에 대한 내 경험과 생각을 공유하겠습니다.

요즘 이 기능을 많이 사용하기 때문에 이 게시물을 직접 편집할 것으로 예상됩니다. 나는 또한 그것에 대한 많은 틈새 사례가 있을 수 있다고 생각합니다. 그런데 포스팅 마지막에 여행에 도움이 되었던 링크를 걸어 놓겠습니다.

getComputedStyle이란 무엇입니까?

MDN에서 직접:

Window.getCompulatedStyle() 메서드는 활성 스타일시트를 적용하고 해당 값에 포함될 수 있는 기본 계산을 해결한 후 요소의 모든 CSS 속성 값을 포함하는 객체를 반환합니다.

개별 CSS 속성 값은 개체에서 제공하는 API를 통해 액세스하거나 CSS 속성 이름으로 색인을 생성하여 액세스합니다.

일반 JavaScript의 사용 예:

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

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

간단히 말하면 주어진 요소에 대한 스타일을 반환합니다. 여기서 흥미로운 점은 계산 해결입니다. CSS에서 calc():
를 사용하여 너비 속성을 지정했다고 가정해 보겠습니다.

#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에서 자동으로 전환

최신 브라우저에서 calc-size()를 널리 사용할 수 있게 된 이후에 이 게시물을 읽고 있다면 잠시 멈춰서 사용해 보세요. 여기서는 우리 솔루션보다 성능이 뛰어날 것 같습니다. 자동으로 전환할 수 없는 현실에 갇혀 있다면 계속 진행하세요!

웹 개발 과정에서 자동 애니메이션과 자동 애니메이션을 몇 번 접한 적이 있을 것입니다. 애니메이션 라이브러리가 여기에 잘 맞을 수도 있지만 전혀 필요하지 않다고 말하면 어떻게 될까요?

일부 텍스트 콘텐츠가 포함된 켜기/끄기를 전환할 수 있는 상자가 있다고 가정해 보겠습니다. 텍스트 콘텐츠는 동적이므로 미리 최대 높이를 지정할 수 없습니다. 아, 디자이너도 거기에 애니메이션을 원합니다. 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();

  // ...
});

여기 코드는 단순히 상자의 innerText를 일부 의사 문자열로 설정하거나 빈 문자열로 되돌립니다.

지금 버튼을 클릭하면 아무것도 변경되지 않은 것을 볼 수 있습니다. 상자의 높이를 0픽셀로 설정하고 오버플로를 숨기기 때문입니다.

텍스트에 필요한 공간을 확인하기 위해 상자 높이를 자동으로 설정하고 상자에서 getCompulatedStyle을 호출할 수 있습니다. 여기서의 아이디어는 자동을 통해 필요한 만큼 요소를 확대하고 여기에서 getComputeStyle이 해당 크기를 픽셀 단위로 가져오는 것입니다.

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 호출 내에서 getComputeStyle의 높이로 설정할 수 있습니다. 한번 해보세요!

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

위 내용은 getComputedStyle: 좋은 부분, 나쁜 부분, 추악한 부분의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.