ES2015(ES6)なブラウザゲームをつくる-2: 各DOMをjsで生成する

# このページについて
[コンパイルしないES2015(ES6)なブラウザゲーム](https://qiita.com/snakada/items/eb86ed81e0eb28e8d0ea)を作ります。ES2015(ES6)を触り始めたとか、これから触ってみたいとか、HTML/CSS/JSがなんとなくわかるとかいう方向けに、コマンドとか難しいものを使わずに、Chromeで動くコンパイル不要なES2015ゲームを作ろうというのをコンセプトに掲げて数回にわけて連載しているチュートリアル記事です。

# TL;DR

前回は [仕様の確認と設計、基本パーツのHTMLとCSS](https://qiita.com/snakada/items/538067a6cae7d92eb866) ということで、単純なHTML/CSS をコーディングしましたが、そのままでは当然どうにもならないので、基本的なjsに切り分けていきます。

<details>
<summary>シリーズ目次</summary>
- phase0: [はじめに、対象者、使う技術、成果物](https://qiita.com/snakada/items/eb86ed81e0eb28e8d0ea) ([demo](https://snakada.github.io/wallgame/))
- phase1: [仕様の確認と設計、基本パーツのHTMLとCSSを作る](https://qiita.com/snakada/items/538067a6cae7d92eb866) ([demo](https://snakada.github.io/wallgame/phase1/))
- **phase2: 各DOMをjsで生成する** ([demo](https://snakada.github.io/wallgame/phase2/))
- phase3: [プレイヤーを動かせられるようにする](https://qiita.com/snakada/items/d8edefb2b071d96d7893) ([demo](https://snakada.github.io/wallgame/phase3/))
- phase4: プレイヤーが壁に当たらないようにする ([demo](https://snakada.github.io/wallgame/phase4/))
- phase5: プレイヤーを動かすとスコアが増えるようにする ([demo](https://snakada.github.io/wallgame/phase5/))
- phase6: 壁をランダムに生成する ([demo](https://snakada.github.io/wallgame/phase6/))
- phase7: 壁を自動で動くようにする ([demo](https://snakada.github.io/wallgame/phase7/))
- phase8: プレイヤーと壁が衝突するとゲームオーバーにする ([demo](https://snakada.github.io/wallgame/phase8/))
- phase9: ゲームオーバーになったらタイマーを止める ([demo](https://snakada.github.io/wallgame/phase9/))
</details>

# index.htmlファイルを編集する

[前回のindex.html](https://qiita.com/snakada/items/538067a6cae7d92eb866#indexhtml)は一切jsがありませんでしたが、本記事では各DOMをjsで生成することにしますので、それらを読み込むように準備しておきます。修正前と修正後を比較すると、divタグがなくなってjsファイルの読み込みに置き換わっていますね。

```修正前&nbsp;-&nbsp;index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>wallgame</title>
    <link rel="stylesheet" href="./css/app.css">
  </head>
  <body>
    <div id="app">
      <div id="field">
        <div id="player" style="top: 100px; left: 40px;"></div>
        <div class="wall" style="right: 100px;">
          <div class="wallTop" style="height: 150px;"></div>
          <div class="wallBottom" style="height: 50px;"></div>
        </div>
      </div>
      <div id="score">123</div>
      <!--
      <div id="gameover">GAMEOVER<br><span>RETRY</span></div>
      -->
    </div>
  </body>
</html>
```

```修正後&nbsp;-&nbsp;index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>wallgame</title>
    <link rel="stylesheet" href="./css/app.css">
  </head>
  <body>
    <script src="./js/dom.js"></script>
    <script src="./js/player.js"></script>
    <script src="./js/wall.js"></script>
    <script src="./js/score.js"></script>
    <script src="./js/field.js"></script>
    <script src="./js/gameover.js"></script>
    <script src="./js/app.js"></script>
  </body>
</html>
```

<br>

以降、今回新たに追加した各jsを詳しく見ていきます。

# 各DOMをjsで生成する

前の記事では、[基本パーツのHTMLとCSSを作り](https://qiita.com/snakada/items/538067a6cae7d92eb866)ました。パーツである各DOMを扱いやすくするために、本記事ではそれらをjsで生成するように置き換えていきます。

## dom.js

jQueryはDOMやイベント等を操作するのに便利なライブラリですが、本シリーズではブラウザのネイティブなjsで動作させることを目的としているため、今回は使わないことにします。その代わりというわけでもないのですが、少しDOMの操作を便利にするためのユーティリティをまずはじめに用意しておきます。以下のとおりです。このあとの記事でもよく使うものばかりですので、しっかり見ておいてください。

```dom.js
/**
 * idを持ったDOMを生成して返します。
 * @param  {string} id 設定するid
 * @return {object}    生成したDOM
 */
const createDivWithId = id => {
  const $div = document.createElement('div');
  $div.id = id;
  return $div;
};

/**
 * CSSクラス名を持ったDOMを生成して返します。
 * @param  {string} className 設定するCSSクラス
 * @return {object}           生成したDOM
 */
const createDivWithClassName = className => {
  const $div = document.createElement('div');
  $div.className = className;
  return $div;
};

/**
 * DOMにCSSスタイルを付与します。
 * @param {object} $dom     処理対象のDOM
 * @param {string} property 設定するCSSプロパティ名
 * @param {string} value    設定するCSSプロパティ値
 */
const setDomStyle = ($dom, property, value) => $dom.style[property] = value;

/**
 * 親DOMに子DOMを組み込みます。
 * @param {object} $parent 親DOM
 * @param {object} $child  子DOM
 */
const appendDomChild = ($parent, $child) => $parent.appendChild($child);

/**
 * 親DOMに子DOMを複数まとめて組み込みます。
 * @param {object} $parent  親DOM
 * @param {object} children 子DOMの配列
 */
const appendDomChildren = ($parent, children) =>
  children.forEach($child => appendDomChild($parent, $child));

/**
 * DOMにテキストをセットします。
 * @param {object} $dom 処理対象のDOM
 * @param {string} text セットするテキスト
 */
const setDomText = ($dom, text) => $dom.textContent = text;

/**
 * DOMにHTMLをセットします。
 * @param {object} $dom 処理対象のDOM
 * @param {string} html セットするHTML
 */
const setDomHtml = ($dom, html) => $dom.innerHTML = html;
```

## app.js

次に、いわゆるエントリーポイントとなる`app.js`を見てみます。上記で用意しておいた`createDivWithId()`と`appendDomChild()`および`appendDomChildren()`を使います。`$`で始まる変数が登場していますが、それらはあとで実装するDOMを表しています。ここではアプリ本体となる`$app`に子DOM達を組み込んだうえで`body`タグに埋め込むくらいにぼんやり把握していただければ大丈夫です。

```app.js
/**
 * アプリDOMを生成して
 * フィールドDOMとスコアDOMを組み込みます。
 */
const $app = createDivWithId('app');
appendDomChildren($app, [$field, $score, $gameover]);

/**
 * bodyにアプリDOMを組み込みます。
 */
appendDomChild(document.body, $app);
```

## player.js

キーボードでぐりぐり動かすことになるDOMです。壁にぶつかったら駄目な子です。ここでは`$player`として、その位置を仮でセットしています。あとで`$field`に組み込まれます。

```player.js
/**
 * プレイヤーDOMを生成します。
 */
const $player = createDivWithId('player');
setDomStyle($player, 'top',  '100px');
setDomStyle($player, 'left', '40px');
```

## wall.js

プレイヤーの障害物となるDOMです。ここで注目するのは`$wall`という親DOMに対して`$wallTop`と`$wallBottom`という子DOMを組み込んでおくという点です。諸々含んでおいた`$wall`はあとで`$field`に組み込まれます。

```wall.js
/**
 * 壁(枠)DOMを生成します。
 */
const $wall = createDivWithClassName('wall');
setDomStyle($wall, 'right', '100px');

/**
 * 壁(上)DOMを生成します。
 */
const $wallTop = createDivWithClassName('wallTop');
setDomStyle($wallTop, 'height', '150px');

/**
 * 壁(下)DOMを生成します。
 */
const $wallBottom = createDivWithClassName('wallBottom');
setDomStyle($wallBottom, 'height', '50px');


/**
 * 壁(枠)DOMに壁(上)と壁(下)DOMを組み込みます。
 */
appendDomChildren($wall, [$wallTop, $wallBottom]);
```

## score.js

`$score`をつくって、仮の点数`123`をセットしてあります。

```score.js
/**
 * スコアDOMを生成します。
 */
const $score = createDivWithId('score');
setDomText($score, '123');
```

## field.js

`$field`はゲームの舞台(=フィールド、面）となるDOMです。事前に作っておいた`$player`と`$wall`を組み込みます。

```field.js
/**
 * フィールドDOMを生成して
 * プレイヤーDOMと壁(枠)DOMを組み込みます。
 */
const $field = createDivWithId('field');
appendDomChildren($field, [$player, $wall]);
```

## gameover.js

ゲームオーバーになったときに出現する`$gameover`というDOMです。ここまでと様相が違うのは、このDOM専用の`hideGameOver()`という関数を用意しておき、実際に使っているという点です。あとの工程では表示する用の関数`showGamveOver()`も作るのですが、今は要らないので省略しています。

```gameover.js
/**
 * ゲームオーバーDOMを生成します。
 */
const $gameover = createDivWithId('gameover');
setDomHtml($gameover, 'GAMEOVER<br><span>RETRY</span>');

/**
 * ゲームーバー画面を隠す関数。
 */
const hideGameOver = () => setDomStyle($gameover, 'display', 'none');

/**
 * 一旦ゲームオーバー画面は隠しておきます。
 */
hideGameOver();
```

本日は以上です。

ここで提示したコードは https://github.com/snakada/wallgame/tree/phase2 でもご覧いただけます。
また実際の画面は https://snakada.github.io/wallgame/phase2/ でご覧いただけます。

[phace3: プレイヤーを動かせられるようにする](https://qiita.com/snakada/items/d8edefb2b071d96d7893) に続きます。