thisなきJS エラーあり

# はじめに
　JSでTODOアプリをつくり、そのやり方を教材として書き起こしていた。教材の仕様に沿ってアロー関数をつかったらうまく動作しなくなった。なんだこれは。少しだけ考えた。

# 問題
　問題が発生したのはTODOの内容をクリックして、既存のタスクを編集する箇所。今までのfunctionではOKだったが、アロー関数にするとなんだかおかしい。


# コード
　問題点を明らかにするためにその箇所だけを抜き出して別にコードを書いた。「テスト」をクリックするとアロー関数のやつが実行されて、「テスト２」だと普通の関数が実行される。
　テストをクリックして、入力フォームのカーソルを外すと

　「Uncaught TypeError: Cannot read property 'classList' of null」

　というエラーが出てくる。
　

```html:test.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="utf-8"/>
    <link rel ="stylesheet" href="todo.css">
    <title>イベントハンドラテスト</title>
</head>
<body>
  <header>
    <h1>TODOテスト</h1>
  </header>
  <div class="container">
    <div class="todo-container">
      <ul class="todo-list">
        <li>
          <span class="todo-content1">テスト</span>
        </li>
        <li>
          <span class="todo-content2">テスト2</span>
        </li>
      </ul>
    </div>
  </div>
    <script src = "test.js"></script>
</body>
</html> 
```

```javascript
const editTodo = (e) => {
  let itemToEdit = e.target;
  if(!itemToEdit.classList.contains('on')) {
    itemToEdit.classList.add('on');
    let contentBeforeEdit = itemToEdit.textContent;
    itemToEdit.innerHTML = '<input type="text" class="editbox1" value="'+contentBeforeEdit+'" />';
    const editContent1 = document.querySelector('.editbox1');
    
    const saveTodoContent  = (e) => {
        let itemToSave = e.target;
        itemToSave.parentNode.classList.remove('on');
        let txtvalue = itemToSave.value;
        if (txtvalue ==''){
         txtvalue = itemToSave.defaultValue;
        }
        itemToSave.parentNode.innerHTML = txtvalue;
        
    }
    editContent1.addEventListener('blur',saveTodoContent);
  }
}

function editTodo2(e) {
  if(!this.classList.contains("on")) {
    this.classList.add("on");
    let contentBeforeEdit = this.textContent
    this.innerHTML = '<input type="text" class="editbox2" value="'+contentBeforeEdit+'" />'
    const editContent2 = document.querySelector(".editbox2")

    let saveTodoContent = function(){
      this.parentNode.classList.remove('on')
      let txtvalue = this.value
      if (txtvalue ==""){
       txtvalue = this.defaultValue
      }
      this.parentNode.innerHTML = txtvalue
    
    }
    editContent2.addEventListener("blur",saveTodoContent)
  }
}

//Select DOM
const todoContent = document.querySelector('.todo-content1');
todoContent.addEventListener('click', editTodo);
const todoContent2 = document.querySelector('.todo-content2');
todoContent2.addEventListener('click', editTodo2);

```

# 問題を追う

　カーソルを外した時に「classListがnullですよ」というエラーが発生する。ちょうどこの行である。

```javascript
 const saveTodoContent  = (e) => {
//
      itemToSave.parentNode.classList.remove('on');
//
```

　Chromeのデベロッパーモードで少しずつ検証していった。そうするとカーソルを外す時に実行されるイベント`saveTodoContent`が２回実行されていることがわかった。そして２回目の実行時にエラーが発生していた。なぜだ。

　だいたいはthisが原因だった。

# 原因
　この機能では項目の状態を知る必要がある。つまり「タスクが現在入力モードなのか否か」ということを知りたいのだ。コード上ではclasslistにonというclassを付与したり外したりしてそれを操作している。
　今まではその検知にthisを用いていたが、今回アロー関数に書き換えたことによってthisが使えなくなり、別の記法で書き換えた。そこがよくなかったのだ。

　ここである。

```javascript:変更前
function editTodo2(e) {
  if(!this.classList.contains("on")) {
// ..　クリックときに編集モードでなければ以下を実行
```

```javascript:変更後
const editTodo = (e) => {
  let itemToEdit = e.target;
  if(!itemToEdit.classList.contains('on')) {
// ..　クリックときに編集モードでなければ以下を実行
```

# this/e.target

thisとe.targetはなにが違うのか。thisは場面によってさまざまに容態を変化させるが、ここでは以下の記事を参考にするならば「関数を呼び出している元のオブジェクト」である。

https://qiita.com/takkyun/items/c6e2f2cf25327299cf03

　e.targetとはクリックしたときの物体である。テキスト文字であったり、入力フォームであったりする。

　thisの中身はタスクが編集モードか否かでも変わらない。しかしe.targetは中身が変わる。ためしにconsole.logで読んでみよう。


```javascript
const editTodo = (e) => {
  let itemToEdit = e.target;
  console.log(e.target);
  console.log(this);

//


function editTodo2(e) {
  console.log(e.target);
  console.log(this);
```
　
![スクリーンショット 2021-03-29 5.25.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104211/f239f2ae-37e3-a5c3-7671-efe6c4184489.png)

上4行はアロー関数のほう。下はいままでの関数だ。たしかになんか違っていた。

# どうしたか

　アロー関数にはイベントが実行される条件を増やした。

```javascript
const editTodo = (e) => {
  let itemToEdit = e.target;
  if(!itemToEdit.classList.contains('todo-content1')){
    return;
  }
  if(!itemToEdit.classList.contains('on')) {
//....

```

　これで問題は解決した。


# おわりに

　文章がまとまらないままこれを書き出してしまった。それでもどこかに残しておかないと、後々自分が困ってしまう。

　もしかしたら追記するかもしれないししないかもしれない。