2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

data-*(カスタムデータ属性)と、SCSSを組み合わせた、メニューバー作成

Last updated at Posted at 2019-06-23

はじめに

スマホ用のメニューバーの作成です。
ボタンを押すと、メニューが現れる、何処にでも良くありそうなものです。

それを勉強の為に、0からSCSSで作ってみたのですが、上手く行かない部分があったため、解決策をメモ書きです。

メニューバー(改良前)

非常にテキトーなMenubarです。実際の動作はこちら。
「■」を押すと、メニューバーが表示されます。

一応ソースコードも載せて置きます。

html
<nav>
  <div class="menubar">
    <div>Logo</div>
    <label class="viewbutton" for="nav-visible"></label>
  </div>
  <input class="open" type="checkbox" id="nav-visible">
  <ul>
    <li>Menu1</li>
    <li>Menu2</li>
    <li>Menu3</li>
    <li>Menu4</li>
  </ul>
</nav>
scss
nav {
  display: flex;
  flex-direction: column;
}

.menubar {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  height: 3rem;
  line-height: 3rem;
  background-color: aqua;
  .viewbutton {
    display: block;
    font-size: 2rem;
    height: 3rem;
    width: 3rem;
    text-align: center;
  }
}

.open {
  display: none;
}

ul {
  display: flex;
  flex-direction: column;
  $li_height: 2rem;
  background-color: red;
  margin: 0;
  height: 0;
  overflow-y: hidden;
  li {
    height: $li_height;
    line-height: $li_height;
    list-style: none;
  }
  .open:checked ~ & {
     height: $li_height * 4;
  }
}

このサンプルのポイントは、下記のコードの部分です。

問題点
  .open:checked ~ & {
     height: $li_height * 4;
  }

Menu部分(Menu1~Menu4が表示される部分)の高さは、ul要素で決めています。
これはli要素の個数を、scssに直接書き込まなければならないので、決して良いコードとは思えませんでした。
出来る限り、依存性は無くしたい所です。

li要素でheightを決める

li要素でheightを決めれば良いのでは?、というツッコミがあるかと思います。
実際に作ってみました。transition部分の細かい動作(メニューの非表示時に、文字が潰れた様な表現になる)には気になりますが、これでも良い様には思います。

最大のメリットは、li要素を幾ら増やしても、scssの修正が不要である事だと思います。

ul部分
ul {
  display: flex;
  flex-direction: column;
  background-color: red;
  margin: 0;
  li {
    $li_height: 2rem;
    height: 0;
    line-height: 0;
    list-style: none;
    overflow-y: hidden;
    .open:checked ~ & {
      height: $li_height;
      line-height: $li_height;
    }
    transition: height, 0.5s;
  }
}

ですが、諸事情からどうしても親要素でheightを決めたかった、と言うことです。
メニューの非表示時に、文字が潰れた様な表現になるのも気になりますし……。

javascriptを使えば解決するのでしょうが、ここではそれ以外の解決策を探ってみる事にしました。

解決案

data-*(カスタムデータ属性)をul属性につける事で、liの個数をscssに伝える様にしました。
html上に個数を指定しないとならないのは面倒ですが、scssに直接個数を書くよりはマシな様な気がします。

修正後html(一部抜粋)
  <ul data-items="5">
    <li>Menu1</li>
    <li>Menu2</li>
    <li>Menu3</li>
    <li>Menu4</li>
    <li>Menu5</li>
  </ul>
修正後scss(一部抜粋)
ul {
  display: flex;
  flex-direction: column;
  $li_height: 2rem;
  background-color: red;
  margin: 0;
  height: 0;
  overflow-y: hidden;
  li {
    height: $li_height;
    line-height: $li_height;
    list-style: none;
  }

  @for $i from 1 through 12 {
    .open:checked ~ &[data-items="#{$i}"] {
      height: $li_height * $i;
    }
  }

  transition: height 0.5s;
}

@forの部分がポイントです。
このコードによって、下記コードを出力します。

forによるコード出力
.open:checked ~ &[data-items="1"] {
  height: $li_height * 1;
}
.open:checked ~ &[data-items="2"] {
  height: $li_height * 2;
}
.open:checked ~ &[data-items="3"] {
  height: $li_height * 3;
}

// 以下省略

そして、data-itemsが一致した部分で、heightを決めています。
このコードでは、data-items="5"としたので、height: $li_height * 5となります。
要するに、5行分のheightを設定する、という事ですね。

最後に

素直にjavascript使おうよ、というツッコミはあると思います。
全く持ってその通りです。

ただ、出来る限りデザインはcssで完結させたいというのが、私の考えなんですよね……。
私はプロのhtmlデザイナーでも無ければ、Webプログラマーでも無い、タダの素人なので、この考えが正しいかどうかは解りませんが……。

今回は、scssでこの様なコードを書けば良いのでは、と言うことで記事を書いてみました。
もしかたら、他に良い方法があるのかも知れませんが、私の頭ではこれが精一杯です。

おまけ

css4からは:has疑似要素と言うのがあるそうで、これを使えば、子要素の条件に基づいて、親要素を読む事が可能だとか。
これを使えば、data-itemsを使わなくともいけそうな気がするのですが、残念ながら、まだどのBrowserも未サポートとの事。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?