JavaScriptハンズオン資料

# はじめに
この記事はJavaScript入門を目的としたハンズオンの資料となります。
本記事ではJavaScriptとHTML・CSSを組み合わせた実用的な部分の入門となるよう書くため、HTML・CSS及びJavaScriptの基本的な部分は解説しません。JavaScriptの文法や機能は[JavaScript Primer](https://jsprimer.net/)、HTML・CSSは[HTML](https://developer.mozilla.org/ja/docs/Web/HTML), [CSS](https://developer.mozilla.org/ja/docs/Web/CSS)を見て頂ければと思います。
## 環境
- 最新のGoogle chrome
- テキストエディタ(メモ帳でないのが望ましい)
- Windows

でのコーディング及び実行を想定しています。
# 基本の雛型
## HTMLファイル上に記述する

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Hello world!</title>
  </head>
  <body>
    <p>Hello world!</p>
    <script type="text/javascript">
      // ここにJavaScriptを書きます
    </script>
  </body>
</html>
```

`<script>`はbody内の一番最後に記述します。

## ファイルを分ける
`html`ファイルと`js`ファイルを分けて記述することが出来ます。本記事ではファイルを分けてコーディングします。

```
project_directory
    |
    +- index.html
    +- style.css
    +- index.js
    +- images
        |
        +- hoge.png
        +- fuga.png
        +- piyo.png
```

以上のようなディレクトリ構造を想定した場合、

```html
<body>
  <script type="text/javascript" src="index.js"></script>
</body>
```

とHTMLファイル上に記述するのと同様、body内の一番最後に記述します。

# イベント
以下、

```
project
    |
    +- index.html
    +- style.css
    +- index.js
```

というディレクトリであることを前提に進めていきます。

## ボタンを押してメソッド発火

まずはボタンを押すと、`Hello World`とアラートされるプログラムをつくります。

```html
<button onclick="alert('Hello World')">ボタン</button>
```

これでも望む動作をしますが、この後のセクションにスムーズに入るために、別にメソッドを用意して動かしたいと思います。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <input type="button" value="ボタン" onclick="helloWorld()">
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
const helloWorld = function(){
    window.alert("Hello World");
}
```

このように記述して実行してみましょう。ボタンを押すと`Hello World`とアラートされるはずです。

- [use strict](https://jsprimer.net/basic/introduction/)
- [無名関数](https://jsprimer.net/basic/function-declaration/)
- [input](http://www.htmq.com/html/input.shtml)

## onload
ロードが終わったタイミングや特定のDOMが構成し終わったタイミングでメソッドを発火させたいことがあるかもしれません。そういう時は`onload`を使いましょう。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <p>こんにちわ</p>
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
window.onload = function(){
    setTimeout(()=>window.alert("DOMが構成された"), 1000);
}
```

`setTimeout`により、DOMが構成された1秒後にアラートが出ます。……ちょっとわかりにくいですね。

- [window.onload](https://developer.mozilla.org/ja/docs/Web/API/GlobalEventHandlers/onload)
- [setTimeout](https://developer.mozilla.org/ja/docs/Web/API/WindowTimers/setTimeout)

# 取得
## ページ内の要素を取得
HTML内の要素を取得してみましょう。
DOM内にあるｐタグの数を取得してみます。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <p>a</p>
        <p>b</p>
        <p>c</p>
        <input type="button" value="get_p_num" onclick="tagGetFunc()">
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
const tagGetFunc = function(){
    const pNum = document.getElementsByTagName("p");
    window.alert(pNum.length);
}
```

実行してみましょう。`3`と出たはずです。これは全体の`<p>`タグの数です。

また、`id`や`class`、`name`でも指定して取得できます。
が、ここでは`id`のみ例を載せます。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <p id="hoge">こんにちわ</p>
        <input type="button" value="Hello" onclick="helloWorld()">
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
const helloWorld = function(){
    const hoge = document.getElementById("hoge");
    window.alert(hoge.textContent);
}
```

- [getElementById](https://developer.mozilla.org/ja/docs/Web/API/Document/getElementById)
- [textContent](https://developer.mozilla.org/ja/docs/Web/API/Node/textContent)
- [getElementsByClassName](https://developer.mozilla.org/ja/docs/Web/API/Document/getElementsByClassName)
- [getElementsByClassNameとgetElementByIdの返り値の違い](https://qiita.com/rindarinda5/items/c26dc81fe8cd98992dc1)
- [getElementsByName](https://developer.mozilla.org/ja/docs/Web/API/Document/getElementsByName)

## テキストボックスからの取得
次はボタンを押すと、テキストボックスに入力した文字のアラートが出るコードを書きます。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <input type="text" id="data"><input type="button" value="ボタン" onclick="helloWorld()">
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
const textData = document.getElementById("data");
const helloWorld = function(){
    window.alert(textData.value);
}
```

テキストボックスに文字を入力してボタンを押すと、入力した文字がアラートで出たと思います。

- [inputの属性](https://developer.mozilla.org/ja/docs/Web/HTML/Element/Input#Attributes)

# 制御
## ページ内の要素を書き換え
JavaScriptではHTML要素をイジることが出来ます。
ボタンを押したらHTML上の`<p id="hoge"></p>`内を書き換えるコードを書いてみましょう。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <p id="hoge">こんにちわ</p>
        <input type="button" value="English" onclick="helloWorld()">
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
const helloWorld = function(){
    document.getElementById("hoge").textContent = "Hello!";
}
```

実行してみましょう。`こんにちは` が `Hello!` になったはずです。

ここまでやってきたことを組み合わせてみましょう。
`onload`を用いてDOMが構成されてから`setInterval`で1秒ごとにカウントし、pタグ内の数値を更新します。そしてボタンを押すか、5秒経った時点でカウントが止まるプログラムにしてみます。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <p id="timer">0</p>
        <input type="button" value="stop" onclick="stopCount()">
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
let count = 0;
let timer;
const timerNode = document.getElementById("timer");

const stopCount = function(){
    clearInterval(timer);
    window.alert(count + "[s]");
}

window.onload = function(){
    timer = setInterval(()=>{
        count++;
        timerNode.textContent = count;
        if(count >= 5){
            stopCount();
        }
    }, 1000);
}
```

もっと上手なコードがあると思うんですけど、今はこれ以上思いつきません。思いついて、この記事のことを覚えていたら追記します。
5秒目にアラートが出たときページ上での秒数は`4`となっていますが、アラートを消すとちゃんと`5`になります。

- [setInterval](https://developer.mozilla.org/ja/docs/Web/API/Window/setInterval)
- [clearInterval](https://developer.mozilla.org/ja/docs/Web/API/WindowTimers/clearInterval)
- [setIntervalとclearIntervalの使い方](https://sites.google.com/site/jqueryjavascript/setintervaltoclearintervalno-shii-fang)

## DOMの追加
JavaScriptで途中からDOMを追加してみましょう。まずは末尾に要素を生成します。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <p>これは元からあった要素です</p>
        <input type="button" value="生成" onclick="generate()">
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
const p = document.createElement("p");
p.textContent = "新しく追加したノードです";
const generate = function(){
    document.body.appendChild(p);
}
```

ボタンを押すと初めの一回だけ`<p>新しく追加したノードです</p>`が末尾に追加されます。

- [createElement](https://developer.mozilla.org/ja/docs/Web/API/Document/createElement)
- [appendChild
](https://developer.mozilla.org/ja/docs/Web/API/Node/appendChild)

## DOMを生成後、その要素の情報を表示
次は画像要素を生成して、その画像データをアラートさせてみましょう。
画像は読み込みに時間が掛かります。ですから、`onload`を用いてDOM生成後に画像のデータを表示させてやる必要があります。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
const img = document.createElement("img");
img.onload = function() {
     window.alert("画像の横幅は" + img.width + "\n" + "画像の縦幅は" + img.height);
}
img.src = "http://www.lovelive-anime.jp/otonokizaka/img/member/member09_04.png";
document.body.appendChild(img);
```

`document.createElement("img")`は[Image()](https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/Image)でも可です。

## mapを用いた要素の分割生成
`input`で入力した文字列を`split(",")`で`,`ごとに区切ってリスト化、それを要素一つ一つpタグで出力させてみます。

```index.html
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Hello world!</title>
    </head>
    <body>
        <input type="text" id="data"><input type="button" value="ボタン" onclick="helloWorld()">
        <script type="text/javascript" src="index.js"></script>
    </body>
</html>
```

```index.js
"use strict"
let textData = document.getElementById("data");
const helloWorld = function(){
    textData = textData.value.split(",");
    textData.map((el)=>document.body.appendChild(document.createElement("p")).textContent = el);
}
```

ボタンを押すと初めの一回だけpタグで要素が`,`で分割された部分で改行され表示されます。
- [split](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/split)
- [map](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
- [Arrow Function](https://jsprimer.net/basic/function-declaration/#arrow-function)

# おまけ
コードゴルフを少し載せたいと思います。

***FizzBuzz***

```javascript
fizzBuzz=num=>(num%3?"":"Fizz")+(num%5?"":"Buzz")||num;
console.log(fizzBuzz(15));
// FizzBuzz
```

- [JavaScriptの「&&」「||」について盛大に勘違いをしていた件](https://qiita.com/Imamotty/items/bc659569239379dded55)

***連番のリスト***

```javascript
[...Array(5).keys()].forEach(el=>console.log(el))
/*
0
1
2
3
4
*/
```

- [forEach](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
- [スプレッド構文](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
- [keys](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/keys)


***リスト内の合計***

```javascript
console.log([...Array(10).keys()].map(i=>i+1).reduce((a,b)=>a+b,0));
// 55
```

- [reduce](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce)

# おわりに
よく使いそうだと思ったものをまとめました。ここから色々な技術に繋がればいいなと思います。
## 発展として
- [JavaScript Primer](https://jsprimer.net/)
- [初心者がモチベーション上げながらプログラミングをしてシューティング（っぽい）ゲームを1本作る！](https://lambdalisue.hatenablog.com/entry/2013/12/25/160910)
- [Node.js](https://nodejs.org/ja/)
- [Express](https://expressjs.com/ja/)
- [Vue.js](https://jp.vuejs.org/v2/guide/index.html)
- [React.js](https://ja.reactjs.org/tutorial/tutorial.html)
- [初心者のためのGoogle Apps Scriptプログラミング入門](http://libro.tuyano.com/index2?id=638001)
- [jQuery入門](https://jquerystudy.info/)
- [phina.js](http://phinajs.com/)
- [とりあえず試してみたいって方のための phina.js 入門](http://phiary.me/phinajs-tutorial-try/)
- [phina.js version 0.2](http://phiary.me/phinajs-release-v0-2/)
- [phina.js Tips集](https://qiita.com/alkn203/items/bca3222f6b409382fe20)
- [phina.js チュートリアル集](https://qiita.com/alkn203/items/2150f739d383b4fd5a92)
- [phina.js はみ出し制御](http://phiary.me/phina-js-tips-screen-out-control/)
- [phina.js ジャンプアクション](https://qiita.com/alkn203/items/6e0723f4161767a5728f)
- [phina.js superMethodの使い方](https://qiita.com/Negiwine_jp/items/2fb5258f42a8a401c126)
- [phina.jsでシューティングゲームをつくりました～](http://masayasviel.hatenablog.jp/entry/2018/08/14/155247)