デモページ Formを使ってtweet機能を作成 [JavaScript]

通常、HTML FORMでは、
入力したinputの内容を付加してボタンを押した時に、その情報を送信し、別のページに移動します。

usernameにkei
passwordに123456
と入力して登録ボタンを押してみます。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/82f6e12a-afb0-ba53-e289-db2e2e465c6b.png width = 60%>

hogeというformのaction先に対して、
username=keiとpassword=123456が付加された情報が送信され、別のページに遷移しました。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/7521f43c-f896-adf2-168e-bc9c51c143ff.png width= 60%>

しかし、今回は、JavaScriptを使用し、ページが切り替わるのをやめ、入力された情報を元に自分のページ内で入力された情報を使用したいと思います。

例えば、入力された情報をXのコメントのような形で、
ページ内で出力させるようなformを作成してみようと思います。


## 目次： 
- [Formを作成「HTML ファイル」](#formを作成html-ファイル)
- [イベントを設定「JavaScript ファイル」](#イベントを設定javascript-ファイル)
- [Form設定 ページ内で処理を残すには？「JavaScript ファイル」](#form設定ページ内で処理を残すにはjavascript-ファイル)
- [inputに保存した内容を保存する「HTML ファイル」](#inputに保存した内容を保存するhtml-ファイル)
- [Formに入力された値を取得する「JavaScript ファイル」](#formに入力された値を取得するjavascript-ファイル)
- [取得した値をtweet内容として作成する「JavaScript ファイル」](#取得した値をtweet内容として作成するjavascript-ファイル)
- [tweetの内容を削除する「JavaScript ファイル」](#tweetの内容を削除するjavascript-ファイル)

## Formを作成「HTML ファイル」

h1要素「見出しとして記載。今回はFORM DEMO」
form要素：
中身1：input要素、ユーザー名を作成
中身2：input要素、ツイート風に作成
中身3：button要素

formには、idを設定。
tweetFormとしています。

action先は、今回は使用しない為、/hogeと設定しています。

```html


<body>
    <h1>FORM DEMO</h1>

    <form id="tweetForm" action = "/hoge">
        <input type="text" name="username" placeholder="ユーザー名">
        <input type="text" name="tweet" placeholder="いまどうしてる？">
        <button>ツイートする</button>
    </form>
```
ツイートするというボタンを押した時に、javaScriptが走るように修正していきます。

## イベントを設定「JavaScript ファイル」

Formのイベントでsubmitを使用します。

1. まずは、tweetFormのidを取得してきます。
2. tweetFormに対して、イベントハンドラーを設定します。

```js
const tweetForm = document.querySelector('#tweetForm');
tweetForm.addEventListener('submit', function(e) {
    alert('submit');
})
```

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/f3cf5912-7dbb-c504-f657-ac9998a221e4.png width = 60%> 

一旦、alertを出力させてイベントハンドラーが設定されているかを確認してみました。
問題なく接続されています。

## Form設定ページ内で処理を残すには？「JavaScript ファイル」

今回は、Formのinputに入力した内容を同一画面内に出力する処理を行います。
しかし、formはsubmit（登録ボタンをクリック）されたら、actionに指定されている場所へリクエストを投げるという性質があります。

そのため、formのデフォルトの動きを止める必要があります。

#### どうやってFormのデフォルトの動きを止めるのか？

・actionを削除する。

actionを削除して、input要素を入力して、ツイートするボタンをクリックすると、
画面上では、一見問題がないように見えます。

しかし、自分のページに対してリクエストを投げ、そのページを再表示させているので、リフレッシュしているのと同じ意味になってしまいます。

そのため、せっかく載せた情報が無くなってしまいます。

#### JavaScriptで何ができるのか？

・A. イベントオブジェクトを使用することができる。

イベントオブジェクトに、preventDefault()というメソッドがあります。

これを実行すると、デフォルトの動作を止めることができます。
今回の場合は、Formのsubmitになるので、
そのsubmitの時にリクエストが飛んで、ページが変わることを止める作用があります。

```js
tweetForm.addEventListener('submit', function(e) {
    e.preventDefault();
```

Formがsubmitされるのを止めることができました。

## inputに保存した内容を保存する「HTML ファイル」

次に行いたいこととしては、入力された値を使用してみたいと思います。

そのために、HTMLで置き場所を作成します。

HTML、Formの下に、h2を作成、その下に、ulを作成し、その中に
liで作成されたツイートが入るイメージになります。

```html
<h2>ツイート一覧</h2>

<ul id = "tweets">
<li>入力されたツイート</li>
</ul>

```
1. h2を作成
2. ulを作成
3. ulにidを追加「今回は、tweetsとします。」 

liはJavaScriptのDOMを操作して作成します。

## Formに入力された値を取得する「JavaScript ファイル」

:::note info
やりたいこと：

ツイートボタンをクリックしたときに、
Formに入力された値を取得し、新しい要素をulに追加する。
:::

HTMLのinputのvalue属性を使用する。

inputのvalue属性を使用することで、valueに対応したプロパティを取得することができます。

document.querySelectorAllでinput要素の0番目を取得。
usernameInputのvalue属性を選択することにより、1つ目に入ったinputを取得することができます。

同様に、tweetInputとして、[1]番目を取得することにより、inputに入った２つ目の要素を取得することができます。

```js
const tweetForm = document.querySelector('#tweetForm');
tweetForm.addEventListener('submit',funciton(e) {
    e.preventDefault();

    const usernameInput = document.querySelectorAll('input')[0];
    const tweetInput = document.querySelectorAll('input')[1];
    console.log(usernameInput.value);
    console.log(tweetInput.value);
    
});

```

出力結果：
無事に、usernameInput.valueの値と、tweetInput.valueの値が
取得できていることが確認できます。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/2a92746d-eefa-63d6-483b-0dbcbf88a0d2.png width = 70%>

この値を使用して、ツイート一覧のulに作成したinputの内容をliに追加していくことができます。

しかし、取得するものが複数になってしまうと複雑になり、値を取得するのが難しくなってしまいます。

そのため、別の方法を模索してみます。

#### Formに入力された値を取得する「elements」

tweetFormの中身をconsole.dir(tweetForm)で確認してみると、
elementsというプロパティが存在し、その中にnameやtweetの中身が存在します。

これは、HTMLでFormを作成したときに、name属性を設定することにより、そのnameに対応したキーを作成し、その中に値をマッピングしてくれます。

```js
console.log(tweetForm.elements.username.value);
console.log(tweetForm.elements.tweet.value);
```
Formの中に入力したusernameの値とtweetの値が取得できていることを確認できます。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/9e7b61ab-9bf3-66ad-ef5f-951696c5717e.png >


## 取得した値をtweet内容として作成する「JavaScript ファイル」

Formから取得した内容を元に、
ツイート一覧の下に入力した内容をliとして出力してみたいと思います。

```js
    const username = tweetForm.elements.username.value;
    const tweet = tweetForm.elements.tweet.value;

    const newTweet = document.createElement('li');
    const bTag = document.createElement('b');
    bTag.append(username);
    newTweet.append(bTag);
    newTweet.append(` - ${tweet}`);
```

ロジックの説明：
1. usernameの内容の取得 「const username = tweetForm.elements.username.value;」
2. tweetの内容の取得 「const tweet = tweetForm.elements.tweet.value;」
3. liの作成　「const newTweet = document.createElement('li');」
4. bタグの作成（ユーザー名を太字にする。） 
「const bTag = document.createElement('b');」
5. bタグの中に、usernameを挿入。 「bTag.append(username);」
6. bタグをnewTweet「li」に挿入。 「newTweet.append(bTag);」
7. newTweet「li」に テンプレートリテラルを使用し、- とtweetの内容を挿入。
「newTweet.append(` - ${tweet}`);」

ロジックを図式化：
```html
<li>
<b>username</b> - tweet
<li>
```

また、作成したnewTweetの内容をulに追加します。

ul要素の取得
```js
const tweetsContainer = document.querySelector('#tweets');
```

li要素の追加
```js
tweetsContainer.append(newTweet);
```

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/22219e88-08be-0503-8fc8-eeb16cd29551.png width = 60%>

inputに記載したusernameとtweetの内容が、
ツイート一覧として、ulの中にliとして追加されているのを確認できます。

#### 別の関数に移す

ツイートを追加するロジックが見えづらく、他人から見てわかりずらいコードとなってしまっているので、
別の関数に移し、よりわかりやすいように変更していきます。

```js
function addTweet(username, tweet) {
    const newTweet = document.createElement('li');
    const bTag = document.createElement('b');
    bTag.append(username);
    newTweet.append(bTag);
    newTweet.append(` - ${tweet}`);
    tweetsContainer.append(newTweet);
}

    addTweet(username, tweet);

```

addTweet関数として切り出し、フォームから取得したユーザー名とツイートをaddTweet関数に渡して使用します。

#### ツイート内容を投稿した時に、inputから削除する

inputに投稿内容を記載して、投稿ボタンを押した後に
inputの中の内容は自動で削除されて欲しいので、resetメソッドを使用して中身を削除します。

```js
tweetForm.reset();
```
inputに投稿文章を記載後、ツイートするボタンを押すことで、
ツイート一覧に投稿内容が反映され、inputに書いた内容を削除することができます。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/fa23ccf2-bcb9-79d5-7fc9-06ab7730ac39.png width= 60%>

## tweetの内容を削除する「JavaScript ファイル」

通常であれば、削除ボタンを用意するのが一般的だと思いますが、
今回は、liをクリックすると、一覧のリストの中からli要素を取り除きたいと思います。

```js
const lis = document.querySelectorAll('li');

for(let li of lis ) {
    li.addEventListener('click', function() {
    li.remove()
    })
}
```

1. liを取得
2. liに対して、for ofループを行う。
2-1. liにclickのイベントハンドラーを追加する。
2-2. イベントの内容は、liをremoveする。

これで、事前に用意しておいたliに関しては、削除することができますが、
新たに作成したli要素に関しては削除されません。

これは、documentにあるliを取得し、そのli達に対してイベントハンドラーを付加しているためです。
最初からあるliにはイベントハンドラーはついているが、新たに作成したliに対しては、削除機能がついていないものが生成されてしまいます。

#### イベントデリゲーションを使用して対処する。

上記の処理では、削除機能を追加で作成したliに付加することができないため、
イベントデリゲーションを活用していこうと思います。

::: note info
イベントデリゲーションとは？(イベント移譲)
複数の要素に同じイベントハンドラを設定せずに、
共通の親要素に 1 つのイベントリスナ・イベントハンドラを設定することで、
子要素で発生するイベントを捕捉する方法のことです。
:::

今回の場合は、liがクリックされたときに、li自身に処理をさせるのではなく、
その親であるulのところにイベントの処理を任せることで削除機能を実装させていこうと思います。

```js
tweetsContainer.addEventListener('click', function() {
    console.log('ul内click!');
});

```
ログの中身を確認。
・既存のliをクリックしたら、ul内click!が出力されます。
・inputに新規作成したulに対しても、ul内click!のログが出力されるのを確認できます。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/e9936fdb-b38e-ee76-948e-2f5dcff63b13.png width= 70%>

また、console.log(e)
として、イベントオブジェクトの中身も確認してみたいとおもいます。

<img src =https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/22b350df-8109-9101-0f79-4376a88d628e.png width = 70%>

右下を確認。target：li
となっていることを確認できます。

イベントハンドラーはulに設定したのにも関わらずtargetにはliが入っています。

::: note info
target：

targetの中には、実際にイベントが発生した要素が入ります。
:::

このtargetを要素として使用する。

```js
tweetsContainer.addEventListener('click',function(e) {
    e.target.remove()
})
```

上記のコードを書いてしまうと、
イベントオブジェクトの中にBタグが入ってしまい、inputの左側をクリックしてしまうと、左側だけが削除されてしまうので注意が必要です。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/f77f64a6-5989-2830-8574-d9947fb69114.png width = 60% >

そのため、if文で条件分岐をします。
その際に、タグ名であるnodeNameを活用していきます。

<img src = https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679750/b78f7080-ef43-fa80-f7fe-2be7e14026c8.png width = 70%>

```js
tweetsContainer.addEventListener('click',function(e) {
    if (e.target.nodeName === 'LI'){
    e.target.remove()
    }
})
```
これで、ユーザー名をクリックした時には、削除されないようになりました。

## まとめ：

:::note info
HTML Formを使って、inputに入力した値を一覧画面に表示させる。
・HTML Formのデフォルトの遷移機能を停止
・Formに入力された値を取得
・Formに入力された値を出力
・Formに入力された値を削除