【CSS講座第3回】疑似要素::before ::afterを使って+アイコンを作る

アコーディオンで開閉するときに印として+マークを設置して、 クリックorタップをすると-マークになるものってよく見かけませんか?

あれは文字の+でもなく、画像でもなくCSSの疑似要素という機能を使って作ることが出来ます。

Web制作界隈ではもう常識の作り方になっているので、 まだ覚えてない人はこれを機に覚えて疑似要素にも慣れてみましょう。

疑似要素の使い方

まず疑似要素についてちょっとお勉強しましょう。

疑似要素とはHTMLを使わないでCSSのみで要素を追加して装飾を施すことができるので ベースのCSSがあれば::beforeと::afterを使えば擬似的に3つのHTMLが存在するような感じになり それぞれにCSSを適用できるため様々なデザインを作ることが出来ます。

例えば代表的なものはスマートフォンによくありがちなハンバーガーメニューです。

ハンバーガーメニューとはいわゆる「≡」みたいなボタンのことですね。

これを押すとバッテンにアニメーションしながらメニューが開くといった感じです。

もちろんspanタグを3つ使っても作ることができるのですが、 疑似要素を使えばspan1つで作ることが可能になります。

HTMLというのは装飾をするものではないので、 装飾のために無駄に空のspanを追加することはナンセンスです。

意外と面倒くさいからってspan3つとか空のDIVでやってしまっている人が多いんじゃないでしょうか?

疑似要素そのものについてはここでは説明しませんので、 詳しく知りたくなった人は各自ググってください。

それでは疑似要素の使い方を説明します。

まずはHTML側

<div class="parent">
  <p>押すとプラスになります</p>
</div>

はい、これだけです。

後にdivかpタグをクリックするとマイナスからプラスになるアニメーションを作るので 一旦HTMLはこれでいきましょう。

このHTMをベースに疑似要素を使えるようにしていきます。

説明は後からしますのでまずはCSSを見てください。

.parent {
  width:300px;
  height:50px;
  background-color:#ccf;

  position:relative;
}

.parent p {
  width:100%;
  height:50px;
  line-height:50px;
  font-size:18px;
  text-indent:10px;
}

.parent::before, .parent::after {
  content:'';
  position:absolute;
  right:10px;
  top:10px;
  width:20px;
  height:1px;
  background-color:#f00;
}

まずparentが親になるのでサイズとわかりやすいように背景に色をつけて居ます。 疑似要素を便利に扱うキモとしては「position」のプロパティが重要になります。

ようは起点作りですね。

疑似要素は基本的に「position:absolute」を多用することが多いため、 親となるボックスを基準にしておかないと全然関係ない場所に飛んでしまうのを避けるためでもあります。

次に::beforeと::afterについている「content」というプロパティ。

これが擬似要素を使うのに必須なのでかならずつけるようにしましょう。

シングルクォーテーションが空ですが、ここには文字を入れることができるのですが 今回は文字を使わないので空にしています。

そしてposition:absoluteを指定してるので右と上のプロパティを0にして右上寄せにしています。

大きさは横幅だけ20pxにして高さ1pxの背景赤という感じで 赤色の線が表示されるというのがわかっていただけるかと思います。

現在beforeとafterをまとめて設定しているため重なって表示されている状態なので いわゆる赤い「-」が表示されているだけになります。

プラスマークの作り方

現状だとマイナスマークになっているので、beforeとafterをそれぞれ違うCSSを適用して プラスマークを作ってみましょう。

やり方を先に言っておくと、beforeかafterのどちらかを90度回転させるだけですね。

それでは作ってみましょう。

.parent {
  /* 1つ前と変わらないので表記省略 */
}

.parent p {
  /* 1つ前と変わらないので表記省略 */
}

.parent::before, .parent::after {
  /* 1つ前と変わらないので表記省略 */
}

.parent::before {
  transform:rotate(90deg);
}

「.parent::before」が増えただけですね。

今回は重なりとかは気にしなくていいので私はbeforeを選びましたが、afterでも大丈夫です。

やってることは「transform:rotate」を使って90度回転させているだけですね。

角度数値の指定はdegreeの省略でdegとなっているのに気をつけてください。

回転のデフォルト起点は中心部になっているので 今回回転の起点を設定する必要はないです。

もし左上を軸として回転させたい場合とかは「transform-origin」プロパティを使うといいでしょう。

これで赤いプラスマークが出来上がりました。

プラスマークからマイナスマークへのアニメ

最後にCSSの枠からちょっと飛び出してしまうのですが 余談としてHTMLをクリックしたらプラスマークからマイナスマークにアニメーションするものを作ってみます。

アニメーションの仕方がよくわからない人は

【CSS講座第2回】absoluteによる中央配置の方法」を参照してください。

.parent {
  /* 1つ前と変わらないので表記省略 */
}

.parent p {
  /* 1つ前と変わらないので表記省略 */
}

.parent::before, .parent::after {
  content:'';
  position:absolute;
  right:10px;
  width:20px;
  height:1px;
  background-color:#f00;

  /* ボックスの縦の位置を中央に持ってきたいので垂直位置を調整 */
  top:50%;

}

.parent::before {
  transition: transform 0.5s linear;
  transform:translateY(-50%) rotate(90deg);
}

.open::before {
  transform:translateY(-50%) rotate(0deg) !important;
}

これだけです。

変わったのはアニメさせたいのでtransition:transformを指定しました。

そして縦の位置が気持ち悪いのでtop:50%にしてtranslateY(-50%)で 垂直の位置を中央にしています。

次に+マークにするために別のクラス(.open)が追加された時、 角度を0にすれば-マークになってくれます。

要らないかもしれませんが、.openクラスがついているときは -マークにしたいので一応importantをつけて優先度を高めています。

transformをまとめて設定する方法

transformは要素ごとに区切るのはカンマではなく空白になっているのに気をつけてください。

変化後も変化前も「translateY(50%)」を両方記載しているのは おそらくCSSの仕様上、個別に設定できないはずなので変化がなくても一緒に記述しています。

これでparentクラスを持つタグにopenクラスを付与すると+マークになってくれます。

クリックするとopenクラスを付与するJavaScript

openクラスを付与するやり方はjQueryを使っても大丈夫ですが、 私は脱jQueryしてしまったのでVanillaなJavaScriptで記載します。

// クリックするタグを取得
const btn = document.getElementsByClassName("parent")[0];

// クリックしたらクラスを付与する(openクラスがあった場合は削除、なければ付与)
btn.addEventListener('mouseup', e => {
  if(btn.classList.contains("open")) {
    btn.classList.remove("open");
  } else {
    btn.classList.add("open");
  }
});

これでクリックすると+と-が交互に変化するアニメーションが完成しました。

後はopenのついた親がいると子の高さが変化するようなCSSをかけば アコーディオンボックスを作成することが可能になります。

Codepenは便利

オンライン上でHTMLとCSSとJavaScriptをリアルタイムで反映してくれる

CodePen

を使うと当サイトのコードを試しながら確認できます。

サイトにも埋め込めるそうなので、そのうち埋め込んで見ようかなと思います。

それでは第3回CSS講座を終了いたします。