shiisuke’s blog

文系卒。社会人5年目くらい。ITやプログラミングの勉強で学んだことを書きます。

独自ドメイン&HTTPS接続でWebサイトを公開してみた(Amazon S3 + Route 53 + CloudFront + ACM)

Ⅰ.きっかけ

先日受講したJAWS-UGのハンズオンで、「独自ドメイン」&「HTTPS接続」のWebサイトを Amazon S3 上で公開できることを知ったので1、その方法をさっそく試してみました。

草野球の走塁クイズ

<目次>

Ⅱ.使った環境

  • Windows10(64bit)
  • Google Chrome(バージョン: 83.0.4103.116)
  • AWS アカウント

Ⅲ.基礎知識

Amazon Route 53(フィフティスリー)とは?2

  • DNSサーバ機能を提供するサービス
  • www.example.com のようなURLを、実際に使用しているEC2やS3などのAWSサービスのエンドポイントに結び付ける

AWS CloudFront とは?3

  • Webコンテンツの配信を高速化する、コンテンツデリバリーネットワーク(CDN)サービス
  • オリジナルのWebサーバの中身をエッジサーバがキャッシュし、一般ユーザからのリクエストに対してエッジサーバからコンテンツを返す

AWS CloudFront の特徴

  • オリジナルのWebサーバの負担軽減:オリジナルのWebサーバに届くリクエストが減る
  • レスポンス速度の高速化:クライアントからアクセスするネットワークの距離が近くなるため、それに応じてレスポンス速度が速くなる

AWS Certificate Manager(ACM) とは?4

  • SSL/TLS証明書のプロビジョニング、管理、デプロイができるサービス
  • ここで発行した証明書は、Amazon CloudFront で無料で利用できる

HTTPSとは?5

  • 「HyperText Transfer Protocol Secure」の略称
  • インターネット上のHTTP通信がSSLによって保護され、第三者が盗み見しようとしてもデータの内容を解読することができない

Ⅳ.手順

1.ドメインを準備する6

(1)ドメインの購入

  • お名前.comで購入する場合はこちらを参照

(2)ネームサーバの変更

AWS側での設定>

  1. マネジメントコンソール(AWSのサービスを管理する画面)で、「サービス > Route 53」を選択する
  2. 「ホストゾーン > ホストゾーンの作成」をクリックする
  3. 画面右側の「ドメイン名」に、先ほど購入したドメイン名を入力する
  4. 「タイプ」は「パブリックホストゾーン」を選択し、「作成」をクリックする
  5. NSレコードとSOAレコードが表示されていることを確認する

<お名前.com側での設定>

  1. 「お名前.com Navi」にログインする
  2. ドメイン一覧」で、購入したドメイン名を選択する
  3. 「ネームサーバー情報」で「ネームサーバーの変更」を選択する
  4. 「2.ネームサーバーの選択」で「その他」を選択し、Route 53 のNSレコードの値を入力し、「確認」をクリックする
  5. 「ご確認」という画面で「OK」をクリックする
  6. しばらくすると、お名前.comから「ネームサーバー情報変更 完了通知」というメールが届く

2.S3 を使ってWeb サイトを公開する

(1)S3バケットの作成

  1. マネジメントコンソール(AWSのサービスを管理する画面)で、右上に表示されているリージョンを「東京」に設定する
  2. 「サービス > S3」を選択する
  3. バケットを作成」をクリックし、バケット名を入力する
  4. 「パブリックアクセスをすべてブロック」のチェックを外す
  5. 「現在の設定により、このバケットバケット内のオブジェクトがパブリックになる可能性があることを承認します。」にチェックを入れ、「バケットを作成」をクリックする

(2)S3バケットの設定

<静的Webサイトホスティングの設定>

  1. S3 ダッシュボード(メイン画面)で、作成したバケットの名前を選択する
  2. 「プロパティ > Static website hosting」を選択する
  3. 「このバケットを使用してウェブサイトをホストする」を選択し、「インデックスドキュメント」に「index.html」と入力して「保存」をクリックする

バケットポリシーの設定>

  1. 「アクセス権限 > バケットポリシー」を選択する
  2. バケットポリシーエディター」にこちらのコードを入れて「保存」をクリック

※「xxxxxxxxx」には作成したバケットの名前を入れる。

(3)ファイルアップロード

  1. S3画面で「概要」を選択する
  2. 「フォルダの作成」と「アップロード」を使って、公開するWebサイトのHTMLファイルやCSSファイルをアップロードする

※今回はこちらのファイルを使いました

f:id:shiisuke1229:20200712120403p:plain

(4)Webページ閲覧

  1. 「プロパティ > Static website hosting」を選択し、エンドポイントのURLをクリックする
  2. ブラウザで表示されることを確認する

3.HTTPS接続を可能にする

(1)CloudFront の設定

  1. マネジメントコンソールで、「サービス > CloudFront」を選択する
  2. 「Create Distribution」をクリックする
  3. 「Get Started」をクリックする
  4. 「Origin Domain Name」に、 2. (4) で確認したエンドポイントのURLを貼り付け、画面下の「Create Distribution」をクリックする
  5. 3分ほど待つと、「Status」が「In Progress」から「Deployed」に変わる
  6. 一覧に記載されている「Domain Name」をコピーし、ブラウザで開いて、2. (4) と同じWebサイトが表示されることを確認する

(4)Route 53 の設定①

  1. Route 53 ダッシュボード(メイン画面)で「ホストゾーン」を選択する
  2. 作成したドメインの名前を選択する
  3. 「レコードセットの作成」をクリックする
  4. 「レコードセットの作成」で、「名前」をWebサイトを公開予定のドメイン名とする
  5. 「タイプ」は「A - IPv4」を選択する
  6. エイリアス」は「はい」を選択する
  7. エイリアス先」はリストから「S3 ウェブサイトエンドポイント」に出てくるバケット名を選択して、「作成」をクリックする
  8. Aレコードが追加されていることを確認する
  9. 公開予定のWebサイトのドメインをブラウザのアドレスバーに入力し、2. (4) と同じWebサイトが表示されることを確認する

(3)SSL/TLS 証明書の作成

  1. マネジメントコンソールで、右上に表示されているリージョンを「バージニア北部」に設定する
  2. 「サービス > Certificate Manager」を選択する
  3. 「証明書のリクエスト」をクリックする
  4. 「パブリック証明書のリクエスト」を選択した状態で「証明書のリクエスト」をクリックする
  5. ドメイン名」に、公開予定のWebサイトのドメインを入力して、「次へ」をクリックする
  6. DNS の検証」を選択した状態で「次へ」をクリックする
  7. 「タグを追加」では何もせずに「確認」をクリックする
  8. 次の「確認」画面では「確定とリクエスト」をクリックする
  9. ドメイン名の隣にある▶ボタンを選択し、「Route 53 でのレコードの作成」をクリックして、「作成」をクリックする
  10. 10分ほど待つと、AWS Certificate Manager ダッシュボードで「検証状態」が「検証保留中」から「発行済み」に変わる

(4)SSL/TLS 証明書の配置

  1. CloudFront Distributions(メイン画面)で、新しく作成したリストの「ID」を選択する
  2. 「Behaviors」を選択し、先頭のリストにチェックを入れて「Edit」をクリックする
  3. 「Viewer Protocol Policy」で「Redirect HTTP to HTTPS」を選択し、画面下で「Yes, Edit」をクリックする
  4. 「General > Edit」を選択する
  5. 「Alternate Domain Names (CNAMEs)」に、公開予定のWebサイトのドメインを入力する
  6. SSL Certificate」で「Custom SSL Certificate (example.com)」を選択し、画面下で「Yes, Edit」をクリックする
    • ここが操作不可となっている場合は、リージョンの情報が「東京」のままになっているので、一度リロードする
  7. 10分ほど待つと、CloudFront Distributions で「Status」が「In Progress」から「Deployed」に変わる

(5)Route 53 の設定②

  1. Route 53 ダッシュボードで「ホストゾーン」を選択する
  2. 作成したドメインの名前を選択する
  3. 公開予定のドメインのAレコードにチェックを入れ、「エイリアス先」の内容を一旦削除して、リストから「CloudFrontディストリビューション」に出てくる名前を選択して、「レコードセットの保存」をクリックする

(6)Webサイトへのアクセス

  • 公開予定のWebサイトのドメインをブラウザのアドレスバーに入力し、2. (4) と同じWebサイトが表示され、HTTPS接続になっていたら成功!
    • アクセスできない場合は、しばらく時間を置いてからアクセスする

Ⅴ.感想

  • これまでは「AWSってサービスが多くて難しいなぁ...」と感じていましたが、今回実際に手を動かしてみると、各サービスの役割や内容がスッと頭に入ってきて良かったです(^^)
  • 今後は、よく名前を聞く Amazon EC2AWS Lambda などのサービスたちも触ってみたいと思います。
  • 今回作ったサイトは草野球チームのメンバーに自慢します👍

Ⅵ.参考


  1. 詳しい手順はこちらの動画で紹介されています。動画では Route 53 でドメインを取得されていますが、今回は「お名前.com」でドメインを購入し、ネームサーバを Route 53 に変更する手順で進めました。

  2. 小笠原 種高氏『Amazon Web Servicesのしくみと技術がこれ1冊でしっかりわかる教科書』(2019年、技術評論社)を参照

  3. 同上

  4. https://aws.amazon.com/jp/certificate-manager/ を参照

  5. SSLとは?httpsとは?簡単説明」を参照

  6. mochikoAsTech氏『DNSをはじめよう ~基礎からトラブルシューティングまで~』を参照

【JAWS-UG 初心者支部】「AWS 上で静的なWeb サイトを公開しよう!ハンズオン」参加レポート

Ⅰ.きっかけ

以前、草野球チームで走塁を楽しく学ぶためのクイズサイトを「GitHub Pages」で作ったのですが(ブログ記事)、このサイトを「AWS上で動かしてます!」って言えたらカッコいいだろうな~と思い、その方法を学ぼうと参加しました。

※ハンズオン資料はこちらで公開されています

※当日のTogetterはこちら

<目次>

Ⅱ.使った環境

Ⅲ.基礎知識

Amazon S3エススリー)とは?1

  • Amazon Simple Storage Service」の略称
  • Webサーバーや社内のファイルサーバーのように、インターネット上にデータを保存する場所が借りられる

Amazon S3 の特徴

  • 高い可用性・耐久性:「99.999999999%」(イレブンナイン)のデータ耐久性をうたっている
  • 負荷に強い:「秒間30万リクエスト以上」の高負荷に耐えられる
  • Webサーバとして利用可能:静的なWebサイトを公開する機能がある

AWS Cloud9(クラウドナイン)とは?2

AWS Cloud9の特徴

  • 共同でのコーディングが容易:互いのタイピングをリアルタイムで確認できる
  • 多くの言語やツールをサポート:様々な開発ツールやプログラミング言語がパッケージ化されており、簡単にコーディングを開始できる

Ⅳ.ハンズオン

1.Amazon S3 の静的Webサイトホスティング機能を使って、簡単なWebサイトを公開する

(1)S3バケットの作成

  1. マネジメントコンソール(AWSのサービスを管理する画面)で、右上に表示されているリージョンを「東京」、左下に表示されている言語を「日本語」に設定する
  2. 「サービス > S3」を選択する
  3. バケットを作成」をクリックし、バケット名を入力する
    • バケット名は全世界でユニーク(唯一無二)なものにする必要がある
    • 今回は「20200709-shiisuke-s3-hands-on」とした
  4. 「パブリックアクセスをすべてブロック」のチェックを外す
  5. 「現在の設定により、このバケットバケット内のオブジェクトがパブリックになる可能性があることを承認します。」にチェックを入れ、「バケットを作成」をクリックする

f:id:shiisuke1229:20200711173122p:plain

(2)S3バケットの設定

<静的Webサイトホスティングの設定>

  1. S3 ダッシュボード(メイン画面)で、作成したバケットの名前を選択する
  2. 「プロパティ > Static website hosting」を選択する
  3. 「このバケットを使用してウェブサイトをホストする」を選択し、「インデックスドキュメント」に「index.html」と入力して「保存」をクリックする

f:id:shiisuke1229:20200711210009p:plain

バケットポリシーの設定>

  1. 「アクセス権限 > バケットポリシー」を選択する
  2. バケットポリシーエディター」にこちらのコードを入れて「保存」をクリック

※「xxxxxxxxx」には作成したバケットの名前を入れる。私の場合は「20200709-shiisuke-s3-hands-on」

(3)ファイルアップロード

  1. こちらのコードを、自分のPCの任意の場所に「index.html」として保存する
  2. S3画面で「概要 > アップロード」を選択する
  3. 「ファイルを追加」またはドラッグ&ドロップで 上記1のファイルを選択して、「アップロード」をクリックする

(4)Webページ閲覧

  1. 「プロパティ > Static website hosting」を選択し、エンドポイントのURLをクリックする
  2. ブラウザで「Hello, AWS World!!」と表示されれば成功!

f:id:shiisuke1229:20200711213206p:plain

f:id:shiisuke1229:20200711223233p:plain

2.AWS Cloud9 を使って開発を進め、AWS CLI でWebサイトを更新する

(1)AWS Cloud9 環境を構築

  1. マネジメントコンソールで、「サービス > Cloud9」を選択する
  2. 「Create environment」をクリックする
  3. Nameに任意の名前を入力し、「Next step」をクリックする
    • 今回は「S3Hands-on」とした
  4. 「Environment settings」は全てデフォルトのまま「Next step」をクリックする
  5. 「Review」画面で「Create environment」をクリックする
  6. AWS Cloud9 の環境が立ち上がるのを確認する

f:id:shiisuke1229:20200711214726p:plain

(2)AWS Cloud9 上で開発

<環境の設定>

  1. 「左上の Cloud9 のマーク > Preferences」を選択する
  2. 「Soft Tabs」の値を2に変更する

<作業ディレクトリの作成>

  • 画面下部のターミナルに以下のコマンドを1行ずつ入力して「Enter」を押す
$ mkdir my-webpage
$ cd my-webpage

<「index.html」ファイルのアップロード>

  1. 画面左側の「my-webpage」フォルダを選択した状態で、「File > Upload Local Files」を選択する
  2. 「Select files」をクリックし、1. (3) で作成した「index.html」ファイルを選択する

<「index.html」ファイルの編集>

  • 画面左側で「index.html」フォルダをダブルクリックし、画面右側で以下のように編集して、「Files >Save」で保存する(Windowsなら「Ctrl + s」でも可)
<!DOCTYPE html>

<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>しいすけの自己紹介ページ</title>
</head>
<body>
  <h1>名前:しいすけ</h1>
  <p>好きなもの:野球、日本茶、感動系のドラマ/アニメ。</p>
  <p>Twitter:<a href="https://twitter.com/shiisuke1229">@shiisuke1229</a></p>
</body>
</html>

(3)AWS CLI でS3 にファイルコピー

  • 画面下部のターミナルに以下のコマンドを入力して「Enter」を押す
$ aws s3 cp index.html s3://xxxxxxxxx

※「xxxxxxxxx」には作成したバケットの名前を入れる。私の場合は「20200709-shiisuke-s3-hands-on」

(4)ページ閲覧

    1. (4) で表示したエンドポイントのURLを再度表示し、変更が反映されていることを確認する

f:id:shiisuke1229:20200711223343p:plain

【参考】今回作成したリソースを削除する方法

(1)S3バケットの削除

  1. S3ダッシュボードで、作成したバケットの名前の左にあるボタンを選択し、「空にする」をクリックする
  2. バケット名を入力して「空にする」をクリックする
  3. 「サマリー」で「正常に削除されました」と表示されていることを確認し、「終了」をクリックする
  4. 再度バケットの名前の左にあるボタンを選択し、「削除」をクリックする
  5. バケット名を入力して「バケットを削除」をクリックする
  6. 作成したバケットが一覧から消えることを確認する

(2)AWS Cloud9 リソースの削除

  1. Cloud9ダッシュボードで、今回作成した環境を選択し、「Delete」をクリックする
  2. 確認ダイアログが表示されるので、「Delete」と入力したうえで「Delete」をクリックする
  3. しばらくすると一覧から消える

Ⅴ.感想

  • ハンズオン受講前はちゃんとできるか不安でしたが、発表者の金澤さん(@ketancho)の資料と説明がとっても分かりやすくて、意外と簡単にできました!
  • 今回のイベントでは、群馬支部の紹介や Amazon S3 についてのマニアックな話などもあり、AWSJAWS-UG はまだまだ奥が深くて、もっともっと知っていきたいなと思いました。
  • 草野球チームの走塁クイズサイトは、独自ドメインHTTPS接続にしたうえで実際に公開しました。詳細は以下のブログで紹介していますので、よろしければご覧ください(^^)

shiisuke2018.hatenablog.com

草野球の走塁を学べるゲームを作ってみた(HTML + CSS + JavaScript)

※上の画像をクリックしたら実際のサイトに飛びます

Ⅰ.きっかけ

  • 私の所属する草野球チームで、「今シーズンは以下の ”4つのゴー” を走塁で実践しよう!」ということになりました。
  • これをチーム全員で楽しく覚えられたらと思い、クイズ形式で学べるゲームにしてみました。

走塁における「4つのゴー」

場面 ランナーの判断
「ゴロ」ゴー
  • 0アウトor1アウト ランナー2,3塁で内野ゴロ
  • 0アウトor1アウト ランナー2塁でサードゴロorショートゴロ
全員スタートを切る。内野ゴロが外野に抜けた場合に、2塁ランナーの生還率が高まる。
「スイング」ゴー
  • 2アウト ランナーどこかの塁 2ストライクで打者がスイングした
全員スタートを切る。
「ストライク」ゴー
  • 2アウト ランナー2塁 2ストライクで、投手の投げたボールがストライクゾーンに向かった
スタートを切る。
「軌道」ゴー
  • 0アウト~2アウト ランナー1塁or2塁で、投手の投げたボールがキャッチャーの前でバウンドしそう
スタートを切る。キャッチャーが普通に捕ったら急いで戻る。

Ⅱ.作ったもの

  • 野球のランナーの判断を3択で出題するゲーム。
  • 回答を選んだら正誤判定と解説を表示する。
  • すべて回答し終えたらトータルの正解数とコメントを表示する。
  • 作成したサイトはこちら

Ⅲ.使用したもの

Ⅳ.大まかな流れ

  1. HTML、CSSJavaScript、画像のファイルを作る。
  2. ファイルをSourcetreeでGitHubにプッシュする。
  3. GitHub Pagesを使ってWebサイトを公開する。

Ⅴ.詳細な手順

1.必要なファイルの用意

(1)HTMLファイル

  • 以下のコードを書いて「index.html」として保存する。
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>草野球の走塁クイズ</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="css/styles.css">
</head>
<body>  
  <main>
    <div class="row">
      <h1>草野球の走塁クイズ</h1>
    </div>   
    
    <div class="container">   
      <!-- 何問目かを表示する領域    -->
      <h2 id="question_number"></h2>

      <!-- 問題を表示する領域 -->
      <p id="question"></p>

      <!-- 画像を表示する領域 -->
      <p id="image"></p>

      <!-- 選択肢を表示する領域 -->
      <ul id="choices"></ul>

      <!-- 解説を表示する領域 -->
      <p id="answer"></p>

      <!-- 「次の問題へ」ボタンを表示する領域。回答前はボタンを押せない -->
      <div id="btn" class="disabled">次の問題へ</div>

      <!-- 正答数を表示する領域。最初は隠れた状態にする -->
      <div id="result" class="hidden">
        <p id="score"></p>
        <p id="comment"></p>
        <a href="">リトライ</a>
      </div>
    </div>

    <script src="js/main.js"></script>

  </main>
  <footer>
    <p>Copyright &copy; shiisuke</p>
  </footer>
</body>
</html>

(2)CSSファイル

  • 以下のコードを書いて、cssフォルダに「common.css」として保存する。
/* 基本の設定
 -------------------------------------------- */
body {
  background: #e9edef;
  font-size: 16px;
  font-family: Verdana, sans-serif;
  line-height: 2;
  margin: 0;
  padding: 0;
}

div, header, main, footer { 
  display: block; 
}

h1 {
  font-size: 36px;
  font-family: Verdana, sans-serif;
}

h2 {
  font-size: 24px;
  font-family: Verdana, sans-serif;
}

/* メインエリア
 -------------------------------------------- */
main {
  width: 100%;
  max-width: 930px;
  margin: 0 auto;
  padding: 10px 0 5px;
  color: #114046;
}

main h1 {
  color: #0061b1;  
  text-align: center;
}

/* フッター
 -------------------------------------------- */
footer {
  background-color: #e9edef;
  color: #bbcfd4;
  font-family: "Montserrat",sans-serif;
  width: 100%;
  padding: 5px;  
  text-align: center;
}


/* カラムの設定
 -------------------------------------------- */
.row {
  max-width: 930px;
  margin: 0 auto 25px;
}

.container {
  max-width: 930px;
  margin: 8px auto;
  background: #fff;
  border-radius: 4px;
  padding: 10px 20px 40px;
  position: relative;
}

#image > img{
  width: 352px;
  height: 216px;
  margin: 8px auto;
  padding: 0 20px;
}

/* 問題文のスタイル */
#question {
  margin-bottom: 16px;
}

/* 選択肢のスタイル */
#choices {
  list-style: none; /* リストにデフォルトで付いている黒マルなどを外す */
  padding: 0;
  margin-bottom: 25px;
}

/* 選択肢一つ一つのスタイル */
#choices > li {
  border: 1px solid #ccc;
  border-radius: 4px;
  padding: 8px;
  margin-bottom: 17px;
  cursor: pointer; 
}

#choices > li:hover {
  background: #f8f8f8;
}

/* 正解した場合の選択肢のスタイル */
#choices > li.correct {
  background: #d4edda;
  border-color: #c3e6cb;
  color: #155724;
  font-weight: bold;
}

/* 正解だったら「〇」と表示する */
#choices > li.correct::after {
  content: '   〇';
}

/* 間違えた場合の場合の選択肢のスタイル */
#choices > li.wrong {
  background: #f8d8da;
  border-color: #f5c6cb;
  color: #721c24;
  font-weight: bold;
}

/* 間違えたら「✖」と表示する */
#choices > li.wrong::after {
  content: '   ✖';
}

#answer {
  padding: 0;
  margin-bottom: 25px;
  color: #721c24;
}

/* 回答後の「次の問題へ」ボタンと「リトライ」ボタンのスタイル */
#btn, #result > a {
  background: #3498db;
  padding: 10px;
  margin: 50px, 0, 30px;
  border-radius: 0.40px;
  cursor: pointer;
  text-align: center;
  color: #fff;
  font-weight: bold;
  box-shadow: 0 4px 0 #2880b9;
}

/* 回答前の「次の問題へ」ボタンのスタイル */
#btn.disabled {
  background:#ccc;
  box-shadow: 0 4px 0 #bbb;
  opacity: 0.7;
}

/* 正答数を表示する領域のスタイル */
#result {
  position: absolute;
  width: 300px;
  background: #f0fff0;
  padding: 30px;
  box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
  top: 100px;
  left: 0;
  right: 0;
  margin: 0 auto;
  border-radius: 4px;
  text-align: center;
  transition: 0.4s;
}

/* 正答数を表示する領域を画面外に置いておく */
#result.hidden {
  transform: translateY(-500px);
}

/* 正答数のスタイル */
#score {
  font-size: 24px;
  color: #130d4a;
  font-weight: bold;
  margin: 15px 0 10px;
}

/* コメントのスタイル */
#comment {
  font-size: 34px;
  color: #e73738;
  font-weight: bold;
  margin: 0 0 20px;
}

/* 「リトライ」ボタンのスタイル */
#result > a {
  display: block;
  text-decoration: none;
  margin-bottom: 25px;
}

/* ----------------------
 画面の横幅が599px以下の場合に適用(スマホ用)
 ---------------------- */
 @media screen and (max-width: 599px) { 
  
  body {
    line-height: 1.5;
  }

  h1 {
    font-size: 24px;
  }

  h2 {
    font-size: 20px;
  }

  #image > img {
    max-width: 352px;
    max-height: 216px;
    width: 80vw;
    padding: 0 15px;
  }

  .container {
    margin-left: 0; 
    width: auto; 
  }
  .row, #question_number, #question, #choices, #answer, #btn, #btn.disabled, #result, #result.hidden, #score, #comment {
    padding: 0 8px; 
  }
}

(3)jsファイル

  • 以下のコードを書いて、jsフォルダに「main.js」として保存する。
'use strict';
{
  // index.htmlから必要な要素を取得
  const question = document.getElementById('question');
  const choices = document.getElementById('choices');
  const answer = document.getElementById('answer');
  const btn = document.getElementById('btn');
  const result = document.getElementById('result');  
  const scoreLabel = document.getElementById('score');
  const commentLabel = document.getElementById('comment');
  
  // クイズのデータ
  const quizSet = shuffle([
    // qは問題文の配列、cは選択肢の配列(配列の先頭が正解)、aは解説
    {q: '無死2, 3塁でバッターが内野ゴロを打った。2塁ランナーと3塁ランナーはどうする?', c: ['どちらもスタートを切る。', '3塁ランナーはスタートを切り、2塁ランナーは戻る。', 'どちらも戻る。'], a: '<解説>「ゴロ」ゴーの場面。両ランナーとも打った瞬間にスタートを切ることで、打球が外野に抜けた場合に2塁ランナーもホームインできる。ピッチャーゴロの場合は、3塁ランナーが挟まれている間にバッターが2塁へ行く。', x: '<img src="img/scene1.png">'},    
    {q: '無死2塁でバッターがショートゴロを打った。2塁ランナーはどうする?', c: ['3塁へスタートを切る。', '2塁へ戻る。','2塁と3塁の間で止まる。'], a: '<解説>「ゴロ」ゴーの場面。セオリーでは2塁に戻ることになっているが、草野球レベルではショートが3塁に投げてアウトにするのは難しい。', x: '<img src="img/scene2.png">'},
    {q: '2死1, 3塁で2ストライクの場面。1塁ランナーはどのタイミングでスタートを切る?', c: ['バッターがスイングを始めた時。', 'バッターが打った時。', 'ピッチャーが投げた時。'], a: '<解説>「スイング」ゴーの場面。バッターがスイングを始めたらスタートを切る。', x: '<img src="img/scene3.png">'},
    {q: '2死2塁で2ストライクの場面。2塁ランナーはどのタイミングでスタートを切る?', c: ['ピッチャーの投球がストライクゾーンに向かった時。', 'バッターが打った時。', 'ピッチャーが投げた時。'], a: '<解説>「ストライク」ゴーの場面。2塁ランナーはピッチャーの投球がよく見えるので、投球がストライクになりそうだったらスタートを切る。', x: '<img src="img/scene4.png">'},
    {q: '0死2塁でピッチャーがワンバウンドする軌道の投球をした。2塁ランナーはどうする?', c: ['3塁へスタートを切り、キャッチャーが捕ったら2塁へ戻る。', 'キャッチャーが弾いたら3塁へスタートを切る。', '3塁へスタートを切り、キャッチャーが捕ってもそのまま3塁へ。'], a: '<解説>「軌道」ゴー。ピッチャーの投球がワンバウンドする軌道ならスタートを切り、キャッチャーが捕ったら急いで戻る。', x: '<img src="img/scene5.png">'},
  ]);

  // いま何問目かを管理する変数
  let currentNum = 0;

  // 回答したかを管理する変数
  let isAnswered;

  // 正答数を管理する変数
  let score = 0;

  // 選択肢をシャッフルする関数
  function shuffle(arr) {
    for (let i = arr.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [arr[j], arr[i]] = [arr[i], arr[j]];
    }
    return arr;
  }

  // 正誤判定の関数
  function checkAnswer(li) {
    // 回答済みの場合には処理をしない
    if (isAnswered) {
      return;
    }

    // 状態を「回答済み」にする
    isAnswered = true;

    // 選択したリストが選択肢の配列の先頭と一致していれば正解とする
    if (li.textContent === quizSet[currentNum].c[0]) {
      // 正解したときの処理
      li.classList.add('correct');
      score++;
    } else {
      // 間違えたときの処理
      li.classList.add('wrong'); 
    }

    // 解説を表示する
    answer.textContent = quizSet[currentNum].a;

    // 回答後はボタンの色を青に戻す
    btn.classList.remove('disabled');
  }  
  
  // 画面描画の処理の関数
  function setQuiz() {
    // 状態を「未回答」にする
    isAnswered = false;
        
    // 何問目かを表示する
    document.getElementById('question_number').textContent = "第" + (currentNum + 1) + "問 / 全5問";
    
    // 問題文を表示する
    question.textContent = quizSet[currentNum].q;
    
    // 画像を表示する
    document.getElementById('image').innerHTML = quizSet[currentNum].x;


    // 前の問題の選択肢を削除する
    while (choices.firstChild) { 
      choices.removeChild(choices.firstChild);
    }

    // 前の問題の解説を削除する
    answer.textContent = "";

    // 選択肢を表示する
    // シャッフル関数に選択肢の配列のコピーを渡す
    const shuffledChoices = shuffle([...quizSet[currentNum].c]);
    // 一つ一つの要素をchoiceとして受け取る
    shuffledChoices.forEach(choice => {
      const li = document.createElement('li');
      li.textContent = choice;
      // 選択肢をクリックしたときに正誤判定を行う
      li.addEventListener('click', () => {
        checkAnswer(li);
      });
      choices.appendChild(li);
    });

    // 最後の問題だったらボタンのテキストを「スコアを見る」にする
    if (currentNum === quizSet.length - 1) {
      btn.textContent = 'スコアを見る';
    }      
  }

  setQuiz();  

  // ボタンを押したときの処理
  btn.addEventListener('click', () => {
    // 未回答の場合は次の問題に進めないようにする
    if (btn.classList.contains('disabled')) {
      return;
    }
    // 新しい問題に進んだらボタンの色をグレーに変える
    btn.classList.add('disabled');

    // 最後の問題だったら正答数を表示する
    if (currentNum === quizSet.length - 1) {
      scoreLabel.textContent = `スコア: ${score} / ${quizSet.length}`;
      if (score === 5) {
        commentLabel.textContent = 'Excellent!';
      } else if (score === 4) {
        commentLabel.textContent = 'Great!';
      } else if (score === 3) {
        commentLabel.textContent = 'Good!';
      } else {
        commentLabel.textContent = '';
      }
      result.classList.remove('hidden');
    } else {
    // 最後の問題でなかったら次の選択肢を表示する
      currentNum++;
      setQuiz();
    }
  });
}

(4)画像ファイル

  • imgファイルに画像を保存する。

2.ファイルのアップロード

3.GitHub Pagesでの公開

  • GitHubにアクセスし、「Settings」タブの中段にある「GitHub Pages」で「Source」を「None ⇒ master branch」に変更する。

f:id:shiisuke1229:20200128231656p:plain

回答画面

f:id:shiisuke1229:20200128232635p:plain:w450

正解判定画面

f:id:shiisuke1229:20200128232725p:plain:w450

スコア表示画面

f:id:shiisuke1229:20200128232753p:plain:w450

スマホで見た時の画面

f:id:shiisuke1229:20200128234452p:plain:w250

Ⅵ.感想

  • この作業を通して、ボタンを押したときの処理の方法や、jsファイルのデータをHTML側に渡す方法などを知ることができました。
  • 画面のスマホ対応(レスポンシブ化)は結構大変でしたが、チームメイトに紹介した時に「スマホ対応です!」とドヤ顔で自慢できたり、飲み会などちょっとした場でもゲームを楽しめたりできたので、やっておいて良かったと思います(⌒∇⌒)
  • 今回はクイズの問題データをjsファイルに書きましたが、問題数の増加にも対応できるよう、外部のデータベースから取得する方法も調べたいです。

Ⅶ.参考

湊川あい氏【初心者向け】「たのしいGit入門講座」参加レポート

Ⅰ.きっかけ

以前、Webアプリケーションを公開するためにHerokuを使おうとしたのですが、その前提として必要となるGitが分からず挫折してしまいました。。。

そんな中、"わかばちゃんと学ぶ"シリーズで有名な湊川あい(@llminatoll)さんによるGitの使い方講座がサポーターズCoLabで開催されると知り、Gitの勉強の入口になればと思い参加してきました。

結論から言うと、湊川さんの説明はとても分かりやすくて、Gitへの理解がグッと進みました!以下では復習もかねて講座の内容をまとめました。

f:id:shiisuke1229:20191222191644j:plain:w400
わかばちゃんと学ぶ Git使い方入門』の本を買って予習していきました👍

<目次>

Ⅱ.今回使った環境

Ⅲ.講座の内容

1.基礎知識

Gitとは?

  • ファイルを編集して「修正しない方が良かった…」「前の方が良かった…」と思ったときに、ファイルの状態を過去の時点に戻せるタイムマシン。
  • “ある時点” から ”ある時点” までに何が変わったのか(差分)が分かる。
  • 仕事ではみんなGitを通じてプログラムを送ってくるので、社会人になったら必須。

Gitのメリット(ない場合vsある場合)

Gitがない場合 Gitがある場合
ファイルのバージョンが複数あって、どれを使えば良いのか分からない。
例)〇〇_完成版
  〇〇_修正版
  〇〇_田中レビュー済み
ファイルが1つで良いのでスッキリする。
例)〇〇
思いついた時にバックアップを作成して、戻そうと思った時には結構前のバージョンだったりする。 バックアップを都度作成して、好きな時点に状態を戻せる。
同じファイルを数人で別々に編集し、それを1つにまとめる場合、みんなに修正点を聞いて回って差分を手動でコピペしないといけない。 編集された箇所が一目瞭然、楽チン&ミスなしでまとめられる。

GitHubとは?

  • 同じファイルを3~4人で共同編集する場合、リポジトリ※のコピーを各人が持つことになる。
  • 誰かが自分のリポジトリで追加・修正したときに、GitHubを更新し、他のメンバーがそれをコピーすることで変更の差分を埋めることができる。

リポジトリとは、過去のファイルやディレクトリの状態を圧縮して記録する貯蔵庫のこと。必要になったら過去の状態をはき出してくれる。

GitHubのメリット

  • データ置き場としてだけでなく、コミュニケーションの場としても機能する。
  • 例)掲示板みたいに議論する、「こっちの方がいいんじゃない?」とレビューする

2.Gitを使ってみる~料理レシピを管理~

(1)リポジトリを作る

  • SourceTreeで「ファイル > 新規/クローンを作成する」を選択し、「Create」をクリックする。
  • 「保存先のパス」で任意の場所(「PC > ドキュメント」など)を指定し、パスの最後に任意のフォルダ名(「sample」など)を追加して「作成」をクリックする。(※フォルダを先に作ってそのフォルダを指定する、という方法でも可。)
  • 「クローン作製先のパス〇〇は既に存在しています」というメッセージは「はい」を選択する。
  • 指定した場所にフォルダができて、フォルダ内に「.git」というファイル(リポジトリ)があることを確認する。

f:id:shiisuke1229:20191222194016p:plain:w450
.gitファイルは「隠しファイル」にチェックを入れると表示されます。

(2)フォルダにテキストファイルを置く

  • メモ帳を開いて下記1を入力し、先ほど作成したフォルダに「okonomiyaki」という名前で保存する。
1.お好み焼き粉・水・卵を入れる

(3)リポジトリに変更を反映する

  • SourceTreeの「WORKSPACE > ファイルステータス > 作業ツリーのファイル」のリストに表示される「okonomiyaki.text」を選択して、「選択をインデックスに追加」をクリックする。【ステージ】
  • 「Indexにステージしたファイル」のリストに表示された「okonomiyaki.text」を選択し、変更に関するコメント「お好み焼きのタネを追加」を一番下のテキストボックスに記載して、「コミット」をクリックする。【コミット】
  • 「WORKSPACE > History」で先ほどのコメントが記録されていることを確認する。

f:id:shiisuke1229:20191222194651p:plain

※ファイルを一旦ステージに移すのは、コミットするファイルをステージ上で選別して、要らないファイルをコミットしないようにするため。

(4)ファイルを編集する

  • 「okonomiyaki.text」を開き、下記2を記述して保存した後、SourceTreeの「WORKSPACE > ファイルステータス」でステージ→コミットを行う。コミット時のコメントは「具材を追加」とする。
2.キャベツを入れる
  • 「okonomiyaki.text」に下記3を記述して保存した後、ステージ→コミットを行う。コミット時のコメントは「隠し味を追加」とする。
3.コーラを入れる
  • 「WORKSPACE > History」で、すべての変更が記録されていることを確認する。

f:id:shiisuke1229:20191222195533p:plain

(5)ファイルの状態を過去に戻す

  • 「WORKSPACE > History」で、戻りたい地点のコメント(今回は「具材を追加」)をダブルクリックし、「作業コピー切り替えの確認」ダイアログで「OK」を選択する。【チェックアウト】
  • コメント「具材を追加」の隣に「HEAD」という文字が付き、「okonomiyaki.text」を開くと状態が戻っていることを確認する。

f:id:shiisuke1229:20191222200840p:plain

f:id:shiisuke1229:20191222200856p:plain:w350
「3」の記載が消え、「2」を編集した時点に状態が戻りました。

3.GitHubを使ってみる~真央ゼミWebサイトのソースコードを共同管理~

(1)湊川さんのリポジトリをWeb上から取得する

f:id:shiisuke1229:20191222201408p:plain

f:id:shiisuke1229:20191222201419p:plain

  • 「Clone or download」をクリックし、表示されるURLをコピーする。

f:id:shiisuke1229:20191222201434p:plain

  • SourceTreeの「ファイル > 新規/クローンを作成する」をクリックし(または新しいタブを開き)、「Clone」を選択する。
  • 一番上のテキストボックスに先ほどコピーしたURLを貼り付ける。
  • テキストボックスの外をクリックすると他の情報が自動で入力されるので、その状態で「クローン」をクリックする。
f:id:shiisuke1229:20191222202132p:plain:w350

History」にコミットログが表示され、「ドキュメント > PC > my-seminar」にフォルダがコピーされていることを確認する。

f:id:shiisuke1229:20191222202738p:plain
複数人で編集すると、樹形図の表示がカラフルになります。湊川さんはこれを見ると”Gitだー”という感じがするそうです(^^)

f:id:shiisuke1229:20191222203452p:plain:w450
「my-seminar」のフォルダ内

f:id:shiisuke1229:20191222203329p:plain:w450
「index.html」をブラウザで開くとこんな感じです。

(2)ファイルを編集する

  • 「PC >ドキュメント > my-seminar」にある「index.html」をVisual Studio Codeで開く。
  • 「NEWS」一覧に好きな行事を追加して保存する。(今回は「2019/12/22 有馬記念」を追加)

f:id:shiisuke1229:20191222203751p:plain

  • SourceTreeの「WORKSPACE > ファイルステータス」でステージ→コミットを行う。

※本当は新規ブランチを作成して、そこで編集するのが良いらしいのですが、私は新規ブランチでのプッシュ時にエラーが出たためmasterブランチで作業を進めました。

※ブランチについて知りたい方は、『わかばちゃんと学ぶ Git使い方入門』のp107以降をご参照ください。

(3)編集したファイルをWeb上にアップロードする

  • SourceTreeのメニューで「プッシュ」を選択し、プッシュしたいブランチ(今回は「master」)にチェックを入れて、「プッシュ」をクリックする。【プッシュ】

f:id:shiisuke1229:20191222203809p:plain

  • 自分のGitHubページを開き、変更が反映されていることを確認する。

f:id:shiisuke1229:20191222203822p:plain

(4)自分の変更を湊川さんのリポジトリに反映してもらう

  • 自分のGitHubページで「Pull requests > New pull request」を選択する。

f:id:shiisuke1229:20191222204143p:plain

  • 湊川さん(llminatoll)のGitHubページに遷移するので、そこで「Create pull request」をクリックする。

f:id:shiisuke1229:20191222204242p:plain

  • 変更内容のタイトルとコメントを記入して、「Create pull repuest」をクリックする。【プルリクエスト】

f:id:shiisuke1229:20191222204253p:plain

  • リクエストが承認されると、湊川さん(llminatoll)のGitHubページに変更が反映される。【マージ】

f:id:shiisuke1229:20191222204421p:plain

f:id:shiisuke1229:20191222204503p:plain
GitHubの機能を使って公開されているWebページでも変更が反映されています。

f:id:shiisuke1229:20191222204611p:plain
GitHubの「Pull request」ページでチャットのようなやりとりができるのも良いですね!(ちなみに私は競馬はあんまり見ないです(^_^;) )

4.コマンドでGitを使ってみる

  • 講座ではコマンドを使ったリポジトリの作成もしましたが、ブログの分量が長くなったので記載を省略します。
  • なお、WindowsでGitコマンドを使うにはパスを通す必要があり、SourceTreeと一緒にインストールした場合の「git.exe」の場所は以下です。「C:\Program Files (x86)」ではないのでご注意ください。(参考
C:\Users\username\AppData\Local\Atlassian\SourceTree\git_local\bin\git.exe

Ⅳ.感想

  • 私が以前Gitについて調べた時は、Webサイトを20個近く読んでも「"プッシュ"と"プルリクエスト"と"マージ"が大事らしい…」くらいしか分からなかったのですが、今回の講座を通してGitへの理解度が格段に上がりました!
  • また、湊川さんの説明はとにかく分かりやすくて、自分でやっても上手くできたので、「Gitの勉強って楽しいなー!」と心から思えました(^▽^)
  • これからはGitのコマンド操作も学んで、元々使いたかったHerokuにもチャレンジしていきたいです。

草野球ユニのデザイン投票サイトを作ってみた(HTML + CSS + JavaScript+ Googleフォーム)

新ユニフォーム投票サイト

Ⅰ.きっかけ

  • 私の所属する草野球チームでユニフォームのデザインを一新することになったので、HTMLとCSSの勉強をかねて、チームメンバーが好きなデザイン案を選んで投票できるサイトを作ってみました。

Ⅱ.作ったもの

  • 以下の3つのページが含まれるWebサイト
    • 新ユニフォームのデザイン候補一覧ページ
    • 各デザイン候補の詳細ページ
    • 投票フォーム

Ⅲ.使用したもの

Ⅳ.大まかな流れ

  1. HTML、CSSJavaScriptGoogleフォームを使って必要なファイルを作る。
  2. ファイルをFC2ホームページにアップロードする。

Ⅴ.詳細な手順

1.必要なファイルの用意

(1)保存用フォルダ

  1. (2)以降で作成するファイルを保存するために、好きな場所に適当な名前でフォルダを用意する。(今回はデスクトップに「新ユニフォーム投票サイト」という名前のフォルダを用意しました。)
  2. フォルダ内に「css」「images」「css」の各フォルダを作成する。
    f:id:shiisuke1229:20191201183737p:plain:w250
    これ以降で上のようなフォルダを作っていく

(2)画像ファイル

  • Webサイトで使用する画像をimagesフォルダに保存する。

imagesフォルダ

(3)「デザイン候補一覧ページ」のHTMLファイル

  • 以下のコードを書いて「index.html」として保存する。
<!DOCUTYPE html>
<html lang="ja">
<head>
    <!-- 文字化け防止のため、VSCodeの画面右下に表示される文字コード規格もutf-8に揃える。 -->
    <meta charset="utf-8">  
    <!-- ブラウザのタブに表示するタイトル -->
    <title>新ユニフォーム投票サイト</title>
    <!-- レスポンシブ対応 -->
    <meta name="viewport" content="width=device-width, initial-scale=1">  
    <!-- CSSファイルの読み込み -->
    <link rel="stylesheet" href="css/common.css">  
</head>

<body>
    <header>
        <!-- Webページ上部のナビゲーション(メニュー)部分 -->
        <nav>  
            <ul>
                <li><a href="index.html">候補一覧</a></li>
                <li><a href="vote.html">投票フォーム</a></li>
            </ul>
        </nav>        
    </header>

  <main>
      <div class="row">
          <h1>新ユニフォームデザイン候補一覧</h1>
          <p>下のデザインをクリックすると詳細が見られます。</p>
       </div>
      <div class="row">
          <!-- 画像。クリックすると別ページに移動する。 -->
          <div class="col quarter frame">
              No1.赤×黒デザイン<a href="option01.html"><img src="images/option01_front.png" alt="赤×黒ユニフォーム"></a>
          </div>
          <div class="col quarter frame">
              No2.紺×赤デザイン<a href="option02.html"><img src="images/option02_front.png" alt="紺×赤ユニフォーム"></a>
          </div>
          <div class="col quarter frame">
              No3.白×青デザイン<a href="option03.html"><img src="images/option03_front.png" alt="白×青ユニフォーム"></a>
          </div>
      </div>
      <div class="row">
          <div class="col quarter frame">
              No4.青×赤デザイン<a href="option04.html"><img src="images/option04_front.png" alt="青×赤ユニフォーム"></a>
          </div>
          <div class="col quarter frame">
              No5.紺×白デザイン<a href="option05.html"><img src="images/option05_front.png" alt="紺×白ユニフォーム"></a>
          </div>    
      </div>
      <div class="clear">
          <div class="votearea">       
              <!-- 投票フォームに誘導する -->     
              <a class="button" href="vote.html">
                      投票フォームへ進む                       
              </a>
          </div>
      </div>
  </main>

  <footer>
      <p>Copyright &copy; shiisuke</p>
  </footer>
</body>
</html>

(4)「各デザイン候補の詳細ページ」のHTMLファイル

  1. 以下のコードを書いて「option01.html」として保存する。
  2. 同様のファイルを必要な数だけ(今回は5つ)作る。名前は「02」「03」という形で変える。
<!DOCUTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>No1.赤×黒</title>    
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 画像をクリックで拡大表示するためのコード -->    
    <script 
        src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js">
    </script>    
    <link rel="stylesheet" href="css/common.css">
    <!-- 画像をクリックで拡大表示するためのコード --> 
    <link rel="stylesheet" href="css/lightbox.css">
</head>

<body>
    <header>
        <nav>
            <ul>
                <li><a href="index.html">候補一覧</a></li>
                <li><a href="vote.html">投票フォーム</a></li>    
            </ul>
        </nav>
    </header>
    <main>
        <div class="row">
            <div>
                <h1>No1.赤×黒ユニフォーム</h1>
                <p>広島カープ風のデザインです。</p>                    
            </div>
            <div class="col quarter frame">
                <a href="images/option01_front.png" data-lightbox="detail">
                    <img src="images/option01_front.png" alt="赤×黒ユニフォーム_前">
                </a>
            </div>
            <div class="col quarter frame">
                <a href="images/option01_back.png" data-lightbox="detail">
                    <img src="images/option01_back.png" alt="赤×黒ユニフォーム_後">
                </a>                
            </div>
            <div class="col quarter frame">
                <a href="images/option01_cap.png" data-lightbox="detail"> 
                    <img src="images/option01_cap.png" alt="赤×黒ユニフォーム_帽子">
                </a>
            </div>
            <div class="clear">
                <div class="votearea">
                    <p>
                        <span class="price">16,200</span>円(ユニフォーム上下+帽子)
                    </p>
                    <!-- デザイン候補一覧ページに誘導する -->     
                    <a class="button" href="index.html">
                            一覧に戻る                       
                    </a>
                </div>
            </div>
        </div>
    </main>
    <footer>    
        <p>Copyright &copy; shiisuke</p>
    </footer>
    <!-- 画像をクリックで拡大表示するためのコード --> 
    <script src="js/lightbox.js"></script>
</body>
</html>

(5)投票フォームのHTMLファイル

  1. Googleフォームでアンケートを作り、そこで生成されるコードをコピーする。(参考記事

  2. 以下のコードを書いて「vote.html」として保存する。

<!DOCUTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8">
    <title>投票フォーム</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="css/common.css">
</head>

<body>
    <header>
        <nav>
            <ul>
                <li><a href="index.html">候補一覧</a></li>
                <li><a href="vote.html">投票フォーム</a></li>    
            </ul>
        </nav>
    </header>
    <main>
        <!-- Googleフォームのコード --> 
        <iframe src="https://docs.google.com/forms/d/e/1FAIpQLScyGJfSIL9vMHbsIxsxWagmbfq2En6Ib7vrbvWWfLdoCU-PfA/viewform?embedded=true" width="640" height="552" frameborder="0" marginheight="0" marginwidth="0">読み込んでいます…</iframe>
    </main>
    <footer>    
        <p>Copyright &copy; shiisuke</p>
    </footer>
</body>
</html>

(6)すべてのWebページの装飾をするCSSファイル

  • 以下のコードを書いて、cssフォルダに「common.css」として保存する。
@charset "UTF-8";

/* 基本の設定 */
body {
    font-family: 'ヒラギノ角ゴ Pro W3','Hiragino Kaku Gothic Pro','メイリオ',Meiryo,'MS Pゴシック',sans-serif;
    font-size: 16px;
    line-height: 1.7;
    margin: 0;
    padding: 0;
    text-align: center;
}

section,article, aside, nav, header, main, footer { display: block; }

h1,h2,h3,h4 {
    font-family: "Montserrat",sans-serif;
    font-weight: normal;
}

ul, li {
    margin: 0;
    padding: 0;
    list-style: none;
}

a { text-decoration: none;}

img {
    max-width: 100%;
    height: auto;
}

/* ヘッダー */
header {
    background:#a1bbd0 ;
    width: 100%;
    padding: 1em 0 2em;
    position: relative;
}
header nav {
    padding: 10px 0; 
    width: 100%;
    position: absolute;
    top:0;
}
header nav ul{
    margin: auto;
}
header nav ul li{
    display: inline-block;
}
header nav ul li a{
    color:#0000EE;
    font-family: "Montserrat",sans-serif;
    font-size: 16px;
    padding: 0 1em;
}

/* メインエリア */
main {
    width: 100%;
    max-width: 930px;
    margin: 0 auto;
    padding: 10px 0 10px;
    color: #114046;
}

main h1 {
    color: #df6e21;
}

/* フッター */
footer {
    background-color: #687672;
    color: #dee9eb;
    font-family: "Montserrat",sans-serif;
    width: 100%;
    padding: 1em 0;
}

.row {
    max-width: 930px;
    margin: 0 auto 25px;
}
.row::after { 
    content: ""; 
    clear: both;
    display: block;
}
.col {
    float: left;
    margin-left: 4%;
}
.col:first-child {margin-left: 0;}
.harf {width: 40%;}
.quarter {width: 25%;}
.clear {
    clear: both;
}

.button {
    background-color: #2096ba;
    border-bottom: medium none;
    border-radius: 0.5em;
    color: #fff;
    cursor: pointer;
    display: inline-block;
    line-height: 1.7;
    margin: 20 0 2em;
    padding: .6em 2em;
    position: relative;
    text-decoration: none;
}

main a.button { color: #fff; }

main a.button:hover,
main a.button:active { text-decoration: none; }

.votearea {
    border-top: 4px dotted #CCC;
}

/* 画像の背景フレーム */
.frame {
    background-color:#f4e7d7;
    border: 2px solid #f3d9ba;
    padding: 10px 8px 0;
    margin: 14px 14px 20px;
}

.price {
    color:#d73a31;
    font-size:2em;
}

/* レスポンシブ対応 */
@media screen and (max-width: 599px) {
    .col {
        float: none; 
        margin-left: 0; 
        width: auto; 
    }
    .row {
        padding: 0 8px; 
    }
}

(7)画像の拡大表示時の装飾をするCSSファイル

  1. https://github.com/lokesh/lightbox2/releases から「Source code(zip)」をダウンロードして、適当な場所(デスクトップなど)にフォルダを解凍する。
  2. フォルダ内の「src > css」から「lightbox.css」ファイルをコピーし、保存用のcssフォルダにペーストする。

(8)画像の拡大表示をするjsファイル

  • (7)で解凍したフォルダ内の「src > js」から「lightbox.js」ファイルをコピーし、保存用のjsフォルダにペーストする。

(9)画像の拡大表示で使う画像ファイル

  • (7)で解凍したフォルダ内の「src > images」から「close.png」「loading.gif」「next.png」「prev.png」の各ファイルをコピーし、保存用のimagesフォルダにペーストする。

2.ファイルのアップロード

(1)FC2ホームページに登録

  1. https://fc2.com/ でアカウントを取得する。
  2. http://web.fc2.com/ の「特徴・機能一覧」から「今すぐ無料で始める」を選び、好きなアカウント名で登録する。
  3. 登録完了後、「設定 > FTP設定」の画面で、「FTP接続ロック」は「ロックしない」を選択し、その下の「ホスト名(ホストアドレス)」「ユーザー名」「現在のFTPパスワード」はメモしておく。

(2)FileZilla Clientの取得

f:id:shiisuke1229:20191201212233p:plain

(3)FileZilla Client と FC2ホームページ を接続する

  1. FileZilla Clientを開き、「ファイル > サイトマネージャー > 新しいサイト」をクリックする。
  2. ファイル名を好きな名前に変更する。
  3. 「ログオンの種類」は「通常」を選択し、「ホスト」「ユーザ」「パスワード」は(1)でメモした内容を入力する。
  4. 「接続」をクリックする。

(4)ファイルをアップロードする

  1. 今回作成した保存用フォルダを FileZilla Client の左側のエリアで開く。
  2. フォルダの中身を全て選択して、右側のエリアにドラック&ドロップする。

f:id:shiisuke1229:20191201214838p:plain

3.Webサイトへのアクセス

  • 2(1)でメモしたホスト名のWebサイトにアクセスし、イメージ通りのサイトが出来上がっていることを確認する。

<デザイン候補一覧ページ>

デザイン候補一覧ページ

<デザイン候補詳細ページ01>

デザイン候補詳細ページ

<投票フォーム>

投票フォーム

Ⅵ.感想

  • 最初は「単純なHTMLのサイトを作れればいいや」という気持ちで始めましたが、好きなデザインや機能を追加できるのが楽しくて、最終的に凝ったものが出来上がりました(笑)
  • 私はもともと「HTML/CSSとは」「タグの見方」といった基本も理解できていませんでしたが、湊川あいさんの本のおかげでほとんど詰まることなく進められました(^^)
  • 今後はPHPなども触っていきたいと思います!

Ⅶ.参考書籍

【JAWS-UG勉強会初参加!!】「JAWSなアウトプットのススメ!」レポート

f:id:shiisuke1229:20191028203428j:plain:w200

先週10/24(木)、JAWS-UG初心者支部主催のイベント#20 に参加してきました。今回のテーマは「AWSに関するインプットを増やすための、上手なアウトプットの方法」。スピーカーにはJAWS-UGで活躍されている方々が揃い、実体験に基づく中身の濃いお話をしていただきました ♪ ここでは 2つのセッションの内容を共有します。

セッション①:AWS流アウトプットの秘訣 AWSJ 舘岡 守さん

speakerdeck.com

まずはじめに、「なぜアウトプットが必要か」についてお話しいただきました。アウトプットをすると周囲から何かしらの反応(=フィードバック)があり、「そうしたフィードバックを通じて、自分が知らないことを教えてくれる人と出会える」という点を強調されていて、アウトプットすることのメリットがスッと頭に入ってきました。

次は、「AWSの中でのインプットとアウトプットの取組み」の紹介。特に印象的だったのは、「社内で勉強会がゲリラ的に発生し、その内容をAmazon WorkDocsなどで共有・指摘・修正しあっている」という点です。このように日常的にアウトプットする文化は自分の周りでもぜひ育みたい!と思いました。

最後は、今後に向けたアドバイスや応援メッセージなど。「オススメのアウトプットはLT(ライトニングトーク)。資料と顔を覚えてもらえる上に、その場でフィードバックをもらえる」「No Output, No Life. Let's Enjoy OUTPUT!!」「とりあえずやってみて、怒られたら謝る」などのお話を聞いて、すぐにでもLTしたい気持ちになりました(^▽^)

セッション②:LTのコツと心得 森川晃さん(@ariaki4dev)

speakerdeck.com

最初の話題は、「良質なインプットを行うためのスタンス」について。アウトプットをするときは「知っている内容」を伝えるだけでなく、「学習した内容」を補って伝えると、より多くの学びが得られるとのこと。「アウトプットを『学ぶため』に使おう」というメッセージが印象的でした。

そして次は、「何をアウトプットのテーマとするか」について。私もよく「ブログに何を書こう?」と悩むのですが、それに対する森川さんの答えはシンプルで、「自分が過去につまずいたことや悩んだことを書けばよい」。そんな感じの内容で良いんだ!と知って気持ちが軽くなりました(^^)

最後は、LTをするときの準備について。テーマ設定~推敲・練習までの流れがとても分かりやすくて勉強になりました。特に、「スライドの配色は3色~4色」「聞き手が知らない内容は3割まで」というのは初耳で、私もぜひ実践したいと思います。

f:id:shiisuke1229:20191028193528p:plain
ミッフィーの配色は7色しかないとのこと。絵本をたくさん読んでたのに知らなかった...

さいごに

私は最近 Amazon Route 53 を触りはじめたところですが、今回参加してみて、「これからどんどんインプットしてアウトプットしよう」と思いました(^^)

また、JAWS-UGの勉強会は初参加でしたが、所属会社やホスト/ゲストを意識せずお互いがフラットに接したり、ツイートへの反応が早かったり、アウトプットに対する周囲からの賞賛のフィードバックが多かったりと、とっても心地よい雰囲気でした ♪

今後はAWSを触っていきながら、自分の実体験を共有したり、他のJAWS-UGの支部(X-Tech JAWSなど)に参加したりしたいと思います。発表者と運営者の皆様、改めてありがとうございました!

※当日の写真は撮り忘れました(・・;) 会場の様子が見たい方は @taketakekano さんのツイート をご覧ください。

※発表で使用されたスライドはこちら

Gmailでマルチパートメール(HTMLメール+テキストメール)を送信する(Gmail + GAS)

f:id:shiisuke1229:20191022184743p:plain

Ⅰ.きっかけ

先日、会社のテニス部員に大会の案内メールを送った際、よく来るメンバー以外(特に新人さん達)の反応がいまひとつでした。その理由として、「メールを通して大会の魅力を十分アピールできていなかったのではないか?」と考え、もっと魅力を伝えるべくHTMLメールを作ることにしました。

実際にテニス部員に送った大会の案内メール

Ⅱ.前提

  • 会社の規定で「社員同士のメールのやり取りは会社のGmailを使うこと」と定められているため、HTMLメールの作成・送信サービスは使わずにGmailを使って送信する
  • 私が調べた限り、Gmailの通常のメール作成画面ではHTMLメールのコードを書けないようなので、Google App Script(GAS)を利用する
  • HTMLメールのコードは一から書かずにSendGridで用意する
  • HTMLメールが表示できない(または表示不可の設定をしている)受信者がいる場合を想定して、マルチパートメールを作る

※マルチパートメールとは、受信者の環境に合わせてテキストメールとHTMLメールの形式を切り替えられるメールのこと。これを使うことで、受信者の環境がHTMLメール非対応の場合に、メールの本文や画像が表示されなかったり、レイアウトが崩れたり(下図)といった問題を回避できる。1

HTMLメール(左)をテキスト(右)で表示した例。ボタンのリンクが消えており、行間も広い。

Ⅲ.使用したもの

Ⅳ.手順

1.HTMLメールのコードを準備する

(1)SendGridのマーケティングキャンペーン機能を使ってHTMLメールを作成・保存する2

(2)キャンペーン一覧で「Export HTML」を選択し、コードをエクスポートする

f:id:shiisuke1229:20191022211348p:plain

2.テキストメールの内容を準備する

(1)メモ帳などでテキストメールの文面を作成し、1行改行する行の末尾には"\n"を、2行改行する行の末尾には"\n\n"を記載する

f:id:shiisuke1229:20191022213932p:plain

(2)スペース部分を全て削除して1行にまとめる

3.スクリプトを準備する

(1)Googleスプレッドシートを開いてタイトルを付ける(今回は「HTMLメールテスト」とした)

(2)シートのメニューから「ツール > スクリプトエディタ」を選択する

f:id:shiisuke1229:20191022211857p:plain

(3)開いたプロジェクトに名前を付ける(今回は「HTMLメール送信プロジェクト」とした)

f:id:shiisuke1229:20191022212312p:plain

(4)プロジェクトのメニューから「ファイル > 新規作成 > HTML」を選択し、「message」という名前でファイルを作成する

f:id:shiisuke1229:20191022212517p:plain

(5)初めから書かれているコードを削除し、手順1で作成したコードを貼り付ける

f:id:shiisuke1229:20191022213101p:plain

(6)「コード.gs」ファイルに戻り、以下の内容を記述する

function myFunction() {
  //「message.html」ファイルの中身を取得
  var html = HtmlService.createHtmlOutputFromFile("message").getContent();
  
  MailApp.sendEmail({
    to: 'xxxx@xxxx', //宛先メールアドレスを指定
    subject: '【テニス部】〇〇テニス大会のお知らせ', //件名を指定
    name: 'しいすけ', //差出人名を指定
    //テキストパートの本文を記述
    body: 'テニス部の皆様\n\nお疲れ様です。〇〇です。\n\n〇月〇日(〇) に、△△テニス大会が開催されます\n初心者・経験者別でトーナメントが行われ、成績上位チームには豪華賞品もあります。\n\n練習の成果を試す良い機会ですので、みなさま奮ってご参加ください。\n\n=============================\n■日時:2019年〇月〇日(〇) 9時~17時\n■場所:〇〇テニスクラブ\n■参加費:無料\n■持ち物:昼食\n■その他:終了後に懇親会あり\n=============================\n\n参加を希望される方は、以下への回答をお願いいたします。\nhttp://xxxxxxxxxxxxxxx\n\nどうぞよろしくお願いします。',
    //HTMLパートの本文を指定 
    htmlBody: html 
  });
}

(7)メニューの「ファイル > 保存」または「Ctrl + S」(Macの場合は command + S)で保存する

4.スクリプトを実行する

(1)下図の赤枠2か所の文字が一致していることを確認(一致していなければプルダウンで選択)して、メニューの「実行 > 関数を実行」または「▶ボタン」または「Ctrl + R」(Macの場合は command + R)を押す

f:id:shiisuke1229:20191022215626p:plain

(2)「承認が必要です」というダイアログが表示されるので、「承認を許可」→自分のアカウントを選択→「詳細 > HTMLメール送信プロジェクト(安全ではないページ)に移動」→「許可」という手順を踏む(初回実行時のみ)3

(3)メールが届き、HTMLメールを確認できる環境であればHTMLメールが、そうでない環境であればテキストメールが表示されていることを確認する4

実際に届いたHTMLメール
実際に届いたテキストメール

Ⅴ.感想

私はデザインセンスがないので、GASでのメール送信よりもHTMLメールのデザインの方がずっと大変でした💦しかし、先日送ったメールと今回作ったメールを比べた時に、今回のメールの方が見栄えが断然良かったので、頑張って作った甲斐があったなと思いました(^^) 今後、実際にテニス部員に送ってみて反応を確かめたいです。

※今回はお試しということでメールの宛先数を1件としていましたが、実際に使う場合には複数の宛先に送る方法を考える必要があります。

Ⅵ.参考記事


  1. https://www.hai2mail.jp/column/2018/1204.php を参照

  2. SendGridを使ったメールの作成方法は https://sendgrid.kke.co.jp/blog/?p=7097 を参照

  3. 詳細な手順は https://tonari-it.com/gas-script-approval/ を参照

  4. テキストメールの表示確認にはThunderbirdを利用した。設定は https://web-atelier-midori.com/blog/%E3%82%BB%E3%82%AD%E3%83%A5%E3%83%AA%E3%83%86%E3%82%A3/2137/ を参照