JavaScript PrimerのTodoアプリ作成でJS復習(アイテム追加まで)
記事概要
JSの復習として、JavaScript PrimerのTodoアプリ作成をやってみて、結構いい復習になったので紹介
対象読者
JavaScriptの基本的な構文は知っている初学者向けです。
復習内容
コピペでTodoアプリが完成してしまうからこそ、一つ一つの意味理解に割くことができます!
エントリーポイントを作る理由
まず、この教材ではindex.jsというエントリーポイントからモジュールを読み込んでいきます。
以下、その意味の例です。(GPTの力活用)
実行の起点を統一する
複数のモジュールを使用する場合、どのモジュールから実行を開始するかを明確にする必要があります。エントリーポイントはその起点となります。これにより、コードの読みやすさが向上し、プログラム全体の構造が理解しやすくなります。
初期化と設定
エントリーポイントはアプリケーションの初期化や設定のためのコードを含むことが一般的です。例えば、必要なライブラリの読み込み、グローバル変数の初期化、イベントリスナーの設定などが含まれます。
依存関係の解決
エントリーポイントは他のモジュールを読み込み、それらの依存関係を解決します。各モジュールが独立していても、エントリーポイントを通じて相互に連携することが可能です。
読み込み順序の管理
JavaScriptのモジュールは依存関係が解決された順に読み込まれます。エントリーポイントを介して依存関係を一元管理することで、適切な順序でモジュールが読み込まれ、正しく動作することが保証されます。
コード分割と効率的な読み込み
エントリーポイントを通じて必要なモジュールだけを読み込むことで、不要なコードの読み込みを避けることができ、ページの読み込み速度を向上させることができます。
Todoアイテムの作成
コピペ+理解を1時間くらいでできます。なんかはよ終わったし、記事でも書ことなってます()
- index.html
id属性にJSで使う部分が割り当てられている
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>Todo App</title>
<!-- 1. CSSファイルを読み込み -->
<link
href="https://jsprimer.net/use-case/todoapp/final/final/index.css"
rel="stylesheet"
/>
</head>
<body>
<!-- 2. class属性をCSSのために指定 -->
<div class="todoapp">
<!-- 3. id属性をJavaScriptのために指定 -->
<form id="js-form">
<input
id="js-form-input"
class="new-todo"
type="text"
placeholder="What need to be done?"
autocomplete="off"
/>
</form>
<!-- 4. TodoアプリのメインとなるTodoリスト -->
<div id="js-todo-list" class="todo-list">
<!-- 動的に更新されるTodoリスト -->
</div>
<footer class="footer">
<!-- 5. Todoアイテム数の表示 -->
<span id="js-todo-count">Todoアイテム数: 0</span>
</footer>
</div>
<script src="./index.js" type="module"></script>
</body>
</html>
- index.js
エントリーポイントであり、モジュールを読み込む
import { App } from "./src/App.js";
const app = new App();
app.mount();
- App.js
todoアイテムを作成し、#js-todo-listに表示する処理を記載
import { element, render } from "./view/html-util.js";
export class App {
mount() {
const formElement = document.querySelector("#js-form");
const inputElement = document.querySelector("#js-form-input");
const containerElement = document.querySelector("#js-todo-list");
const todoItemCountElement = document.querySelector("#js-todo-count");
// TodoリストをまとめるList要素
const todoListElement = element`<ul></ul>`;
// Todoアイテム数
let todoItemCount = 0;
formElement.addEventListener("submit", (event) => {
// 本来のsubmitイベントの動作を止める
event.preventDefault();
// 追加するTodoアイテムの要素(li要素)を作成する
const todoItemElement = element`<li>${inputElement.value}</li>`;
// TodoアイテムをtodoListElementに追加する
todoListElement.appendChild(todoItemElement);
// コンテナ要素の中身をTodoリストをまとめるList要素で上書きする
render(todoListElement, containerElement);
// Todoアイテム数を+1し、表示されてるテキストを更新する
todoItemCount += 1;
todoItemCountElement.textContent = `Todoアイテム数: ${todoItemCount}`;
// 入力欄を空文字列にしてリセットする
inputElement.value = "";
});
}
}
- html-util.js
App.jsでインポートしているelement, renderの内容
escapeSpecialChars(str)
で文字列がHTMLコンテキストで安全に表示されるようにする。
htmlToElement(html)
で<template> 要素を使用し、スクリプトによる副作用を防ぎつつ、HTML文字列を解釈してDOMツリーに変換。
element(strings, ...values)
でテンプレートリテラルを受け取り、HTML文字列を生成してから htmlToElement 関数を使ってDOM要素に変換。。また、escapeSpecialChars 関数を使用して特殊文字をエスケープ。
render(bodyElement, containerElement)
でbodyElement を containerElement の中に描画。具体的には、containerElement の中身を空にし、その後に bodyElement を追加。これにより、コンテナ要素の中身が指定された要素で上書きされる。
export function escapeSpecialChars(str) {
return str
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}
/**
* HTML文字列からHTML要素を作成して返す
* @param {string} html
*/
export function htmlToElement(html) {
const template = document.createElement("template");
template.innerHTML = html;
return template.content.firstElementChild;
}
/**
* HTML文字列からDOM Nodeを作成して返すタグ関数
* @return {Element}
*/
export function element(strings, ...values) {
const htmlString = strings.reduce((result, str, i) => {
const value = values[i - 1];
if (typeof value === "string") {
return result + escapeSpecialChars(value) + str;
} else {
return result + String(value) + str;
}
});
return htmlToElement(htmlString);
}
/**
* コンテナ要素の中身をbodyElementで上書きする
* @param {Element} bodyElement コンテナ要素の中身となる要素
* @param {Element} containerElement コンテナ要素
*/
export function render(bodyElement, containerElement) {
// containerElementの中身を空にする
containerElement.innerHTML = "";
// containerElementの直下にbodyElementを追加する
containerElement.appendChild(bodyElement);
}
ブラウザの表示
submitしたものがリストに追加されていく
次回の修正点
- DOM更新が直接される
- 追加したTodoアイテム要素を識別できない
この修正を実施
Discussion