2022年9月29日

スタイルとクラス

スタイルとクラスを扱うためのJavaScriptの方法に入る前に、重要なルールがあります。当然のことかもしれませんが、言及しておく必要があります。

要素をスタイルする方法は一般的に2つあります

  1. CSSでクラスを作成して追加する: <div class="...">
  2. プロパティを直接styleに書き込む: <div style="...">

JavaScriptはクラスとstyleプロパティの両方を変更できます。

常にstyleよりもCSSクラスを優先する必要があります。後者はクラスで「処理できない」場合にのみ使用する必要があります。

たとえば、要素の座標を動的に計算し、JavaScriptから設定したい場合は、styleが許容されます。例:

let top = /* complex calculations */;
let left = /* complex calculations */;

elem.style.left = left; // e.g '123px', calculated at run-time
elem.style.top = top; // e.g '456px'

テキストを赤くしたり、背景アイコンを追加したりするなどの他のケースでは、それをCSSで記述してからクラスを追加します(JavaScriptで実行できます)。その方が柔軟性があり、サポートが簡単です。

className と classList

クラスの変更は、スクリプトで最もよく使用されるアクションの1つです。

昔、JavaScriptには制限がありました。"class"のような予約語はオブジェクトプロパティにはできませんでした。その制限は現在はありませんが、当時はelem.classのように"class"プロパティを持つことは不可能でした。

そのため、クラスについては、同様に見えるプロパティ"className"が導入されました。elem.className"class"属性に対応します。

例えば:

<body class="main page">
  <script>
    alert(document.body.className); // main page
  </script>
</body>

elem.classNameに何かを代入すると、クラスの文字列全体が置き換えられます。必要な場合もありますが、単一のクラスを追加/削除したい場合もよくあります。

そのためには別のプロパティがあります: elem.classList

elem.classListは、単一のクラスをadd/remove/toggleするためのメソッドを持つ特別なオブジェクトです。

例えば:

<body class="main page">
  <script>
    // add a class
    document.body.classList.add('article');

    alert(document.body.className); // main page article
  </script>
</body>

そのため、classNameを使用してクラスの文字列全体を操作することも、classListを使用して個々のクラスを操作することもできます。どちらを選択するかは、ニーズによって異なります。

classListのメソッド

  • elem.classList.add/remove("class") – クラスを追加/削除します。
  • elem.classList.toggle("class") – クラスが存在しない場合は追加し、存在する場合は削除します。
  • elem.classList.contains("class") – 指定されたクラスを確認し、true/falseを返します。

また、classListはiterableなので、次のようにfor..ofですべてのクラスをリストできます。

<body class="main page">
  <script>
    for (let name of document.body.classList) {
      alert(name); // main, and then page
    }
  </script>
</body>

要素のスタイル

プロパティelem.styleは、"style"属性に書き込まれた内容に対応するオブジェクトです。elem.style.width="100px"を設定することは、属性styleに文字列width:100pxがあるのと同じように機能します。

複数語のプロパティにはキャメルケースが使用されます。

background-color  => elem.style.backgroundColor
z-index           => elem.style.zIndex
border-left-width => elem.style.borderLeftWidth

例えば:

document.body.style.backgroundColor = prompt('background color?', 'green');
プレフィックス付きプロパティ

-moz-border-radius-webkit-border-radiusのようなブラウザプレフィックス付きプロパティも同じルールに従います。ダッシュはアッパーケースを意味します。

例えば:

button.style.MozBorderRadius = '5px';
button.style.WebkitBorderRadius = '5px';

スタイルプロパティのリセット

スタイルプロパティを割り当てて、後で削除したい場合があります。

たとえば、要素を非表示にするには、elem.style.display = "none"を設定できます。

その後、style.displayが設定されていないかのように削除したい場合があります。delete elem.style.displayの代わりに、空の文字列を代入する必要があります: elem.style.display = ""

// if we run this code, the <body> will blink
document.body.style.display = "none"; // hide

setTimeout(() => document.body.style.display = "", 1000); // back to normal

style.displayを空の文字列に設定すると、ブラウザはCSSクラスとその組み込みスタイルを、そのようなstyle.displayプロパティがまったくないかのように通常どおりに適用します。

また、それを行うための特別なメソッドelem.style.removeProperty('スタイルプロパティ')もあります。したがって、次のようにプロパティを削除できます。

document.body.style.background = 'red'; //set background to red

setTimeout(() => document.body.style.removeProperty('background'), 1000); // remove background after 1 second
style.cssTextによる完全な書き換え

通常、個々のスタイルプロパティを割り当てるにはstyle.*を使用します。div.style="color: red; width: 100px"のように完全なスタイルを設定することはできません。なぜならdiv.styleはオブジェクトであり、読み取り専用だからです。

完全なスタイルを文字列として設定するには、特別なプロパティstyle.cssTextがあります。

<div id="div">Button</div>

<script>
  // we can set special style flags like "important" here
  div.style.cssText=`color: red !important;
    background-color: yellow;
    width: 100px;
    text-align: center;
  `;

  alert(div.style.cssText);
</script>

このような代入は既存のすべてのスタイルを削除するため、このプロパティはめったに使用されません。それは追加するのではなく、それらを置き換えます。必要なものを誤って削除する可能性があります。ただし、既存のスタイルを削除しないことがわかっている場合は、新しい要素に安全に使用できます。

同じことは、属性を設定することによっても実現できます: div.setAttribute('style', 'color: red...')

単位に注意

値にはCSS単位を追加することを忘れないでください。

たとえば、elem.style.top10に設定するのではなく、10pxに設定する必要があります。そうしないと機能しません。

<body>
  <script>
    // doesn't work!
    document.body.style.margin = 20;
    alert(document.body.style.margin); // '' (empty string, the assignment is ignored)

    // now add the CSS unit (px) - and it works
    document.body.style.margin = '20px';
    alert(document.body.style.margin); // 20px

    alert(document.body.style.marginTop); // 20px
    alert(document.body.style.marginLeft); // 20px
  </script>
</body>

注意してください: ブラウザは最後の行でプロパティstyle.marginを「アンパック」し、そこからstyle.marginLeftstyle.marginTopを推測します。

計算されたスタイル: getComputedStyle

スタイルを変更するのは簡単です。しかし、それを読み取るにはどうすればよいでしょうか?

たとえば、要素のサイズ、マージン、色を知りたいとします。どうすればそれを行うことができますか?

styleプロパティは、CSSカスケードなしで、"style"属性の値のみを操作します。

したがって、elem.styleを使用してCSSクラスから取得したものを読み取ることはできません。

たとえば、ここではstyleはマージンを認識しません。

<head>
  <style> body { color: red; margin: 5px } </style>
</head>
<body>

  The red text
  <script>
    alert(document.body.style.color); // empty
    alert(document.body.style.marginTop); // empty
  </script>
</body>

…しかし、たとえば、マージンを20px増やしたい場合はどうでしょうか?現在の値が必要になります。

そのためには別のメソッドがあります: getComputedStyle

構文は次のとおりです

getComputedStyle(element, [pseudo])
element
値を読み取る要素。
pseudo
必要な場合は、擬似要素(たとえば::before)。空の文字列または引数がない場合は、要素自体を意味します。

結果は、elem.styleのようなスタイルのオブジェクトですが、すべてのCSSクラスを考慮に入れています。

例えば:

<head>
  <style> body { color: red; margin: 5px } </style>
</head>
<body>

  <script>
    let computedStyle = getComputedStyle(document.body);

    // now we can read the margin and the color from it

    alert( computedStyle.marginTop ); // 5px
    alert( computedStyle.color ); // rgb(255, 0, 0)
  </script>

</body>
計算された値と解決された値

CSSには2つの概念があります。

  1. 計算されたスタイル値は、すべてのCSSルールとCSS継承が適用された後の、CSSカスケードの結果としての値です。height:1emまたはfont-size:125%のようになります。
  2. 解決されたスタイル値は、最終的に要素に適用される値です。1em125%のような値は相対的です。ブラウザは計算された値を取得し、すべての単位を固定および絶対にします。例: height:20pxまたはfont-size:16px。幾何学的プロパティの場合、解決された値はwidth:50.5pxのような浮動小数点を持つ場合があります。

昔、getComputedStyleは計算された値を取得するために作成されましたが、解決された値の方がはるかに便利であることがわかり、標準が変更されました。

したがって、今日では、getComputedStyleは実際にはプロパティの解決された値を返し、通常は幾何学の場合はpxで返します。

getComputedStyleには完全なプロパティ名が必要です

paddingLeftmarginTopborderTopWidthのように、必要な正確なプロパティを常に要求する必要があります。そうしないと、正しい結果が保証されません。

たとえば、プロパティpaddingLeft/paddingTopがある場合、getComputedStyle(elem).paddingで何を取得する必要があるでしょうか?何も取得しないか、既知のパディングから「生成された」値を取得するでしょうか?ここには標準ルールはありません。

:visitedリンクに適用されたスタイルは非表示になっています!

訪問済みのリンクは、:visited CSS擬似クラスを使用して色付けされる場合があります。

ただし、getComputedStyleはその色へのアクセスを提供しません。そうしないと、任意のページが、ページ上にリンクを作成してスタイルを確認することにより、ユーザーがリンクにアクセスしたかどうかを知ることができてしまうためです。

JavaScriptは、:visitedによって適用されたスタイルを確認できない場合があります。また、CSSには、:visitedで幾何学的な変更を伴うスタイルを適用することを禁じる制限があります。これは、悪意のあるページがリンクが訪問されたかどうかをテストし、したがってプライバシーを侵害するための抜け道がないことを保証するためです。

まとめ

クラスを管理するには、2つのDOMプロパティがあります

  • className – 文字列値。クラスのセット全体を管理するのに適しています。
  • classListadd/remove/toggle/containsメソッドを備えたオブジェクト。個々のクラスに適しています。

スタイルを変更するには

  • styleプロパティは、キャメルケースのスタイルを持つオブジェクトです。読み書きすることは、"style"属性の個々のプロパティを変更することと同じ意味を持ちます。importantやその他のまれなものを適用する方法については、MDNにメソッドのリストがあります。

  • style.cssTextプロパティは、"style"属性全体、つまりスタイルの完全な文字列に対応します。

解決されたスタイルを読み取るには(すべてのクラスを考慮し、すべてのCSSが適用され、最終値が計算された後)

  • getComputedStyle(elem, [pseudo]) は、それらを含むスタイルライクなオブジェクトを返します。読み取り専用です。

タスク

重要度: 5

指定されたコンテンツを持つ <div class="notification"> という通知を作成する関数 showNotification(options) を記述してください。通知は1.5秒後に自動的に消えるようにする必要があります。

オプションは以下の通りです

// shows an element with the text "Hello" near the right-top of the window
showNotification({
  top: 10, // 10px from the top of the window (by default 0px)
  right: 10, // 10px from the right edge of the window (by default 0px)
  html: "Hello!", // the HTML of notification
  className: "welcome" // an additional class for the div (optional)
});

新しいウィンドウでデモ

指定された上/右の座標に要素を表示するために、CSSポジショニングを使用してください。ソースドキュメントには必要なスタイルが用意されています。

タスクのサンドボックスを開く。

チュートリアルマップ

コメント

コメントする前にこれを読んでください…
  • 改善点についての提案がある場合は、コメントする代わりにGitHub issueを提出するか、プルリクエストを送ってください。
  • 記事の内容が理解できない場合は、詳しく説明してください。
  • コードを数語挿入するには、<code> タグを使用し、数行の場合は <pre> タグで囲み、10行を超える場合はサンドボックス(plnkrjsbincodepen…)を使用してください。