本稿について
- JavaScript
- HTML
- CSS
のみを使ってTodoアプリを作っていきたいと思い、その開発の記録となります。
要所要所で詰まったところなどを備忘録的に書いていってます。
今回は、DBに関する知見を使わずに 実装していきたいと考えております。
この記事が参考になりそうな方
初学者の方(初心者ではなく、学習を始めたばかりの方)となります。
フレームワークや、データベースなどのバックエンドの知見を用いずに、
JavaScript、HTML、CSSのみで実装を目指します。
(ほぼほぼJavaScriptっす)
最終形のイメージ(参考)
こちらのような、GoogleのTodoアプリを参考にしていきたいと思います。
ファイル構成(超簡易)
.
├── 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();
ファイル出力ボタンを押して確認してみると、出力はされました。が...
ファイル出力された際、HTMLLIElementの中身をどのように出力する?
ファイル出力
ボタンを押してファイル出力はできましたが、
出力された中身をみると下記のようになってしまいました。。
li要素自体をそのまま出力しようとすればそりゃ当然で、
中身を取得するためにtextContent
を使い、
doneボタンを押したli要素については✅マーク付き、そのほかはただの四角🟩で出力されるようにしてみました。
最終的に下記のようにli要素の中身を出力できるようにできました。
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
まだまだ実装する機能は残っていますし、(タスクのグループ化、サイドバーでの選択、作業済みのタスクを移動させる 等)当初の再集計イメージにはまだ遠いですが、
続きはまた別途書いていこうと思いますー