##100個の要素の順番を、21個のセレクタで特定して変化をつけたcodepenのデモ
See the Pen Use nth-child as CSS variable by keisuke Takahashi (@ksksoft) on CodePen.
- - - ##要素の順番を取得するならcounterでは? 要素の順番はCSSの` counter` で取得できます。カスタマイズしてリストマーカー的に使ったりするアレです。 しかし、` counter` で取得した内容はテキストなので、擬似要素に出力はできても、variableで` calc()` できません。つまり、計算には使えない残念さんです。 - - - ##要素数が少なければ、順番毎に処理を書いてしまえ 3つの要素に順番に変化をつけたい場合、nth-child()を3つ書けばいいです。 <ul>
<li>one</li>
<li>two</li>
<li>three</li>
</ul>
li {
background-color: pink;
list-style: none;
width: 10em;
animation: roll infinite 10s 0s both;
}
/* 開始から3秒は停止、2秒使って1回転、その後停止させるkeyframes */
@keyframes roll {
0%,
30% {
transform: rotate(0turn);
}
50%,
100% {
transform: rotate(1turn);
}
}
/* 同じkeyframesを使い、要素の順番に応じてanimation-delayだけを変える */
li:nht-child(1) { animation-delay: 0s;}
li:nht-child(2) { animation-delay: 1s;}
li:nht-child(3) { animation-delay: 2s;}
###keyframesは使い回す
同じ動きなら同じ keyframes
を使う、というのがCSS animation
のコツです。 keyframes
の%を要素の順番で計算してずらす、なんてことはやらないように。
まず keyframes
を要素の数だけ書くのは大変ですし、 animation
の動きを変えたくなった時に、全ての keyframes
の%を調整するハメになります。
##でも、要素数が100個ならどうする?
:nth-child(1){}
から :nth-child(100){}
まで書けばできます。
できますが、 :nth-child(An + B){}
のようにn変数を使って記述したいところです。
###要素の順番を特定する機能を切り離す
codepenで1から100までを特定した部分を抜粋します(ここがポイントです)。
/* Get element order */
li:nth-child(10n + 0) { --digit-1: 0; }
li:nth-child(10n + 1) { --digit-1: 1; }
li:nth-child(10n + 2) { --digit-1: 2; }
li:nth-child(10n + 3) { --digit-1: 3; }
li:nth-child(10n + 4) { --digit-1: 4; }
li:nth-child(10n + 5) { --digit-1: 5; }
li:nth-child(10n + 6) { --digit-1: 6; }
li:nth-child(10n + 7) { --digit-1: 7; }
li:nth-child(10n + 8) { --digit-1: 8; }
li:nth-child(10n + 9) { --digit-1: 9; }
li:nth-child(-n + 10) { --digit-2: 0; }
li:nth-child(n + 10):nth-child(-n + 20) { --digit-2: 10; }
li:nth-child(n + 20):nth-child(-n + 30) { --digit-2: 20; }
li:nth-child(n + 30):nth-child(-n + 40) { --digit-2: 30; }
li:nth-child(n + 40):nth-child(-n + 50) { --digit-2: 40; }
li:nth-child(n + 50):nth-child(-n + 60) { --digit-2: 50; }
li:nth-child(n + 60):nth-child(-n + 70) { --digit-2: 60; }
li:nth-child(n + 70):nth-child(-n + 80) { --digit-2: 70; }
li:nth-child(n + 80):nth-child(-n + 90) { --digit-2: 80; }
li:nth-child(n + 90):nth-child(-n + 100) { --digit-2: 90; }
li:nth-child(n + 100):nth-child(-n + 110) { --digit-2: 100; }
途中の改行を挟んで、前半と後半に分けて説明します。
###【前半】要素の順番の1の位を特定する
前半の10個のセレクタで、要素の順番の1の位を特定します。
:nth-child(An){}
は、「A個ごとに〜」という意味になります。順番は、10進数で取得したいので、Aに10を入れます。
:nth-child(10n + B){}
とすれば、「10個ごとにBを足す」というセレクタになります。
例えば、 :nth-child(10n + 1){}
にマッチする要素の順番は、
n | 代入して計算 | 結果 |
---|---|---|
0 | (10 * 0 + 1) | 1 |
1 | (10 * 1 + 1) | 11 |
2 | (10 * 2 + 1) | 21 |
3 | (10 * 3 + 1) | 31 |
--- | --- | --- |
9 | (10 * 9 + 1) | 91 |
となり、要素の順番の1の位が全て1になる、という仕組みです。
こうして取得した1の位を、--digit-1
と定義したvariableの値として代入できます。
###【後半】要素の順番の10の位を特定する
後半で、要素の順番の10の位を特定します。ここで使用するnth-child
は2種類あります。
セレクタ | 意味 |
---|---|
:nth-child(-n + C) | C以下にマッチ |
:nth-child(n + D):nth-child(-n + C) | C〜Dの範囲にマッチ |
したがって、:nth-child(-n + 10){}
は、10以下の要素にマッチします。
1から10番目の要素では、10の位として--digit-2
と定義したvariableが、0になります。
次の :nth-child(n + 10):nth-child(-n + 20) {}
は、10〜20の要素にマッチして、--digit-2
に10の位として 10 が代入されます。
ちなみに、10番目の要素が、直前のセレクタと重複してますが、:nth-child
が2回使われているので、その分、セレクタの詳細度が上がり、こちらが優先されます。
以下、20〜29には20が、30〜39には30が代入されていき、10の位が99まで特定されます。各セレクタの数字を10刻みにしているのは、可読性をあげて理解しやすくするためです。
最後のセレクタは100番目の要素になり、10の位は100になります。
ここを、3桁目にして、--digit-3
とするのが正しいでしょうが、今回は1から100までを特定するということで省略しました。
###var()に要素の順番から計算した変数を使えば、記述は1回で済む
再び、codepenから抜粋します。
li {
/* ここまでは省略。以下が肝要。 */
--digit-1: 0;
--digit-2: 0;
--h: calc(var(--digit-1) + var(--digit-2));
--delay: calc( (var(--digit-1) + var(--digit-2)) * 0.1s);
background-color: hsla(var(--h), 100%, 60%, 0.9);
animation: roll infinite 10s var(--delay) both;
}
念の為、--digit-1
と--digit-2
に0を代入しています。
--h
が要素の「順番」です。--digit-1
と--digit-2
をcalc()
で足して--h
に代入しています。
--delay
は その順番に0.1秒を掛けた値です。もちろん、直前に算出した--h
に0.1秒を掛けても良いです。
###要素順に応じて変化させる
codepenのデモでは、背景色とアニメーションの開始するタイミングを変化させてみました。
background-color
のhsla()
の h
の部分に、var(--h)
を使い、色相を変化させます。
animation
の指定では、1つ目の秒数がanimation-duration
になり、2つ目の秒数が animation-delay
になる決まりなので、2つ目にvar(--delay)
を指定します。
これで、要素数が未定でも、変化がつけ易くなります。
##記述量は減らせたか?
要素の順番毎にセレクタと処理を書いて、変化を実装する場合、要素が多くなると、数に比例して記述量が増えてしまいます。
###順番毎に処理を書く場合
要素数 | セレクタの数 | varの数 |
---|---|---|
10 | 10 | --- |
100 | 100 | --- |
1000 | 1000 | --- |
###順番を特定する部分を分離した場合
要素数 | セレクタの数 | varの数 |
---|---|---|
10 | 11 | 1 |
100 | 21 | 2 |
1000 | 41 | 3 |
特定したい要素数が増えるほど、恩恵にあずかれる、という主張です。
###処理は一回だけ書けば良い
特定した順番を変数として利用すれば、一回だけ処理を書けば良いことになります。要素の順番毎にセレクタと処理を書く必要はありません。記述もスッキリします。
##おわりに
世界でなら500人、日本でも30人くらいに、この投稿やcodepenが刺さると良いなあ、と思います。
Qiitaへの投稿は初です。無作法があったらすいません。
###CSSメタ言語を使えばイイじゃん、と思ったあなたへ
LessとかSassとかでmixinとかforとかするのかもしれませんが、私はメタ言語は使わないので、パスします。
1000個のセレクタを簡単に書けそうですが、コンパイル後の行数は増えちゃう気がしますが、どうなんでしょう?
CSSメタ言語での記述がお分かりになる方は、コメントやご自身の投稿で、その方法を補足してください。
###javascriptを使えばイイじゃん、と思ったあなたへ
CSSだけでやるのが面白いし、好きなんだよ、という主張なんです。