第1回 Webフロントエンド開発勉強会 〜ToDoアプリ作成〜


第1回「Webフロントエンド開発勉強会」では、[ToDoアプリ作成](#todo%E3%82%A2%E3%83%97%E3%83%AA%E4%BD%9C%E6%88%90) を実装することを目的とし、そのために必要な HTML、CSS、JavaScript の説明をしていきます。

また、JavaScript は ES5 を対象とし、第2回で ES6 にも少し触れる予定です。

# 環境構築

今回の開発で作成するファイルは HTML、CSS、JavaScriptとなり、いずれもテキスト形式のためお好みのテキストエディタで編集できますが、今回は Visual Studio Code を使って説明します。
こちらの記事を参考に環境の構築をしてください。

[Visual Studio Code でフロントエンドの開発環境を構築してデバッグする](https://qiita.com/C3REVE/items/273646ad028e98758e70)

# HTML
HTML は Webページの構成をタグと言われるパーツで組み立てる役割をします。

タグは基本的に`<タグ名></タグ名>`のように開始タグと終了タグで他のタグを囲うことで構成していき、このタグで囲った部分をHTMLの要素（エレメント）と言います。

タグ名によって役割が異なり、よく使用するタグ名は以下があります。

- `<p>` 段落を作る
- `<h1>` ～ `<h6>` 見出しを作る
- `<a>` リンクを作る
- `<ul>` `<ol>` `<li>` リストを作る
- `<br>` 改行する
- `<input>` 入力要素を作る
- `<img>` 画像を表示する
- `<span>` １行のインライン要素を作る
- `<div>` 複数行のブロック要素を作る

その他のタグは以下を参照してください。
[HTMLタグリファレンス](http://www.htmq.com/html/indexm.shtml)

タグによっては、終了タグを持たないものも存在し、上記の場合、`<input>`、`<img>` は終了タグを持ちません。

タグには**属性**と呼ばれる情報を持つことができ、以下の場合、`h1` がタグ名、`id`、`class`、`name`が属性になります。

```html
<h1 id="p1" class="p01" name="p001" > Contents </h1>
```

`id` と `class` は後で説明する CSS で指定することができ、`id` はドキュメント内で一意、`class` はドキュメント内で複数定義できます。

よく使用される属性には以下があります。

- `title` 要素の補足情報（ツールチップ）を指定する
- `style` 要素に直接スタイルシートを適用する
- `disabled` 要素を非活性状態にして入力を無効にする

その他の属性は以下を参照してください。
[HTML 属性リファレンス](https://developer.mozilla.org/ja/docs/Web/HTML/Attributes)

タグは他のタグを入れ子にすることができ、複数のタグを入れ子にしていくことでドキュメントを構成していきます。

```html
<div>
   <p>divの子要素</p>
</div>
```

`<input>`タグは、type属性によって異なる入力形式を作ることができます。

```html
<!-- テキストボックス -->
<input type="text" value="テキスト">

<!-- チェックボックス -->
<input type="checkbox" value="1" checked="checked">

<!-- ラジオボックス -->
<input type="radio" value="1" checked="checked">
```

その他のtypeは以下を参照してください。
[INPUT - HTMLタグリファレンス](http://www.htmq.com/html/input.shtml)

# CSS

CSS は HTML で構成したタグに装飾を行う役割をします。

タグ名、id、class で指定でき、指定したタグの `{ }` 内に 文字色、背景色、フォントサイズ等の style を記述することで装飾します。

- 何もつけないとタグ名指定
- 先頭に `#` を付けると id 指定
- 先頭に `.` を付けると class 指定

```css
/* タグ名で指定 */
h1 {
    /* 文字色を設定 */
    color: #ff0000;
}

/* id で指定 */
#p1 {
    /* 背景色を設定 */
    background-color: #0000ff;
}

/* class で指定 */
.p01 {
    /* フォントサイズを設定 */
    font-size: 12px;
}
```
また、スペースで区切ることで入れ子になった要素を絞って指定することができ、以下は divタグの中の id="p1" の中の class="red" の文字色を赤色に設定します。

```css
div #p1 .red {
    color: red;
}
```

その他の style は以下を参照してください。
[スタイルシートリファレンス](http://www.htmq.com/style/indexa.shtml)

# JavaScript
JavaScript は動的な処理を行う役割をします。

## 変数

JavaScript では、`var 変数名 = 初期値` で変数を定義することができます。
変数の宣言はどの場所でも可能ですが、どこで宣言しても関数の先頭で宣言したことになります。

```js
var num = 1;
```

## 変数型

変数の型は主にプリミティブ型、配列、オブジェクトがあります。

### プリミティブ型
```js
// 数値
var num = 1;

// 文字列
var str = 'One';

// 真偽値
var bool = true;

// null値
var nil = null;

// 未定義
var undef = undefined;
```

変数を初期化しなかった場合、初期値は `undefined` になります。
JavaScript では、`null` と `undefined` は別物として扱われることに注意してください。

文字列は `'シングルクォーテーション'`、`"ダブルクオーテーション"`どちらも使用できますが、後に説明するDOM操作などでダブルクォーテーションを文字列として扱う場合、も実をダブルクォーテーションで囲うとエスケープしなければならないため、シングルクォーテーションで統一することをおすすめします。

文字列は `+` または `concat()` で結合することができます。

```js
var str = 'ABC' + 'DEF';

var str = 'ABC'.concat('DEF');
```

### 配列
配列は `new Array()` で生成できますが、`[]` でも生成できます。
以下の場合 `arr1` と `arr2` は同じ意味になります。

```js
var arr1 = [ 'one', 'two' ];

var arr2 = new Array();
arr2.push('one');
arr2.push('two');
```

### オブジェクト
同様にオブジェクトも `new Object()` で生成できますが、`{}` でも生成できます。 
以下の場合 `obj1` と `obj2` は同じ意味になります。

```js
var obj1 = { one: 1, two: 2 };

var obj2 = new Object();
obj2['one'] = 1;
obj2.two = 2;
```

# 条件文

JavaScript の条件文は `if else`, `switch case` が利用できます。

## if else文

条件式は `if` `else` で判定できます。
`==` は 型が異なっても真として判定しますが、`===` は型まで一致しなければ真と判定しません。

```js
var num = '1';

if (num == 1) {
    console.log('num は 1 です');
} else {
    console.log('num は 1 ではありません');
}

// 出力結果
// num は 1 です
```

```js
var num = '1';

if (num === 1) {
    console.log('num は 1 です');
} else {
    console.log('num は 1 ではありません');
}

// 出力結果
// num は 1 ではありません
```

JavaScript では、`0`, `''`, `null`, `undefined` を評価した場合は `false` の扱いとなることに注意してください。

```js
var val;

val = 0;
if (val) {
    console.log('0 です');
}

val = '';
if (val) {
    console.log('空文字 です');
}

val = null;
if (val) {
    console.log('null です');
}

val = undefined;
if (val) {
    console.log('undefined です');
}

// 何も出力されない
```

以下のように、入力された値を返す関数 `inputValue()` の戻り値を評価するとき、`0` が入力された場合は「値が入力されていません」が表示されてしまいます。

```js
var value = inputValue();

// 入力チェック
if (!value) {
    console.log('値が入力されていません');
}
```


## switch文

C言語やJavaのような switc / case 文を利用することもできます。

```js
var num = 1;

switch (num) {
    case 1:
        console.log('1です');
        break;
    case 2:
        console.log('2です');
        break;
    default:
        console.log('それ以外です');
        break;
}

// 出力結果
// 1です
```

# 繰り返し文

JavaScript の繰り返し文は、`for` `while` `do while` で行います。

## for文

`for ([初期化式]; [条件式]; [加算式])` で値を加算していき条件の値の間、繰り返し処理をします。
`変数名 ++` は変数に +1 した値を設定します（インクリメント）

```js
var step;
for (step = 0; step < 3; step ++) {
    // 値が 0 から 2 まで計 3 回実行される
    console.log(step);
}

// 実行結果
// 0
// 1
// 2
```

## for - in 文

`for ([変数] in [配列 or オブジェクト])` で配列の場合はインデックス、オブジェクトの場合はキーを1件ずつ取り出します。

```js
var arr = [ 'A', 'B', 'C' ];
for (var index in arr) {
    console.log(index, arr[index]);
}

// 実行結果
// 0 A
// 1 B
// 2 C

var obj = { a: 'A', b: 'B', c: 'C' };
for (var key in obj) {
    console.log(key, obj[key]);
}

// 実行結果
// a A
// b B
// c C
```

## while文 do while文

`while (条件式)` `do ... while(条件式)` で条件式に一致する間、繰り返し処理をします。

```js
var n = 1;
while (n <= 3) {
    console.log(n);
    n ++;
}

// 実行結果
// 1
// 2
// 3

var i = 1;
do {
    console.log(i);
    i ++;
} while (i <= 3);

// 実行結果
// 1
// 2
// 3
```

# 関数

関数は `function 関数名(引数) { }` で定義します。
戻り値を返す場合は `return` で値を返します。

```js
// 関数を定義する
function increment(num) {
    // 引数の数値に 1 を足した数値を返す
    return num + 1;
}

// 関数を呼び出す
var ret = increment(2);

// 結果をコンソールに出力
console.log(ret);

// 出力結果
// 3
```

関数は HTML から呼び出すことができます。
以下はボタンをクリックしたときに呼び出す例で、クリックしたときに呼び出す `onclick` や、値を変更したときに呼び出す `onchange` 等があります。

```html
// ボタンをクリックしたときに関数を呼び出す
<button onclick="increment()">Click me!</button>

// チェックボックスのチェック状態を変更したときに関数を呼び出す
<input type="checkbox" onchange="increment()">
```

### 無名関数

JavaScript では、関数名を指定しない無名関数を定義できます。
関数の引数としてコールバック関数を渡す場合等に利用できます。

以下は、JavaScript の標準関数 `setTimeout()` を呼び出す例です。
第二引数で渡した時間（ミリ秒）が経過後に、第一引数で渡した無名関数が呼ばれます。

```js
setTimeout(function () {
    console.log('3秒経過しました');
}, 3000);
```

# ダイアログ表示

JavaScriptには、ユーザーに対して警告を表示したり、確認や入力のダイアログを表示する関数が用意されています。

## alert関数



```js
alert('アラートです');
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/158277/7303afe6-eb84-de0c-26f0-03a9c6fdad5b.png)


## confirm関数



```js
var ret = confirm('確認です');

if (ret) {
    console.log('[OK]しました');
} else {
    console.log('[キャンセル]しました');
}
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/158277/4ff1e62c-71ed-696f-5147-c5310b17fc4d.png)

## prompt関数

```js
var ret = prompt('入力してください', '初期値');

if (ret) {
    console.log(ret + ' が入力されました');
} else {
    console.log('[キャンセル]しました');
}
```

![image.png](https://qiita-image-store.s3.amazonaws.com/0/158277/2649c43c-4295-27cd-9506-9d5c422e6137.png)


# DOM 操作

DOM(document Object Model)は、HTMLのドキュメントにJavaScriptからアクセスするための仕組みで、DOMを操作することで、HTMLの属性を変更したり、要素を追加したりすることができます。

## 要素を取得する

DOMでHTMLの要素を取得するには、主に以下の方法があります。

- id を指定して取得する `document.getElementById()`
- name を指定して取得する `document.getElementsByName()`
- class を指定して取得する `document.getElementsByClassName()` *※IE9以上*
- タグ名を指定して取得する `document.getElementsByTagName()`
- CSSセレクタを指定して最初の１件を取得する `document.querySelector()` *※IE8以上*
- CSSセレクタを指定して全て取得する `document.querySelectorAll()` *※IE8以上*

以下は取得のサンプルですが、`<script src="main.js"></script>` を headタグ内に記述した場合、headタグはドキュメントを読み込む前に実行されてしまうため、DOMで要素を取得することができません。
これを回避するために bodyタグの後に scriptタグを記述するようにします。

*※他によく使われる手法として即時関数を利用する方法もありますが、ここではDOMの説明のため bodyタグの後に記述する方法を利用します*

```html:index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>DOM</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
    <p id="p1" name="p01" class="odd">1</p>
    <p id="p2" name="p02" class="even">2</p>
    <p id="p3" name="p03" class="odd">3</p>
    <p id="p4" name="p04" class="even">4</p>
</body>
<script src="main.js"></script>
</html>
```

```js:main.js
// idを指定して取得
var element = document.getElementById('p1');
console.log(element.innerHTML);

// 出力結果
// 1

// nameを指定して取得
var elements = document.getElementsByName('p02');
for (var i = 0; i < elements.length; i ++) {
    console.log(elements[i].innerHTML);
}

// 出力結果
// 2

// classを指定して取得
var elements = document.getElementsByClassName('even');
for (var i = 0; i < elements.length; i ++) {
    console.log(elements[i].innerHTML);
}

// 出力結果
// 2
// 4

// タグ名を指定して取得
var elements = document.getElementsByTagName('p');
for (var i = 0; i < elements.length; i ++) {
    console.log(elements[i].innerHTML);
}

// 出力結果
// 1
// 2
// 3
// 4

// セレクタを指定して１つ取得
var element = document.querySelector('#p1');
console.log(element.innerHTML);

// 出力結果
// 1

// セレクタを指定して複数取得
var elements = document.querySelectorAll('.odd');
for (var i = 0; i < elements.length; i ++) {
    console.log(elements[i].innerHTML);
}

// 出力結果
// 1
// 3
```

document全体からではなく、変数にあるDOMからその子要素を取得することもできます。

```js
var body = document.getElementsByTagName('body');
var p1 = body.getElementById('p1');
```

`children`で直下の子要素を参照することもできます。

```js
var div = document.getElementById('contents');
console.log(div.children);
```

## 要素を変更する

取得した要素に対して動的に class、属性、値 等を設定することができ、主に以下の方法で要素の変更ができます。

- `className` classを取得/設定する
- `setAttribute()` 属性を設定する
- `removeAttribute()` 属性を削除する
- `value` inputタグの値を取得/設定する

```html:index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Page Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="main.css" />
</head>
<body>
    <input type="text" id="inputText" value="">
    <input type="checkbox" id="inputCheckbox" onchange="changeCheckbox()">
    <div id="message"></div>
</body>
<script src="main.js"></script>
</html>
```

```js:main.js
function changeCheckbox() {

    var inputText = document.getElementById('inputText');
    var checkbox = document.getElementById('inputCheckbox');
    var message = document.getElementById('message');

    // チェックボックスがチェックされた
    if (checkbox.checked) {

        // class を設定する
        inputText.className = 'checked';
        
        // 属性を設定する
        inputText.setAttribute('style', 'color: red;');
    
        // 値を設定する
        inputText.value = 'チェックしています';

        // 子要素を設定する
        message.innerHTML = 'チェックしました';

    } else {
        inputText.value = 'チェックしていません';
        inputText.removeAttribute('style');
        message.innerHTML = 'チェックを外しました';
    }
}
```

### チャレンジ

チェックボックスがオンのときにテキストボックスの入力を無効にしてみましょう！

<details><summary>答え</summary><div>

`setAttribute()` または `disabled` を直接設定すると要素を無効にできます。

```js
// 入力を無効にする
inputText.setAttribute('disabled', true);
inputText.disabled = true;

// 入力を有効にする
inputText.removeAttribute('disabled');
inputText.disabled = false;
inputText.disabled = '';
```
</div></details>

## 要素を作成する

`document.createElement()` の引数にタグ名を指定することで新しい要素を作成ができ、追加したい親要素に`appendChild()`することで要素を追加できます。
要素ではなくテキストを追加する場合は `document.createTextNode()` または `innerHTML` `innerText` で追加できます。

```js:main.js
function changeCheckbox() {

    var checkbox = document.getElementById('inputCheckbox');
    var message = document.getElementById('message');

    // 要素を作成
    var p = document.createElement('p');
    if (checkbox.checked) {
        p.appendChild(document.createTextNode('チェックしました'));
        // 以下でもOK
        p.innerText = 'チェックしました';
    } else {
        p.appendChild(document.createTextNode('チェックを外しました'));
        // 以下でもOK
        p.innerText = 'チェックを外しました';
    }

    // messageの子要素に追加
    message.appendChild(p);
}
```

また、`innerHTML` で子要素にタグを追加することもできます。

```js
    var p = document.createElement('p');
    p.innerHTML = '<span>チェック</span>しました';
```

### チャレンジ

「チェックしました」「チェックを外しました」の 「チェック」 の文字を `<span>` で囲ってフォント色を青色にしましょう！

<details><summary>答え</summary><div>

`innerHTML` または `createElement()` した要素を `appendChild()` でタグを作成できます。

```js
var p = document.createElement('p');
if (checkbox.checked) {
    p.innerHTML = '<span style="color: blue;">チェック</span>しました';
} else {
    p.innerHTML = '<span style="color: blue;">チェック</span>を外しました';
}
```

または

```js
var p = document.createElement('p');

var span = document.createElement('span');
span.innerHTML = 'チェック';
span.setAttribute('style', 'color: blue;');
p.appendChild(span);

if (checkbox.checked) {
    p.appendChild(document.createTextNode('しました'));
} else {
    p.appendChild(document.createTextNode('外しました'));
}
```

</div></details>


## イベントリスナーを追加する

取得した要素や作成した要素にに対して `addEventListener()` でイベントリスナーを追加できます。
第一引数には `click`, `change` 等のイベント名、第二引数にはイベント発生時に呼ばれる関数を渡します。

```js:main.js
function changeCheckbox() {

    var checkbox = document.getElementById('inputCheckbox');
    var message = document.getElementById('message');

    // 要素を作成
    var p = document.createElement('p');
    if (checkbox.checked) {
        p.innerHTML = 'チェックしました';
    } else {
        p.innerHTML = 'チェックを外しました';
    }

    // イベントリスナーを追加
    p.addEventListener('click', function(event) {
        alert('クリックしました');
    });

    // messageの子要素に追加
    message.appendChild(p);
}
```

その他のイベントは以下を参照してください。
[イベントリファレンス](https://developer.mozilla.org/ja/docs/Web/Reference/Events)

## 要素を削除する

DOMを格納した変数に対して `remove()` することで、要素を削除できます。

```js:main.js
function changeCheckbox() {

    var checkbox = document.getElementById('inputCheckbox');
    var message = document.getElementById('message');

    // 要素を作成
    var p = document.createElement('p');
    p.innerHTML = checkbox.checked ? 'チェックしました' : 'チェックを外しました';

    // イベントリスナーを追加
    p.addEventListener('click', function(event) {
        // 要素を削除する
        p.remove();
    });

    // messageの子要素に追加
    message.appendChild(p);
}
```

# ToDoアプリ作成

以下の HTMLをベースに各ステップを実装して ToDoアプリを作成してください。

```html:index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>TODO APP</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" type="text/css" media="screen" href="main.css" />
    <script src="main.js"></script>
</head>
<body>
    <h1>Todo</h1>
    <input type="text" id="inputTask">
    <button onclick="addTask()">追加</button>
    <ul id="taskList"></ul>
</body>
</html>
```

### Step1. タスク追加
`inputTask` に入力したタスクを `taskList` に追加して、`inputTask` の値をクリアしてください。

![step1.gif](https://qiita-image-store.s3.amazonaws.com/0/158277/fab16634-83d9-9a0d-f56d-203566524b71.gif)

<details><summary>答え</summary><div>

```js:main.js
function addTask() {
    // 入力した要素を取得
    var inputTask = document.getElementById('inputTask');

    // タスクを作成
    var task = document.createElement('li');
    task.innerHTML = inputTask.value;

    // タスクリストにタスクを追加
    document.getElementById('taskList').appendChild(task);

    // 入力したタスクをクリア
    inputTask.value = '';
}
```
</div></details>

### Step2. 入力チェック
タスクが入力されていない状態で追加した場合に「タスク名を入力してください」を表示して以降の処理を中止してください。

![step2.gif](https://qiita-image-store.s3.amazonaws.com/0/158277/2c991f0f-116e-6796-e450-1eacfb221bb9.gif)

<details><summary>答え</summary><div>

```js:main.js
function addTask() {
    // 入力した要素を取得
    var inputTask = document.getElementById('inputTask');

    // 入力チェック
    if (inputTask.value.length <= 0) {
        alert('タスク名を入力してください');
        return;
    }

    // タスクを作成
    var task = document.createElement('li');
    task.innerHTML = inputTask.value;

    // タスクリストにタスクを追加
    document.getElementById('taskList').appendChild(task);

    // 入力したタスクをクリア
    inputTask.value = '';
}
```
</div></details>

### Step3. 追加確認
タスクを追加する前に確認ダイアログを表示してください。

![step2.gif](https://qiita-image-store.s3.amazonaws.com/0/158277/28df8e01-4086-b3b1-4c1e-2288e30e89b4.gif)

<details><summary>答え</summary><div>

```js:main.js
function addTask() {
    // 入力した要素を取得
    var inputTask = document.getElementById('inputTask');

    // 入力チェック
    if (inputTask.value.length <= 0) {
        alert('タスク名を入力してください');
        return;
    }

    // 確認ダイアログを表示
    if (confirm(inputTask.value + ' を追加しますか？')) {

        // タスクを作成
        var task = document.createElement('li');
        task.innerHTML = inputTask.value;

        // タスクリストにタスクを追加
        document.getElementById('taskList').appendChild(task);

        // 入力したタスクをクリア
        inputTask.value = '';
    }
}
```
</div></details>

### Step4. チェックボックス表示
タスクにチェックボックスを表示してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/158277/45af5c5a-c3c4-5424-021f-636501bf656a.png)

<details><summary>答え</summary><div>

```js:main.js
function addTask() {
    // 入力した要素を取得
    var inputTask = document.getElementById('inputTask');

    // 入力チェック
    if (inputTask.value.length <= 0) {
        alert('タスク名を入力してください');
        return;
    }

    // 確認ダイアログを表示
    if (confirm(inputTask.value + ' を追加しますか？')) {

        // タスクを作成
        var task = document.createElement('li');
        task.innerHTML = '<label><input type="checkbox">' + inputTask.value + '</label>';

        // タスクリストにタスクを追加
        document.getElementById('taskList').appendChild(task);

        // 入力したタスクをクリア
        inputTask.value = '';
    }
}
```
</div></details>

### Step5. タスク完了
チェックボックスにチェックを付けたタスクを灰色で表示してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/158277/ba6c54a0-054c-f251-3aa5-1b607c14b461.png)

<details><summary>答え</summary><div>

```js:main.js
function addTask() {
    // 入力した要素を取得
    var inputTask = document.getElementById('inputTask');

    // 入力チェック
    if (inputTask.value.length <= 0) {
        alert('タスク名を入力してください');
        return;
    }

    // 確認ダイアログを表示
    if (confirm(inputTask.value + ' を追加しますか？')) {

        // タスクを作成
        var task = document.createElement('li');
        task.innerHTML = '<label><input type="checkbox">' + inputTask.value + '</label>';

        // チェックボックスの changeイベントを設定
        var checkbox = task.getElementsByTagName('input')[0];
        checkbox.addEventListener('change', function(event) {
            task.className = checkbox.checked ? 'done-task' : '';
        });

        // タスクリストにタスクを追加
        document.getElementById('taskList').appendChild(task);

        // 入力したタスクをクリア
        inputTask.value = '';
    }
}
```

```css:main.css
.done-task {
    color: lightgray;
}
```

</div></details>

### Step6. タスク削除
タスクに削除ボタンを追加して削除ボタンをクリックしたタスクを削除してください。

![step5.gif](https://qiita-image-store.s3.amazonaws.com/0/158277/dd395053-37e4-33e4-3d5c-0c77958df729.gif)

<details><summary>答え</summary><div>

```js:main.js
function addTask() {
    // 入力した要素を取得
    var inputTask = document.getElementById('inputTask');

     // 入力チェック
     if (inputTask.value.length <= 0) {
        alert('タスク名を入力してください');
        return;
    }

    // 確認ダイアログを表示
    if (confirm(inputTask.value + ' を追加しますか？')) {

        // タスクを作成
        var task = document.createElement('li');
        task.innerHTML = '<label><input type="checkbox">' + inputTask.value + '<button>削除</button></label>';

        // チェックボックスの changeイベントを設定
        var checkbox = task.getElementsByTagName('input')[0];
        checkbox.addEventListener('change', function(event) {
            task.className = checkbox.checked ? 'done-task' : '';
        });

        // 削除ボタンの clickイベントを設定
        var button = task.getElementsByTagName('button')[0];
        button.addEventListener('click', function(event) {
            task.remove();
        });

        // タスクリストにタスクを追加
        document.getElementById('taskList').appendChild(task);

        // 入力したタスクをクリア
        inputTask.value = '';
    }
}
```

```css:main.css
.done-task {
    color: lightgray;
}

#taskList button {
    margin-left: 16px;
}
```

</div></details>

### Extra1. タスク数表示
現在のタスクの個数を表示してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/158277/1571ed16-3e06-234b-9203-801d7cc46563.png)

<details><summary>答え</summary><div>

```js:main.js
function addTask() {
    // 入力した要素を取得
    var inputTask = document.getElementById('inputTask');

    // 入力チェック
    if (inputTask.value.length <= 0) {
        alert('タスク名を入力してください');
        return;
    }

    // 確認ダイアログを表示
    if (confirm(inputTask.value + ' を追加しますか？')) {

        var taskInfo = document.getElementById('taskInfo');
        var taskList = document.getElementById('taskList');

        // タスクを作成
        var task = document.createElement('li');
        task.innerHTML = '<label><input type="checkbox">' + inputTask.value + '<button>削除</button></label>';

        // チェックボックスの changeイベントを設定
        var checkbox = task.getElementsByTagName('input')[0];
        checkbox.addEventListener('change', function(event) {
            task.className = checkbox.checked ? 'done-task' : '';
        });

        // 削除ボタンの clickイベントを設定
        var button = task.getElementsByTagName('button')[0];
        button.addEventListener('click', function(event) {
            task.remove();
            // タスク情報を更新
            taskInfo.innerHTML = taskList.children.length + '個のタスク';
        });

        // タスクリストにタスクを追加
        taskList.appendChild(task);

        // タスク情報を更新
        taskInfo.innerHTML = taskList.children.length + '個のタスク';

        // 入力したタスクをクリア
        inputTask.value = '';
    }
}
```
</div></details>

### Extra2. タスク数、完了数表示
現在のタスクの個数と完了タスクの個数を表示してください。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/158277/2a3fe9bb-7be5-1cd8-c3bc-73fc8f3f9cb1.png)

<details><summary>答え</summary><div>

```js:main.js
function addTask() {
    // 入力した要素を取得
    var inputTask = document.getElementById('inputTask');

    // 入力チェック
    if (inputTask.value.length <= 0) {
        alert('タスク名を入力してください');
        return;
    }

    // 確認ダイアログを表示
    if (confirm(inputTask.value + ' を追加しますか？')) {

        var taskList = document.getElementById('taskList');

        // タスクを作成
        var task = document.createElement('li');
        task.innerHTML = '<label><input type="checkbox">' + inputTask.value + '<button>削除</button></label>';

        // チェックボックスの changeイベントを設定
        var checkbox = task.getElementsByTagName('input')[0];
        checkbox.addEventListener('change', function(event) {
            task.className = checkbox.checked ? 'done-task' : '';
            // タスク情報を更新
            updateInfo(taskList);
        });

        // 削除ボタンの clickイベントを設定
        var button = task.getElementsByTagName('button')[0];
        button.addEventListener('click', function(event) {
            task.remove();
            // タスク情報を更新
            updateInfo(taskList);
        });

        // タスクリストにタスクを追加
        taskList.appendChild(task);

        // タスク情報を更新
        updateInfo(taskList);

        // 入力したタスクをクリア
        inputTask.value = '';
    }
}

function updateInfo(taskList) {

    var taskInfo = document.getElementById('taskInfo');
    var doneCount = 0;

    for (var i = 0; i < taskList.children.length; i ++) {
        if (taskList.children[i].getElementsByTagName('input')[0].checked) {
            doneCount ++;
        }
    }

    // タスク情報を更新
    taskInfo.innerHTML = taskList.children.length + '個中' + doneCount + '個完了';
}
```
</div></details>


# あとがき

今回は ToDoアプリ作成のために必要なフロントエンドの知識について説明しました。
ここで説明した知識は基本的なものとなりますが、JavaScript はもっと奥が深いです。

[次回](https://qiita.com/C3REVE/items/fb03a4ca57b1fc246ca3) は、iTunes Search API を利用して、 楽曲検索アプリ作成のために必要な JavaScript の知識と、JavaScript を学ぶうえで身に着けておいた方が良い知識について説明します。