プログラミングの理解が遅すぎる初心者がJavaScript、Node.jsで投票型掲示板を作ろうとしてます

トップページでは記事の順番がごちゃごちゃなので、記事もくじをご覧いただければと思います。

投票ページの作成⑤候補者追加の動作を作成

前回は投票ページの候補者を追加するためのフォームを作りました。

次はフォームに名前と年齢を入力して追加ボタンを押すことで、投票ページの候補として追加されるようにします。

この辺についても以前に記事にしていますので、それを参照してさらっと終わらせたいと思います。

html-css-javascript.hatenadiary.com

前回作成したフォームは以下。

  <form id="addCandidateForm" action="#" method="post">
        <label for="candidateName">候補者名:</label>
        <input type="text" id="candidateName" name="candidateName" placeholder="候補者" required>
        <label for="candidateAge">候補者年齢:</label>
        <input type="number" id="candidateAge" name="candidateAge" min="0" max="150" placeholder="年齢" required>
        <button type="submit">追加</button>
    </form>

まずはJavaScriptで、追加ボタンをクリックしたら候補者名(candidateName)と候補者の年齢(candidateAge)を取得するようにします。

で。

重要なことを忘れていました。

候補者とか投票数とかを示す場所がありませんでした。

これを記しておかないと、JavaScriptで操作することができません。

なので以下を付け足します。

<ul id="candidateList"></ul>

これでJavaScriptのほうでIDで取得することができるようになりました。

名前と年齢の取得

JavaScriptのファイル名は「candidateAdd.js」とでもしておきましょう。

要素の取得

document.getElementById('addCandidateForm').addEventListener('submit', function(event) {
    event.preventDefault();

     const name = document.getElementById('candidateName').value.trim();
    const age = document.getElementById('candidateAge').value.trim(); 
 const candidateList = document.getElementById('candidateList');

    if (!name || !age) {
        alert('候補者名と年齢を入力してください。');
        return;
    }   
  console.log(name, age);
});

送信ボタンを押してフォームのイベント発生ですから、IDはaddCandidateForm、トリガーはsubmitですね。

querySelector('form')でもいいんですけど、今後フォームが2つ以上出てくるときとかもあるかもしれないので、フォームについてはIDタグをつけるようにしていきます。

名前、年齢のどちらかが空欄の場合はアラートが出るようにしておきました。

htmlのほうでもrequiredを付けておきましたが、念のため。

trim()もつけておきましたので、余計なスペースなどもなくなりました。

あとこれも念の為、nameとageに正しい値が代入されているか確認するためにconsole.log(name, age);も入れておきました。

次は取得した要素を配列に入れていきます。

配列を作成

普通の掲示板の場合は、書き込んだ内容を次々に足していくだけでしたのでappendChild()だけで済んだのですが、今回は投票数に応じて並べ方なども変えていく必要があります。

なので配列を作ります。

これで条件による並べ方などは制御することができます。

また、今回はadd.htmlから送信したデータを、vote.htmlという別のHTMLファイルで反映させることも必要になります。

しかし配列を作っただけだと、add.htmlから開くJavaScriptとvote.htmlから開くJavaScriptは別物ですので、配列は渡すことはできません。

じゃあ配列はどこかに保存して、どちらのHTMLファイルからもアクセスできるようにするしかありません。

「どこかってどこよ?」というと、「ブラウザ」です。

ブラウザに保存するところがあります。

ストレージの中にブラウザに割いている部分があるという理解でいいです。

localStorage

ここに保存できます。

しかしこのlocalStorage、なんと配列はそのままでは保存できません

できるのは文字列のみ。

なら配列も文字列に置き換えるしかありません。

なのでやることは以下の通り。

配列を文字列に置き換えてlocalStorageに保存→必要なときにlocalStorageから取り出して文字列を配列に戻す。

めんどくせえ。

では配列を文字列にするのですが、この文字列はJSON形式です。

つまりJSONの書き方に従います。

JSONの形式についてはこちら。

html-css-javascript.hatenadiary.com

では配列を作ります。

const candidates=[];

(実際はここで書くのではなく、絶対に用意すべき箱ですのでイベントリスナーより前(一番上)に書きます。

物理的にも、まず箱を用意してから中身を作るのと同じです。)

次に配列の要素の一つを作ります。

要素はオブジェクトになりますので、以下のような感じになります。

const newCandidate = {
    name: name,
    age: age,
    vote: 0,
};

ですね。

でもここでちょっと裏技というか省略できるところがあります。

キーと値が同じ場合、いちいち:で挟む必要がなく、その名前一つを書くだけで済みます。

name:name;

だったら

name

だけで済むことになります。

なので上の配列は

const newCandidate = {
    name,
    age,
    vote: 0,
};

にすることができます。

voteについては初期値が0なので省略できません。

んでから配列にフォームから送られてきた要素を次々足していくことになりますので、ここは配列の最後に要素を足すpushをつかいます。

それから配列の中身を取得してから、HTMLの

    に要素を入れるためにcreateElementを使います。

    listItemの中のtextにフォームで送信した内容を反映させて、これを配列の一番後ろに追加します。

    この辺、詳しくは以前のブログに記載しているので、そちらをご覧ください。

    html-css-javascript.hatenadiary.com

    candidates.push(newCandidate);
    
    const listItem = document.createElement('li');
    listItem.textContent = `名前: ${newCandidate.name}, 年齢: ${newCandidate.age}, 投票数: ${newCandidate.vote}`;
    candidateList.appendChild(listItem);
    

    ここまでをまとめます。

    const candidates=[];
    
    document.getElementById('addCandidateForm').addEventListener('submit', function(event) {
        event.preventDefault();
    
        const name = document.getElementById('candidateName').value.trim();
        const age = document.getElementById('candidateAge').value.trim();  
        const candidateList = document.getElementById('candidateList');
        if (!name || !age) {
            alert('input the name and age of candidates');
            return;
        }   
        console.log(name, age);
    
    const newCandidate = {
        name,
        age,
        vote: 0,
    };  
    candidates.push(newCandidate);
    
    const listItem = document.createElement('li');
    listItem.textContent = `名前: ${newCandidate.name}, 年齢: ${newCandidate.age}, 投票数: ${newCandidate.vote}`;
    candidateList.appendChild(listItem);
    });
    

    ここまでで配列とその中身を作りました。

    ほとんど以前に作った「JavaScript 掲示板を作る③書き込みを反映させる」と同じ内容ではありますが。

    次回からが重要で、今回つくった配列をローカルストレージに保存することで、別のHTMLファイルから読み込めるようにします。

投票ページの作成④候補者追加ページの作成

これまで、トップページ、投票ページを作成してきました。

で、投票ページには投票ボタンと投票結果を表示することにしたのですが、投票の候補者を追加できるようにする必要もあります。

今回はそのためのページを作成していくことにします。

候補者を追加するわけですので、普通に「add.html」というファイル名にでもしておきましょう。

送信する内容は、名前と年齢くらいでいいですかね。

投稿フォームにつきましては以前作成しましたので、そちらを参照していただければと思います。

html-css-javascript.hatenadiary.com

これを参考にして候補者とその年齢をデータとして送信するフォームを作成します。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>候補者追加</title>
</head>
<body>
    <form id="addCandidateForm" action="addCandidate" method="post">
        <label for="candidateName">候補者名:</label>
        <input type="text" id="candidateName" name="candidateName" placeholder="候補者" required>
        <label for="candidateAge">候補者年齢:</label>
        <input type="number" id="candidateAge" name="candidateAge" min="0" max="150" placeholder="年齢"  required>
        <button type="submit">追加</button>
    </form>
</body>
</html>

今までもそうしてきましたけど、英単語をつなぐときにはハイフンにして「candidate-age」のようにしてもいいんですけど、これをやるとJavaScriptで書くときに不便なので「candidateAge」のように、二つ目以降の単語の頭を大文字にするということで統一します。

ここまでで使ったハイフンもすべて書き直します。

ハイフンを消してから、次の単語の頭を大文字にするだけですから難しいものではないと思いますが、ちゃんとコピペで再現できるようにまとめの記事で全部記載しておきます。

あと候補者年齢のほうですが、maxとminを付けることでめちゃくちゃな数字を入れられないようにしました。

requiredは入力必須の項目であることを意味します。

無記入では送信できないようにしました。

で、できたのがこちら。

候補者追加フォーム1

当然このままじゃ不格好なのでCSSで揃えていくことにします。

まあそこまで難しいことをするわけでもなく、縦に並べるくらいでいいですね。

#addCandidateForm {
  display: flex;
  flex-direction: column;
  width: 300px;
  margin: 2rem auto;
  gap: 1rem;
}

これで以下のようになりました。

とりあえず形はこれでよさそうです。

次は送信した内容を「vote.html」の投票するところに反映させるようにします。

投票ページの作成③CSSの作成

前回、投票ページのHTMLを作りました。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>投票ページ</title>
</head>
<body>
<header><!-- 後ほどJavaScriptで共通ページを引っ張ってくる-->
    <h1>投票ページ</h1>
</header>
    <main class="vote-main">
<table> <!-- テーブル-->
  <thead><!-- 項目を書く行-->
    <tr><!-- 以下のものを一行に収める-->
      <th>順位</th><!-- 内容(値)-->
      <th>候補者</th>
      <th>投票数</th>
      <th>投票ボタン</th>
    </tr>
  </thead>
  <tbody id="candidate-list">
    <!-- ここに行が追加されていく-->
  </tbody>
</table>
    </main>
</body>
</html>

これを整理するためにCSSを書いていきます。

で、CSSを進めていく上で必要なクラスやIDなどを付け足していきます。

その際はコメントアウトで付け足した部分をメモしていきます。

HTMLはトップページと投票ページを分けましたが、CSSは今のところは特に分ける意味もないので、トップページを書いたものを使います。

body {
  font-size: 1rem;
  font-family: sans-serif;
  margin: 0;
  padding: 0;
}

header {
  position: relative;
  background-color: #fff;
  padding: 1rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

nav {
  position: absolute; /*ヘッダーを基準に書いていく*/
  display: none; /*最初はメニューが見えないようにしておく*/
  &.show{
    display: block; /* show クラスがついたら表示*/
  }
  top: 100%; /*ヘッダーの下に配置*/
  right: 0; /* 右端はヘッダーに合わせる*/


  ul {
    list-style: none;/* マーカーを消す*/
    padding: 0;      /* ul の内側の余白を消す*/
    
    li {
      margin-bottom: 0.5rem;

      a { /* リンクの表示の扱い*/
        display: block; /* li 全体をクリック可能に*/
        padding: 0.5rem 1rem; /* 内側の余白 */
       text-decoration: none; /* リンクの下線を消す*/
        color: #333; /* ダークグレー*/

        &:hover { /* ホバーしたときの扱い*/
          background-color: #f0f0f0;  /* ホバー時の色 */
        }
      }
    }
  }
}

まずmainのレイアウトや見た目を整えていきます。

main

トップページのmainとこんがらがってはいけないので、HTMLの方にクラスをつけて<main class="vote-main"><!-- 投票用のメイン-->にします。

これでCSSを書いていきます。

.vote-main {
  width: 100%;
  padding: 1rem;
  box-sizing: border-box;/*paddingなども含めて幅を計算する*/
}

次にtableですね。

table

テーブルに関してはCSSでもJavaScriptでもほぼ確実に制御することになるので、こっちにもクラスを付けておきます。

クラス名は<table class="vote-table"> <!-- テーブル-->でいいですね。

では書いていきます。

.vote-table {
  width: 100%;
  border-collapse: collapse; /*セルの境界線を重ねる*/
  margin-bottom: 1rem;

  th, td {
    border: 1px solid #ccc; /*薄いグレーの境界線*/
    padding: 0.5rem;
    text-align: left;
  }

  th {
    background-color: #f9f9f9; /*薄いグレーの背景色*/
  }
}

まあこんな感じです。

thは項目(見出し)のセルを表し、tdはデータ(値)のセルを表します。

現時点ではテーブルといっても一行だけで、データではなく項目だけを表示したいのでtdは要らないのですが、今後JavaScriptでデータを追加していくので、ここは記載したままにしておきます。

次の問題は項目ごとの横幅です。

すべてを均等にするのではなく、順位、候補者名、投票数、投票ボタン、という項目なので、それに適した長さにする必要があります。

まずはテーブルレイアウトをfixedにして固定します。

で、要素の一つ下(子)を制御するのでnth-childを使い、長さの割合を一つずつ決めていきます。

nthとはn-th、つまりn番目を意味します。

thの中の何番目をどれくらいの割合の長さにするかを決めるので、以下のように書きます。

  th {
    background-color: #f9f9f9; /*薄いグレーの背景色*/
    &:nth-child(1) {
      width: 10%; /*順位の幅*/
    }
    &:nth-child(2) {
      width: 45%; /*候補者の幅*/
    }

    &:nth-child(3) {
      width: 20%; /*投票数の幅*/
    }
    
    &:nth-child(4) {
      width: 25%; /*投票ボタンの幅*/
    }
  }

vote-tableのところでレイアウトをfixedにするのも忘れないように。

.vote-table {
  width: 100%;
  border-collapse: collapse; /*セルの境界線を重ねる*/
  margin-bottom: 1rem;
  table-layout: fixed; /*項目の横幅を固定する*/

これで以下のようなレイアウトになります。

投票結果の項目

実際の順位やら候補者やらを表示する箇所はJavaScriptで制御することになります。

投票ボタンを押したら数字を投票数を1増やし、それを順位順に並べる、といった制御です。

で、今思いついたんですけど、候補者を増やすフォームも必要ですね。

入力フォームに候補者名を書いて、送信ボタンを押せば候補者の名前が反映されてテーブルの行が増える、というような。

ただこれ、投票とは役割が違って「候補者を追加する」ことが目的ですから、このページに作るのは違うように思います。

なので候補者追加のページはまた別に作成します。

次回はそれを作っていくことにします。

投票ページの作成②HTMLの作成

前回言った通り、投票ページでは投票の項目を表すところと、投票の結果を表すところを作ります。

html-css-javascript.hatenadiary.com

投票結果はテーブルのような形になりますので、使うタグは<table>ですね。

<table> <!-- テーブル全体 -->
  <thead> <!-- 見出し(項目)の行 -->
    <tr> <!-- この中に1行分のセル(th)が収まる -->
      <th>順位</th>       <!-- 列の見出し -->
      <th>候補者</th>     <!-- 列の見出し -->
      <th>投票数</th>     <!-- 列の見出し -->
      <th>投票ボタン</th> <!-- 列の見出し -->
    </tr>
  </thead>

  <tbody id="candidate-list"> <!-- データ行が追加される場所 -->
    <!-- ここに JavaScript で <tr> が追加される -->
  </tbody>
</table>

コピペできるように全部を書くと以下のようになります。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>投票ページ</title>
</head>
<body>
<header><!-- 後ほどJavaScriptで共通ページを引っ張ってくる-->
    <h1>投票ページ</h1>
</header>
    <main>
<table> <!-- テーブル-->
  <thead><!-- 項目を書く行-->
    <tr><!-- 以下のものを一行に収める-->
      <th>順位</th><!-- 内容(値)-->
      <th>候補者</th>
      <th>投票数</th>
      <th>投票ボタン</th>
    </tr>
  </thead>
  <tbody id="candidate-list">
    <!-- ここに行が追加されていく-->
  </tbody>
</table>
    </main>
</body>
</html>

・・・あれ?

HTMLはこれで終わりですね。

またあっさりと終わってしまいました。

次はCSSに進みます。

投票ページの作成①方針を考える

前回まででトップページとそのヘッダーを作りました。

次はメインコンテンツである掲示板を作ります。

タイトルでもある投票型の掲示板です。

候補があって、候補の横に添えられている投票ボタンを押したら数値が増える、という感じで。

トップページでメインを「投票するところ」としておきましたけど、トップページにそんなものを作ってしまったら見づらくて仕方なくなると思うので、別のページにしようと思います。

とりあえずファイル名は「vote.html」にします。

ヘッダーはすべて共通にすることになりますので、ヘッダーもファイルを別につくってJavaScriptでそれを引っ張ってくることにします。

なのでここではメインである投票フォームを考えていこうと思います。

まずは構成。

構成の方針

今回も最低限のものだけを考えていくことにします。

上部に「順位」「候補」「投票数」「投票ボタン」という項目を作って、その下に候補を任意に増やしていき、行を追加していく感じにしようと思います。

投票フォーム

こんな感じです。

書き方の方針

まずはHTMLで構成を作ることになります。

まず項目の箇所と追加していく場所を確保します。

それらをCSSのdisplayをflexにして横並びのレイアウトを構成します。

投票ボタンを押したときや候補を追加するとき、あとは順位を順番にそろえるあたりはJavaScriptで行うことになります。

操作をしたあとの再描写もJavaScriptで行うことになりますね。

この場合、再描写ってのはリロードじゃなく、DOM操作による部分的な変化を描写するという意味で使っています。

おわりに

短いですが、今回はここまでにしておきます。

この記事の方針に従ってページを作成していきます。

次はまずHTMLを作成します。

ハンバーガーメニューの作成③JavaScript※追記あり

さて、ここまででハンバーガーメニューのHTMLとCSSを書いてきました。

以下の通りです。

HTML

<!doctype html>
<html lang="ja">

<head>
    <meta charset="utf-8" />

    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>投票ボタン付き 横棒グラフ</title>
    <link rel="stylesheet" href="style.css">
    <link rel="stylesheet"
        href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200&icon_names=menu" />
</head>

<body>
    <header>

        <button id="menu-btn">
            <span class="material-symbols-outlined">menu</span>
        </button>
        <nav>
            <ul>
                <li><a href="#">ホーム</a></li>
                <li><a href="#">投票ページ</a></li>
                <li><a href="#">コメント</a></li>
                <li><a href="#">お問い合わせ</a></li>
            </ul>
        </nav>
    </header>
    <main>
        <div>
            投票をするところ
        </div>
    </main>
    <footer>
        <p>&copy; 2025 投票アプリ</p>
    </footer>
</body>

</html>

CSS

body {
  font-size: 1rem;
  font-family: sans-serif;
  margin: 0;
  padding: 0;
}

header {
  position: relative;
  background-color: #fff;
  padding: 1rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

nav {
  position: absolute; /*ヘッダーを基準に書いていく*/
  display: none; /*最初はメニューが見えないようにしておく*/
  top: 100%; /*ヘッダーの下に配置*/
  right: 0; /* 右端はヘッダーに合わせる*/

  ul {
    list-style: none;/* マーカーを消す*/
    padding: 0;      /* ul の内側の余白を消す*/
    
    li {
      margin-bottom: 0.5rem;

      a { /* リンクの表示の扱い*/
        display: block; /* li 全体をクリック可能に*/
        padding: 0.5rem 1rem; /* 内側の余白 */
       text-decoration: none; /* リンクの下線を消す*/
        color: #333; /* ダークグレー*/

        &:hover { /* ホバーしたときの扱い*/
          background-color: #f0f0f0;  /* ホバー時の色*/
        }
      }
    }
  }
}

ですのでここからはボタンを押したらメニュー画面が出てきて、もう一度押したら消える、という動きをJavaScriptで書いていきます。

JavaScriptを書いていく

HTMLにstyle.cssをあとで書き加えるということを前提に書いていきます。

</bodyの直前に<script src="script.js"></script>ですね。

ちなみにsrcはソース(source)の略です。

では考えていきましょう。

getElementByIDでボタンの要素を取得します。

これを操作することでnavを操作するわけですので、こちらはqueryselectorで要素を取得しておきます。

const menuBtn = document.getElementById('menu-btn');
const nav = document.querySelector('nav');

次にボタンをクリックしたらイベントが起こるので以下のようになります。

menuBtn.addEventListener('click', () => {
})

次にイベントの中身(トグル)を書きます。

navを付けたり消したりするわけですので以下のように書きます。

と、ここまで書いて気づきました。

ボタンを押したときのクラスをまだ書いてませんでした。

というわけでshowとでも名付けることにします。

nav {
  position: absolute; /*ヘッダーを基準に書いていく*/
  display: none; /*最初はメニューが見えないようにしておく*/
  &.show{
    display: block; /* (これを追加)show クラスがついたら表示*/
  }
  top: 100%; /*ヘッダーの下に配置*/
  right: 0; /* 右端はヘッダーに合わせる*/

改めてトグルを書きます。

navにトグルを付与することでshowクラスがついたり消えたりするわけですので以下のようになります。

menuBtn.addEventListener('click', () => {
    nav.classList.toggle('show');
})

これで完成です。

ボタンを押すごとに、showというクラスが付与されてnavが表示される、クラスが消されて表示がなくなる、の繰り返しになります。

おわりに

今回は簡単でしたね。

正直もうちょっと時間がかかるかと思いましたけど、簡単で助かりました。

次回はこれらがちゃんと動くかを確認したいと思います。

※追記

どうせ絶対にうまくいかないだろうし、修正する過程とかもあるだろうから確認は次回にしようと思ったのですが、やってみたら普通にできました。

メニューボタン
ハンバーガーメニュー

ボタンを押したら、メニューが現れたり消えたりします。

特に問題はありませんでした。

なので次は、メインの項目、つまり投票するところを作っていきます。

おそらく私の頭では理解して作っていくにはかなり時間がかかると思いますが、頑張って行こうと思います。

ハンバーガーメニューの作成②CSS

前回はハンバーガーメニューの骨子となるHTMLを作成しました。

<!doctype html>
<html lang="ja">

<head>
    <meta charset="utf-8" />

    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <title>投票ボタン付き 横棒グラフ</title>
    <link rel="stylesheet" href="style.css">
    <link rel="stylesheet"
        href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200&icon_names=menu" />
</head>

<body>
    <header>

        <button id="menu-btn">
            <span class="material-symbols-outlined">menu</span>
        </button>
        <nav>
            <ul>
                <li><a href="#">ホーム</a></li>
                <li><a href="#">投票ページ</a></li>
                <li><a href="#">コメント</a></li>
                <li><a href="#">お問い合わせ</a></li>
            </ul>
        </nav>
    </header>
    <main>
        <div>
            投票をするところ
        </div>
    </main>
    <footer>
        <p>&copy; 2025 投票アプリ</p>
    </footer>
</body>

</html>

これをCSSで操作し、ハンバーガーメニューを作ることにします。

掲示板のCSSのでそこまで凝ったものにする必要はないので楽で助かります。

イメージとしてはnavタグの中にメニューがありますので、このnavの表示、非表示をJavaScriptで操作することにします。

表示の時にはメニューが見えて、非表示の時には消える感じです。

まずは全体の構成などを決めるためにbodyを設定しておきます。

ブログの最初の方でも書きましたけど、VSCodeを使いますのでSassで書いていきます。

一応書いておきますと、以下のような書き方の場合、

body {
  font-size: 1rem;  /* font-size がキー(プロパティ) */
  color: red;       /* color がキー(プロパティ) */
}

font-sizeをキー(プロパティ)、1remを値と呼びます。

ではまずbodyを書いていきます。

body

body {
  font-size: 1rem;
  font-family: sans-serif;
  margin: 0;
  padding: 0;
}

フォントの種類や色、余白を設定します。

背景色とかもここで決めてしまおうかと思いましたが、ヘッダーやメイン、フッターで色分けする可能性もあるのでボディで決めてしまうのはやめておきます。

もし色分けをしないなら、後ほど各セッションで設定するか、ボディに戻ってきて書き足すことにします。 次にヘッダーです。

header {
  position: relative;
  background-color: #fff;
  padding: 1rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

そんなにややこしいことはしませんが、とりあえず要素の配置だけ決めておきます。

まずposition: relative;ですが、これは何かを基準にして相対的な位置に自分を置くという意味ですが、相対的なものの対象がないため自分自身が基準になります。

これを付けておくことで、今後ヘッダーを基準として配置していくことができます。

背景はとりあえず白、paddingは1remにしておきましょう。

メニューを並べる方向は横で、flexはデフォルトは横方向なので特にいじる必要はありません。

<button>と<nav>は横に並ぶことになります。

次にハンバーガーメニューについて配置していきます。

ハンバーガーメニュー

nav {
  position: absolute; /*ヘッダーを基準に書いていく*/
  display: none;  /*最初はメニューが見えないようにしておく*/
  top: 100%;  /* ヘッダーのすぐ下にメニューを配置*/
  right: 0; /* 右端はヘッダーに合わせる*/
}

コメントアウトで基本的なことは書いてますが、私自身のために以下に注意書きを書いておきます。

改めて書きますが、イメージとしてはbuttonを押すたびに<nav>が消えたり現れたりする、というようにします。

先ほどヘッダーのところでposition: relative;と書きましたので、ポジションをabsoluteと書いたことによってnavの基準はヘッダーになります。

ヘッダーのすぐ下に配置するのでtopは100%にしておきます。

これはずらし具合を示します。

ヘッダー、メニューの二つの紙があると考えてください。

100%で二枚が連なるように配置されます。

0%ならnavの位置はヘッダーの上端からということになってしまい、完全に紙を重ねることになります。

対して右端はヘッダーと合わせることになりますので、ずらし具合は0になります。

次はulの中を書いていくことになります。

ul

  ul{
    list-style: none;
    padding: 1rem;
  }

以上。

リストの「・」とか余白を消して、文字の周りに適当に余白を作っておきます。

あとで確認してから気に入らないところを直していきます。

つぎはliですね。

li

li {
      margin-bottom: 0.5rem; /*リストの間に幅を持たせる */

      a { /* リンク文字の表示の扱い*/
        display: block;            /* 全体をクリック可能に*/ 
        padding: 0.5rem 1rem;      /*内側の余白 */ 
        text-decoration: none;   /*リンクの下線を消す*/
        color: #333; /* ダークグレー*/

        &:hover { 
          background-color: #f0f0f0;  /* ホバー時の色 ライトグレー*/
        }
      }
    }

display: block;についてですが、<a>は最初はインラインの要素を持っていて、文字の分しか幅を持っていません。

なのでblockにすることで縦に並べ、かつ端から端までをリンクの範囲を広げます。

&:hoverについて

&というのは親セレクタを示すものなのですが、「書き方はSassやのになんで親セレクタを書く必要があんねん」という疑問が出てきますが、これはスペースの問題です。

もしこれを省略して以下のように書いてしまうと、

li {
  :hover {
    color: red;
  }
}

出力は「liの中にある子要素」ということで

li :hover {
  color: red;
}

このようになってしまいます。

lihoverの間にスペースが入ってしまって疑似セレクタとして機能してくれないわけです。

なので&:hoverと直接くっつける必要があります。

つまり以下のようになります。

li {
  &:hover {
    color: red;
  }
}

おわりに

さて、CSSについてはこんなものでしょうか。

ボタンについてはGoogleフォントから直接持ってきてますし、書く必要はないでしょう。

今回やったことをまとめておきます。

body {
  font-size: 1rem;
  font-family: sans-serif;
  margin: 0;
  padding: 0;
}

header {
  position: relative;
  background-color: #fff;
  padding: 1rem;
  display: flex;
  justify-content: space-between;
  align-items: center;
}

nav {
  position: absolute; /*ヘッダーを基準に書いていく*/
  display: none; /*最初はメニューが見えないようにしておく*/
  top: 100%; /*ヘッダーの下に配置*/
  right: 0; /* 右端はヘッダーに合わせる*/

  ul {
    list-style: none;/* マーカーを消す*/
    padding: 0;      /* ul の内側の余白を消す*/
    
    li {
      margin-bottom: 0.5rem;

      a { /* リンクの表示の扱い*/
        display: block; /* li 全体をクリック可能に*/
        padding: 0.5rem 1rem; /* 内側の余白 */
       text-decoration: none; /* リンクの下線を消す*/
        color: #333; /* ダークグレー*/

        &:hover { /* ホバーしたときの扱い*/
          background-color: #f0f0f0;  /* ホバー時の色*/
        }
      }
    }
  }
}

次回はこれらを動かすためにJavaScriptを書いて、動作を見てみることにします。