JavaScriptを使って、CSSの疑似要素「before/after」を制御する ~animation-durationプロパティを例に~

はじめまして、さきがけデジタルの三浦です。
主にウェブページなどのコーディングを担当しております。

私のブログでは、開発で役に立ったコードやテクニックを紹介していきたいと思います!
趣味の話もしていきたいですね~。

◇  ◇

さて、今回は「CSS」と「JavaScript(JS)」についてのお話です。

ウェブページのコーディングでよく使うCSSの疑似要素「before/after」。
文字を挿入するだけではなく、線や図形、画像を表示したりなど、
複雑なデザインを構成するために欠かせないテクニックです。

コーダーのみなさんは、こんなことを思ったことはありませんか?

「疑似要素のCSSをJSで上書きできないか」

弊社サイトの構築時、アニメーションの細かい調整が必要となり、実装するために調べてみました。
結論から言うと「疑似要素に対してJSで直接変化を加えることはできない」ようです。

では、どうしたら良いのか。
さらに調べてたどり着いたのが「CSS変数を使う」方法でした。


■ 実現したいこと

(1)ページ内にタイトルを複数設置する
(2)タイトルに疑似要素を使って下線を引く
(3)下線が伸びるようなアニメーションを設定する
(4)タイトルそれぞれの文字数に応じて、アニメーションの秒数を変動させる

なぜアニメーションの秒数を変化させるのか。

すべて同じ秒数にしてしまうと、
文字数が少ないときは「ゆっくり」と、文字数が多いときには「速く」感じてしまうためです。
「ページ全体で均等なアニメーション」にするために、文字数によって秒数を調整することが必要でした。


★ まず、アニメーションを4秒に設定した場合

※「RunPen」ボタンを押して、動きを見てみてください。「Rerun」ボタンを押すと、もう一度再生できます。

アニメーションの秒数は「4秒」に設定しています。
文字が多い場合は、少し速く動いているように見えます。
3つのテキストを比べると、アニメーションにバラつきがあるように感じます。


★ JSを使ってアニメーションの秒数を制御した場合

一方、こちらはJSを使用して、アニメーションの秒数を制御しています。
文字数が増えても、動きが極端に速くなったりしません。
3つのテキストを比べると、動きに統一感が出ていると思います。


ここからは、完成したコードの紹介と解説をしていきます。

★ HTML
<div class="l-content">
  <div class="l-content__section">
   <h2 class="c-title">1234567891012345678910</h2>
  </div>
  <div class="l-content__section">
   <h2 class="c-title">12345678910</h2>
  </div>
  <div class="l-content__section">
   <h2 class="c-title">12345</h2>
  </div>
</div>
★ CSS
.l-content {
  display: grid;
  gap: 16px 0;
}
.c-title {
  display: inline-block;
  white-space: nowrap;
  position: relative;
}
.c-title::after {
  display: block;
  content: "";
  width: 100%;
  height: 1px;
  background-color: #000;
  transform: scaleX(0);
  transform-origin: center left;
  animation: bar var(--animeDuration) linear forwards;
  position: absolute;
  bottom: 1px;
  left: 0;
}
@keyframes bar {
  0%   { transform: scaleX(0); }
  100% { transform: scaleX(1); }
}

JavaScript
window.addEventListener("load", function() {
 const title = document.querySelectorAll(".c-title");
 const baseDuration  = 0.18;
 title.forEach(element => {
  let textCount = element.textContent.length;
  let animeDuration = baseDuration * textCount;
  element.style.setProperty("--animeDuration", `${animeDuration}s`);
 });
});

■ CSSの解説(要点のみ)

animation: bar var(--animeDuration) linear forwards;

アニメーションを指定しますが、JSで変化させたいプロパティの値は「CSS変数」で指定します。

■ JSの解説

window.addEventListener("load", function() { ~

ページの読み込み後に実行されるようにします。

const title = document.querySelectorAll(".c-title");

複数ある H2タグ「.c-title」を取得して、変数「title」に格納します。
※このときの返値は「NodeList」となります。

const baseDuration = 0.18;

アニメーションのベースとなる秒数を指定します。※ CSSのみのサンプルと合わせています

title.forEach(element => { ~

取得したタイトル要素が複数あるので、forEach文で処理を繰り返します。

let textCount = element.textContent.length;

タイトルの文字数を取得します。

let animeDuration = baseDuration * textCount;

アニメーションの秒数を計算します。今回は、ベースの秒数と文字数を掛け算しています。

element.style.setProperty("--animeDuration", ${animeDuration}s);

「setProperty関数」を使い、HTMLタグに直接「style」を設定します。
「CSS変数:animeDuration」の値が「●●秒(計算後)」となるように記載します。


JSがうまく実行されると、
H2タグにCSS変数と値が書き加えられます(検証ツールなどでご確認ください)。

<h2 class="c-title" style="--animeDuration: 22s;">1234567891012345678910</h2>
<h2 class="c-title" style="--animeDuration: 11s;">12345678910</h2>
<h2 class="c-title" style="--animeDuration: 2s;">12</h2>

H2タグに直接styleが書かれているので、
各CSS変数は上書きされることなく、要素それぞれに違う値が適用されます。

◇  ◇

いかがでしたでしょうか?

今回は「アニメーションの秒数」だけにフォーカスを当てましたが、
別のプロパティにしてみたり、複数組み合わせみたりすれば、さらにコーディングの幅が広がるかと思います。

「setProperty関数」にCSS変数を指定しましたが、通常のCSSプロパティも設定できるので、
疑似要素以外にも使い道はたくさんありそうですね。

きょうはここまで!
今後はコーディングの話のほか、ゆる~い事も書いていきたいと思います。


ではでは、またお会いしましょう ノシ

三浦真行さんのプロフィール画像
三浦真行

「スト6」にハマり、10年ぶりぐらいに格ゲー界に戻ってきました🎮

前職はさまざま。入社後はコーディングやWordPressでのサイト構築経験多数。筋トレで激ヤセしたことも。

こんな記事もあります

お問い合わせ

ウェブ制作や「ふるさと電報」に関するお問い合わせ、ご相談などお気軽にご連絡ください。お見積りは無料で作成いたします。