データベースを使わずにToDoアプリ作成してみる


## 本稿について

* JavaScript
* HTML
* CSS

のみを使ってTodoアプリを作っていきたいと思い、その開発の記録となります。
要所要所で詰まったところなどを備忘録的に書いていってます。
今回は、**DBに関する知見を使わずに**　実装していきたいと考えております。

## この記事が参考になりそうな方
初学者の方（初心者ではなく、学習を始めたばかりの方）となります。
フレームワークや、データベースなどのバックエンドの知見を用いずに、
JavaScript、HTML、CSSのみで実装を目指します。
（ほぼほぼJavaScriptっす）

## 最終形のイメージ（参考）
こちらのような、GoogleのTodoアプリを参考にしていきたいと思います。

![724fa2b381849fa16992412f02161d46.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540634/44fa49d8-a491-db4a-38a9-05e4ddaef0fe.gif)

## ファイル構成（超簡易）
```
.
├── README.md
├── css
│   ├── sanitize.css
│   └── style.css
├── js
│   └── main.js
└── todo.html
```

## ひな形
ざっくりと、下記の雛形を用意しました。最初の取っ掛かりのために、Todoのli要素を数個、HTML固定で記載してます

```HTML
<!doctype html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
    <title>Document</title>
      <script type="text/javascript" src="js/main.js"></script>
    <link rel="stylesheet" href="css/style.css">
    <link rel="stylesheet" href="css/sanitize.css">
  </head>
  <body>
    <h1>My Tasks</h1>
    <form>
      <input type="text" name="" id="new_todo"/>
      <input type="button" value="追加" id="btn" onclick="appendTodo()"/>
    </form>
    <ul id="lists">
      <li>Todo 1 <button onclick="changeDone()">done</button></li>
      <li>Todo 2 <button onclick="changeDone()">done</button></li>
      <li>Todo 3 <button onclick="changeDone()">done</button></li>
      <li>Todo 4 <button onclick="changeDone()">done</button></li>
      <li>Todo 5 <button onclick="changeDone()">done</button></li>
      <li>Todo 6 <button onclick="changeDone()">done</button></li>
    </ul>
    <button id="btnexp" onclick="exportTodolist()">ファイル出力</button>
    <input type="file" id="importTodo" />
    <pre id="pre1"></pre>
  </body>
</html>

```





## データベースを使わずにどうやってTodoを記憶させる？
今回はデータベースを使わずに行うため、一度作成したTodoリストを出力させる形式で作ることを目指しました。
下記の記事を参考にBLOBを使っています
https://javascript.keicode.com/newjs/download-files.php

BLOBを使ってファイル出力する機能のソースコード

```js
  const blob = new Blob([nodeItem.value],{type:"text/plan"});
  const link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  link.download = '作ったファイル.txt';
  link.click();
```
ファイル出力ボタンを押して確認してみると、出力はされました。が...
![62e40fb5898dded99611795fed28b81f.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540634/b602398a-ff0c-f76e-c0bb-87884ab53bda.gif)

## ファイル出力された際、HTMLLIElementの中身をどのように出力する？
`ファイル出力`　ボタンを押してファイル出力はできましたが、
出力された中身をみると下記のようになってしまいました。。
<img width="635" alt="スクリーンショット 2020-07-19 15.58.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540634/5ed2db85-d841-bffe-732f-7a77305042cd.png">
li要素自体をそのまま出力しようとすればそりゃ当然で、
中身を取得するために`textContent `を使い、
doneボタンを押したli要素については✅マーク付き、そのほかはただの四角🟩で出力されるようにしてみました。
最終的に下記のようにli要素の中身を出力できるようにできました。


<img width="635" alt="スクリーンショット 2020-07-21 19.24.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540634/b535eec1-6685-56ab-13bb-9f3a393f1b7f.png">


doneボタンまで出力されててじゃまなので、下記のような書き方をして必ず後ろについてくるdoneの４文字を削るようにしました。

```javascript
  for(i=0;i < Todolist.length; i++){
    const todolist = Todolist[i];
    if (todolist.className === 'done'){
      nodeItem.value += `☑︎ ${todolist.textContent.slice(0,-4)}\n`;
    }else {
      nodeItem.value += `□ ${todolist.textContent.slice(0,-4)}\n`;
    }
```
※innerTextやinnerHTMLとtextContentの違いについて、こちらのMDNリファレンスわかりやすいです。
HTML要素や子要素を取り除くことができるか、といった点や、処理が重くならないよう再フローがかからないようにできる点から、今回textContent採用しています

https://developer.mozilla.org/ja/docs/Web/API/Node/textContent#Differences_from_innerText

## constしか使いたくない
とりあえずファイル出力できた時点でのソースはこんな感じでした。

```JavaScript

function exportTodolist(){

  const Todolist = document.querySelectorAll('li');
  let nodeItem = "";

  for(let i=0;i < Todolist.length; i++){
   nodeItem += Todolist[i].textContent;
  }

  // ファイル出力の実装
  let blob = new Blob([nodeItem],{type:"text/plan"});
  let link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  link.download = '作ったファイル.txt';
  link.click();
}
```
下記の記事が有名ですが、
https://qiita.com/taiju_suzuki/items/49ed558bd837452353b8
varやletは再代入や再宣言で値を書き換えるリスクがあるため、
constだけ使うようにしたいので下記の感じで書き換えます。

```JavaScript
function exportTodolist(){

  const Todolist = document.querySelectorAll('li');
  const nodeItem = {value:""};
  for(let i=0;i < Todolist.length; i++){
    nodeItem.value += (Todolist[i].textContent + `\n`)
  }

  // ファイル出力の実装
  const blob = new Blob([nodeItem.value],{type:"text/plan"});
  const link = document.createElement('a');
  link.href = URL.createObjectURL(blob);
  link.download = '作ったファイル.txt';
  link.click();
}
```


## ファイルインポート機能の実装
DBを使わない為一度作成したTodoはファイルに出力するが、
逆にファイルを読み込む機能も実装しておいた方が都合が良い。
↓のように実装してみました。ソースコードとてもきちゃない。

```js

//ファイルインポート機能
window.addEventListener('load', () => {
  const f = document.getElementById('importTodo');
  f.addEventListener('change', evt => {
    const input = evt.target;
    if (input.files.length == 0) {
      console.log('No file selected');
      return;
    }
    const file = input.files[0];
    const reader = new FileReader();
    reader.onload = () => {
      const pre = document.getElementById('pre1');
      const result = reader.result.split("\n");
      for(i=0;i < result.length - 1; i++){
        const li = document.createElement('li');
        const button = document.createElement('button');
        button.innerText = 'done';
        button.onclick = "changeDone()";
        li.innerHTML = result[i];
        li.appendChild(button);
        console.log(li.innerHTML.charAt(0));
        if (li.innerHTML.charAt(0) !== `□`){
          li.className = "done";
        }
        li.innerHTML = li.innerHTML.slice(2);
        const ul = document.querySelector('ul');
        ul.appendChild(li);
      }
    };
    reader.readAsText(file);
  });
});

```
こちらの日本語版スタックオーバーフローの記事を参考にしています。
https://ja.stackoverflow.com/questions/36426/javascript%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E4%BB%B6%E6%95%B0%E3%83%81%E3%82%A7%E3%83%83%E3%82%AF%E5%87%A6%E7%90%86%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6

無事、ファイルimport機能も実装できました。
![0d9330344dc72a3670ca1faadd60b30b.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/540634/ec986cd9-a750-ffdc-7062-a006def04add.gif)

まだまだ実装する機能は残っていますし、（タスクのグループ化、サイドバーでの選択、作業済みのタスクを移動させる　等）当初の再集計イメージにはまだ遠いですが、
続きはまた別途書いていこうと思いますー