今回の記事では、JavascriptやFIREBASEの初心者の方や、未経験からエンジニア転職を目指している方向けに、ToDoリストの作成方法を解説していきます。JavascriptのDOM操作やイベント処理に加え、バックエンドとの簡単な連携も機能に含めていますので、初心者の方にとってちょうど良い練習になるかと思います。
今回、FIREBASEのRealtimeDatabaseと連携してToDoリストを作成しますので、RealtimeDatabaseの操作について、まだ理解不足の方は以下の記事も参考にしてみて下さい。
ToDoリストの実装例
今回は動作確認の勉強のため、ToDoリストの最低限の機能を実装しています。
ToDoリストをご参考ください。
FIREBASEにリストデータを登録するため、ブラウザのリロードなどでリストデータが失われることはありません。
HTMLとCSSの準備
まずは、HTMLとCSSを実装していきます。
HTML
<main>
<!-- 新規作成エリア -->
<div id="create">
<h2>新規作成</h2>
<input type="text" name="newData" id="newData" placeholder="新規ToDo"/><br><br>
<button id="btn-new" onclick="createData()">リスト追加</button><br><br>
</div>
<!-- TODOリスト表示エリア -->
<div id="registered">
<h2>登録済みリスト</h2>
<button id="btn-get" onclick="getData()">リスト取得</button>
<button id="btn-delete" onclick="deleteData()">リスト削除</button><br><br>
<ul id="list">
</ul>
</div>
</main>
ToDoリストの「新規作成エリア」と「リスト表示エリア」をそれぞれマークアップしていきます。
各buttonには、onclickを設定し、Javascript側で実装するメソッドをクリック時に実行できるようにします。
また、<ul id=”list”>を用意して、FIREBASEから取得したToDoリストデータを<li>要素として表示させていきます。
CSS
.done + p{
text-decoration: line-through;
}
body{
margin: 0;
color: #333;
}
main{
padding: 0px 20px;
}
h1{
text-align: center;
background-color: #777;
color: white;
margin: 0;
padding: 20px 0;
}
div{
max-width: 400px;
margin: 20px auto;
border-radius: 7px;
box-shadow: 0px 0px 4px .1px#777;
padding: 10px;
text-align: center;
}
button{
cursor: pointer;
padding: 4px 15px ;
background-color: #777;
color: white;
border: 1px solid #777;
border-radius: 5px;
font-weight: bold;
}
button:hover{
border: 1px solid #333;
background-color: #333;
}
ul{
padding: 0;
list-style: none;
text-align: left;
}
p{
display: inline;
padding-left: 10px;
}
#create input{
line-height: 1.5;
width: 70%;
}
.done + p{text-decoration: line-through;}で、表示された各ToDoリストにチェックをすると「取り消し線」がつくようにスタイリングします。Javascript側でリスト要素のクラスの設定をして、doneクラスがついたリストだけ「取り消し線」を引くようにします。
その他、CSSの詳細な解説は割愛しますので、お好みでスタイルを適用してください。
画面イメージ
HTMLとCSSで作成した画面イメージは以下のような形になります。
JavascriptでToDo機能を実装する
HTMLとCSSで作った静的な画面にJavascriptを使って動きを付けていきます。
コードの全体像
const $newData = document.querySelector("#newData");//インプットボックスの要素取得
const $list = document.querySelector('#list');//リスト表示させる要素
let $values=[];//TODOリストアイテムを格納します。
// ★★todoの新規作成処理
async function createData() {
// バリデーション
if($newData.value.trim()==''){
alert('TODO登録データが入力されていません。')
return;
}
// フェッチ通信でFIREBASEへPOSTする
await fetch(
"https://ご自身のFIREBASEのURL/todo.json",//FIREBASEのURL+階層名+.json
{
method: "POST",
headers: {"Content-Type": "application/json",},
body: JSON.stringify({todo: $newData.value}), //入力値をJSONへ変換する
}
);
$newData.value=""; //インプットボックスを空欄
getData() //getDataメソッドでリストアイテムを最新化する
}
//★★データ取得処理
async function getData() {
// フェッチ通信でFIREBASEのデータを取得
const response = await fetch(
"https://ご自身のFIREBASEのURL/todo.json",//FIREBASEのURL+階層名+.json
{ method: "GET" }
);
const responseData = await response.json();//promiseをJSONへ変換
// 表示リストを一度クリア
while($list.lastChild){
$list.removeChild($list.lastChild);
}
// FIREBASEから取得したデータが0件の場合メッセージを表示する
if(responseData==null){
const p=document.createElement('p');
p.innerHTML='登録済みのTODOがありません。'
$list.appendChild(p);
}
// FIREBASEから取得したデータをリスト表示
for(const i in responseData){ //JSONに格納された値を繰り返し処理で抽出
const li = document.createElement('li');
const inp = document.createElement('input')
inp.type='checkbox'
inp.id=i
const p=document.createElement('p');
p.innerHTML=responseData[i].todo
li.appendChild(inp)
li.appendChild(p)
$list.appendChild(li);
}
refresh() //表示されているリストアイテムにイベント処理を付与する
}
// リストアイテムの要素をJavascript側で再取得します
async function refresh(){
$values = document.querySelectorAll('#list li input')
// input要素がクリックされたい際に、クラスの切替とチェックの切替を行う
$values.forEach((val)=>{
val.addEventListener('click',()=>{
if(val.checked==false){
val.checked=false;
val.classList.remove('done')
}else{
val.checked=true;
val.classList.add('done')
}
})
})
}
//★★データ削除関数実行
async function deleteData(){
const deleteList=[]
// リストアイテムにチェックがあるアイテムを削除対象の配列に格納する
$values.forEach((val)=>{
if(val.classList.contains('done')){
deleteList.push(val);
}
})
// 削除対象配列のIDのアイテムをFIREBASEからDELETEする
for(const i of deleteList){
await fetch(
`https://ご自身のFIREBASEのURL/todo/${i.id}.json`,//FIREBASEのURL+階層名+.json
{method: "DELETE"}
);
}
getData(); //getDataメソッドでリストアイテムを最新化する
}
// 画面ロードの度に、リストアイテムを最新化します。
window.addEventListener('load',getData)
Javascriptは少し長いコードになるため、「新規作成」「リスト取得」「リスト削除」のメソッドに分けて解説していきます。
ポイントは、各メソッドでFIREBASE上のデータを操作し、メソッドの実行ごとにブラウザのリスト表示とDOM取得をリフレッシュしている点です。
以下から各パートの実装について、解説していきます。
HTMLのDOMを取得する
まず、HTMLをJavascript側で操作できるようにDOMを取得していきます。
const $newData = document.querySelector("#newData");//インプットボックスの要素取得
const $list = document.querySelector('#list');//リスト表示させる要素
let $values=[];//TODOリストアイテムを格納します。
id=newDataで新規作成のインプットボックスの値、id=listでリスト表示させる要素を取得します。
$valuesはFIREBASEから取得したリストを格納する配列です。後ほど利用します。
新規作成機能を実装する
新規作成ボタンがクリックされた時に、createData()メソッドが実行されます。
async function createData() {
// バリデーション
if($newData.value.trim()==''){
alert('TODO登録データが入力されていません。')
return;
}
// フェッチ通信でFIREBASEへPOSTする
await fetch(
"https://ご自身のFIREBASEのURL/todo.json",//FIREBASEのURL+階層名+.json
{
method: "POST",
headers: {"Content-Type": "application/json",},
body: JSON.stringify({todo: $newData.value}), //入力値をJSONへ変換する
}
);
$newData.value=""; //インプットボックスを空欄
getData() //getDataメソッドでリストアイテムを最新化する
}
- 3~6行目で、新規作成入力欄の値が空になってないか入力チェックします
- 7~15行目で、FIREBASEへデータ登録するfetch通信を記述します
- 16行目で、入力欄を空欄にします
- 17行目で、リストアイテムを最新化します。getData()メソッドは後述します。
これで、新規作成ボタンを押すと入力したToDoがFIREBASEへ新規作成されます。入力値が空の場合はアラートが発生します。
リスト取得機能を実装する
続いて、FIREBASEに登録済みデータをブラウザのToDoリストエリアに表示させていきます。
async function getData() {
// フェッチ通信でFIREBASEのデータを取得
const response = await fetch(
"https://ご自身のFIREBASEのURL/todo.json",//FIREBASEのURL+階層名+.json
{ method: "GET" }
);
const responseData = await response.json();//promiseをJSONへ変換
// 表示リストを一度クリア
while($list.lastChild){
$list.removeChild($list.lastChild);
}
// FIREBASEから取得したデータが0件の場合メッセージを表示する
if(responseData==null){
const p=document.createElement('p');
p.innerHTML='登録済みのTODOがありません。'
$list.appendChild(p);
}
// FIREBASEから取得したデータをリスト表示
for(const i in responseData){ //JSONに格納された値を繰り返し処理で抽出
const li = document.createElement('li');
const inp = document.createElement('input')
inp.type='checkbox'
inp.id=i
const p=document.createElement('p');
p.innerHTML=responseData[i].todo
li.appendChild(inp)
li.appendChild(p)
$list.appendChild(li);
}
refresh() //表示されているリストアイテムにイベント処理を付与する
}
- 2行目~:フェッチ通信でFIREBASEのデータを取得します
- 7行目:フェッチ通信の戻り値はPromise形式なのでJSONへ変換します
- 9行目~:表示リストエリアの<ul>配下の要素を一度クリアします
- 13行目~:FIREBASEからのJSONデータが空の場合は、「0件」のメッセージを記載した<p>要素をHTMLへ追加します
- 18行目~:FIREBASEから取得したJSONデータを繰り返し処理していきます。<li>、<input>、<p>要素を作成して、JSONからのリストデータやIDを要素に付与していきます。作成した要素は、HTMLの<ul id=”list”>配下に追加していきます。
- 30行目~:表示されたリストにクリックイベントを付与します。refresh()は後ほど解説していきます
これで、FIREBASEに登録済みのリストデータをブラウザ上に表示させることができました。
続いて、上記の30行目で実行したrefreshメソッドについて解説していきます。
async function refresh(){
$values = document.querySelectorAll('#list li input')
// input要素がクリックされたい際に、クラスの切替とチェックの切替を行う
$values.forEach((val)=>{
val.addEventListener('click',()=>{
if(val.checked==false){
val.checked=false;
val.classList.remove('done')
}else{
val.checked=true;
val.classList.add('done')
}
})
})
}
こちらのメソッドでは、FIREBASEから取得したリストデータをブラウザへ表示させた後に、各リストに「取り消し線」スタイル用のdoneクラスを切替できるようにクリックイベントを付与していきます。
これで、各リストをクリックすると「取り消し線」がスタイルされるようになりました。
リスト削除機能を実装する
最後に、ブラウザから削除処理を実行して、FIREBASEのリストデータを削除していきます。
async function deleteData(){
const deleteList=[]
// リストアイテムにチェックがあるアイテムを削除対象の配列に格納する
$values.forEach((val)=>{
if(val.classList.contains('done')){
deleteList.push(val);
}
})
// 削除対象配列のIDのアイテムをFIREBASEからDELETEする
for(const i of deleteList){
await fetch(
`https://ご自身のFIREBASEのURL/todo/${i.id}.json`,//FIREBASEのURL+階層名+.json
{method: "DELETE"}
);
}
getData(); //getDataメソッドでリストアイテムを最新化する
}
- 4行目~:deletelist配列に、doneクラス「取り消し線」が入ったリストを削除対象として、配列へ格納します。
- 10行目~:deleteList配列の削除対象をリストを繰り返し処理します。リスト取得メソッドで各リストにFIREBASEのリストデータの各IDを付与しているので、そのIDを持つリストをFIREBASEのデータからも削除してあげます
- 16行目~:最後に、リスト取得メソッドでリスト表示を最新化します
これで、「取り消し線」を引いたリストを対象にFIREBASEからもデータを削除する機能が実装できました。
ブラウザの更新時に最新データを取得する
window.addEventListener('load',getData)
最後に、ブラウザの更新時に、getDataメソッドを実行するイベントを設定すれば、ブラウザ更新後、最新データを確認できるようになります。
まとめ
今回の記事では、JavascriptやFIREBASEの初心者の方や、未経験からエンジニア転職を目指している方向けに、ToDoリストの作成方法を解説しました。
JavascriptのDOM操作やイベント処理に加え、バックエンドとの簡単な連携も機能に含め、初心者の方にとってちょうど良い練習になるかと思いますので、実際に手を動かしてみてスキルの定着につなげてみてください。
全体として、やや冗長なコードかもしれませんので、より効率的な書き方があればぜひチャレンジしてみてください。