ホームページ > 記事 > ウェブフロントエンド > getComputedStyle: 良い部分、悪い部分、醜い部分
getComputedStyle は他に類を見ないものです。バックグラウンドでいくつかの興味深いことを実行し、独自のユニークな使用方法を持っています。この投稿では、このほろ苦い API に関する私の経験と考えを共有します。
最近この機能をよく使うので、私自身もこの投稿を編集するつもりです。また、ニッチなケースもたくさんあると思います。ちなみに、この投稿の最後に、私の旅に役立ったリンクをいくつか載せておきます。
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 変数の結果を静的に計算することは不可能なので、これは完全に理にかなっています。それは環境と財産の側面によって異なります (たとえば、パーセンテージを考えてください)。
ただし、width プロパティにアクセスしようとすると、20 ピクセルが必要になります。最終的には特定の要素の幅を計算することになります:
// 20px const { width } = getComputedStyle(element);
それは素晴らしいことですが、実際の使用例は何ですか?
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(); // ... });
ここでのコードは、ボックスの innerText を疑似文字列に設定するか、空の文字列に戻すだけです。
ここでボタンをクリックしても、何も変わっていないことがわかります。これは、ボックスの高さを 0 ピクセルに設定し、オーバーフローを非表示にしているためです。
テキストに必要なスペースを知るには、ボックスの高さを auto に設定し、ボックスで getComputedStyle を呼び出します。ここでの考え方は、要素を自動で必要なだけ拡大させ、getComputedStyle がそのサイズをピクセル単位で取得することです。
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); });
ボタンをクリックすると、ボックスが必要な高さになっていることがわかります。コンソールで高さをピクセル単位で確認することもできます:
それは素晴らしいことですが、確実に移行するわけではありません。
必要な高さがわかっているので、ボックスの高さを元の位置に戻すことができるかもしれません。 requestAnimationFrame の呼び出し内で、getComputedStyle から取得した高さに設定できます。試してみましょう!
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 の比較:
これが起こる理由は、レイアウトの計算がスタイルの後に行われるためです。レイアウトの計算がスタイルの計算の前に行われることは保証できません。また、ブラウザによっては requestAnimationFrame の実装も異なるようです。 (詳しくはこちらをご覧ください)
Don't get into despair though! We have multiple solutions for this. We can:
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 中国語 Web サイトの他の関連記事を参照してください。