LoginSignup
4
0

More than 5 years have passed since last update.

いまさらCSS/SassでFizzBuzz〜数字も出力した

Last updated at Posted at 2016-11-30

はじめに

みんなご存知FizzBuzz (Wikipedia)を、CSSメタ言語が普及した現在だとどうなるのか今更やってみた。

条件

1から100までの数をプリントするプログラムを書け。ただし3の倍数のときは数の代わりに「Fizz」と、5の倍数のときは「Buzz」とプリントし、3と5両方の倍数の場合には「FizzBuzz」とプリントすること。
どうしてプログラマに・・・プログラムが書けないのか? - Jeff Atwood / 青木靖 訳

CSSはプログラムじゃないので直接HTMLを出力できないため、特別条件をプラスする。
・ HTMLは先に与えられてる
・ SEO、セマンティクス、アクセシリビティなどは無視する

HTMLは以下とする。

emmet
ul>li{i}*100 <!-- 数字付き -->

ul>li*100    <!-- 数字なし -->

CSSでFizzBuzz

CSSで行う場合、
1. 擬似クラス:nth-child(:nth-of-type)を使う
2. 先に表示されてる数字をどのように消すか
がポイントになる。

font-sizeでいらない数字を消す

emmet
ul>li{i}*100
css
// 3の倍数と5の倍数のフォントサイズを0にして見えなくする。
li:nth-child(3n),
li:nth-child(5n){ font-size: 0; }

// 擬似要素に文字を入れる。フォントサイズを戻せば見えるようになる。
// ::beforeと::afterに分けることで、公倍数のときは両方が表示される。
li:nth-child(3n)::before {content: "Fizz"; font-size: 1rem;}
li:nth-child(5n)::after  {content: "Buzz"; font-size: 1rem;}  

counter-incrementで数字も出力する

数字を出す方法を調べてるとCSSカウンタ counter-incrementというものを見つけた。
使い道はまだよくわからない…。

HTML(emmet)
ul>li*100
css
// 1)::beforeにカウンタセット
ul { counter-reset: num;} // カウンタリセット 
li { counter-increment: num;} // numカウンタを増やしていく
li::before { content:counter(num) ""; }// li::beforeにカウンタ数表示

// 3) 5の倍数の::beforeが残るので空contentにして消す。
// "Fizz"を出力する前に消さないと3*5の倍数の時に"Fizz"が消えるので注意。
li:nth-child(5n)::before  { content: "";}

// 2) 3の倍数の::before、5の倍数::afterにそれぞれ"Fizz"、"Buzz"を入れる。
// 3の倍数の::beforeは"Fizz"に上書きされるのでOK。
li:nth-child(3n)::before { content: "Fizz";}
li:nth-child(5n)::after  { content: "Buzz";}

記述順に依存してるのがとても怖い。

【未来】list-style-type: <string>が使える、ようになるかも

ul -> ol にしてビュレットの数字を3n,5n,3*5nの時にstringにすればできそう。
残念ながらまだ草案で実装ブラウザないみたい?

SassでFizzBuzz

メタ言語になると制御構文(if,each,while…)が使えるのでやれることが広がる。

よくある余剰で解く方法

スタイルシート感はゼロ。

scss
@for $i from 1 through 100 {
  // 条件分岐で表示するものを出し分ける
  @if $i % (3 * 5) == 0 {
    li:nth-child(#{$i})::before {content: "FizzBuzz";}
  }
  @else if $i % 3 == 0 {
    li:nth-child(#{$i})::before {content: "Fizz";}
  }
  @else if $i % 15== 0 {
    li:nth-child(#{$i})::after  {content: "Buzz";}
  }
  @else {
     li:nth-child(#{$i})::before {content: "#{$i}";}
  }
}

list(配列)使う方法

1〜15番目までの並びを繰り返していくので、配列にして取り出していく方法があると聞いた。
Sassでも配列と同じ機能(list,map)があるのでやってみた。

が、nth($list, $n)が0だとerorrが返ってくる仕様との戦いになってつらい。
拡張したところでスタイルシートはプログラム言語じゃない現実がつらい。

scss
// 3n,5n,3*5n番目には文字、それ以外は空欄のリストをセットする。
$list : "","","Fizz","","Buzz","Fizz","","","Fizz","Buzz","","Fizz","","","FizzBuzz";
scss
@for $i from 1 through 100 {
  // リストの長さ(15=3*5)
  $length: length($list);
  // contentに入れる値。先に書いておかないとスコープが働いてエラーになる★つらい★
  $content: "";

  // 余剰を使って割り当てる。
  // でも割り切れる(== 0)とエラーになるからその時だけ別の処理をする★つらい★

  // リストの長さ(3*5=15)で割り切れない時 → 配列の値をそのままいれる
  @if $i % $length != 0 {
    $content: nth($list, $i % $length);
  } 
  // 割り切れる時 → $length(=15番目)の値を入れる
  @else {
    $content: nth($list,$length);
  }

  // $contentが空欄""じゃないときはそのままリストの文字を表示させる
  @if $content != "" {
    li:nth-child(#{$i})::before {content: "#{$content}";}
  } 
  // $contentが空欄""の時は$iの数字を表示させる
  @else {
    li:nth-child(#{$i})::before {content: "#{$i}";}
  }
}

割り切ってもnthが0にならないように1を足すとちょっとスッキリした。

@for $i from 0 through 100 {
  $content: nth($list, $i % 15 + 1);
  @if $content != "" {
    li:nth-child(#{$i + 1})::before {content: "#{$content}";}
  } 
  @else {
    li:nth-child(#{$i + 1})::before {content: "#{$i + 1}";}
  }
}

さいごに

あんまり使わない機能を知る良い勉強になった。
listを使う方法は、パターンが決まってる値を無限に繰り返したい時があれば使えるかも?。
mixinを駆使する方法もあるようなので随時試してみたい。

4
0
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
4
0