LoginSignup
4
4

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-03-10

本稿について

  • JavaScript
  • HTML
  • CSS

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

この記事が参考になりそうな方

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

最終形のイメージ(参考)

こちらのような、GoogleのTodoアプリを参考にしていきたいと思います。

724fa2b381849fa16992412f02161d46.gif

ファイル構成(超簡易)

.
├── README.md
├── css
│   ├── sanitize.css
│   └── style.css
├── js
│   └── main.js
└── todo.html

ひな形

ざっくりと、下記の雛形を用意しました。最初の取っ掛かりのために、Todoのli要素を数個、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を使ってファイル出力する機能のソースコード

  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

ファイル出力された際、HTMLLIElementの中身をどのように出力する?

ファイル出力 ボタンを押してファイル出力はできましたが、
出力された中身をみると下記のようになってしまいました。。
スクリーンショット 2020-07-19 15.58.42.png
li要素自体をそのまま出力しようとすればそりゃ当然で、
中身を取得するためにtextContentを使い、
doneボタンを押したli要素については✅マーク付き、そのほかはただの四角🟩で出力されるようにしてみました。
最終的に下記のようにli要素の中身を出力できるようにできました。

スクリーンショット 2020-07-21 19.24.03.png

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

  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採用しています

constしか使いたくない

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


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だけ使うようにしたいので下記の感じで書き換えます。

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はファイルに出力するが、
逆にファイルを読み込む機能も実装しておいた方が都合が良い。
↓のように実装してみました。ソースコードとてもきちゃない。


//ファイルインポート機能
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

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

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4