なぜFormDataオブジェクトを使ってるのか?
そうした方が保存しやすいから。
理屈: 入力form全体のidからdata取得し、formオブジェクトにdataを入れてる
2種類配列を用意しなきゃいけない
テーブル表示用配列:LOCAL_STORAGE_KEY_TABLE_DATA
入力form用配列:LOCAL_STORAGE_KEY_FORM_DRAFT
dataが保存されたら、入力formのdataをすぐ消去
//dataが保存されたら、入力formのdataをすぐ消去
localStorage.removeItem(LOCAL_STORAGE_KEY_FORM_DRAFT);
付け足す設定
入力formに、name属性をつける
formオブジェクトを使う
formオブジェクト用の配列を用意する
formオブジェクトにdataを入れるときは、keyと値がワンセット
入力formに、name属性をつける
<input type="email" id="editEmail" name="editEmail">
formオブジェクトを使う
const formData = new FormData(formElement);
formオブジェクト用の配列を用意
const dataToSave = {};
entries : 変数を指定し、セットで取り出す
→ keyと値をセットで保存
for (let [key, value] of formData.entries()) {
dataToSave[key] = value;
}
Localstorageで保存しない方がいいもの
入力formに、name属性をつける
<input type="email" id="editEmail" name="editEmail">
formオブジェクトを使う
const formData = new FormData(formElement);
formオブジェクト用の配列を用意
const dataToSave = {};
entries : 変数を指定し、セットで取り出す
→ keyと値をセットで保存
for (let [key, value] of formData.entries()) {
dataToSave[key] = value;
}
パスワード
クレジットカード
セッションデータ
マイナンバーカードとか
code全文
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>テーブル表示練習</title>
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.form-group { margin-bottom: 10px; }
.label { display: inline-block; width: 80px; }
.error-message { display:none; color:red; margin-left:5px; }
/* テーブル全体の基本スタイル */
table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
table, th, td { border:1px solid #000; border-collapse:collapse; padding:5px; }
/* テーブルヘッダーの枠線を太くする */
th {
border: 3px solid #000; /* ヘッダーの枠線を太く */
}
/* ボタンの初期スタイル */
#addButon {
padding: 8px 15px;
font-size: 16px;
cursor: pointer;
background-color: #007bff; /* 初期の色(青) */
color: white;
border: none;
border-radius: 4px;
transition: background-color 0.3s ease; /* 色の変化を滑らかに */
}
/* ボタンがクリックされた後のスタイル */
#addButon.clicked-button {
background-color: #28a745; /* クリック後は別の色(例: 緑) */
}
/* 削除・編集ボタンのスタイル */
.action-button {
padding: 5px 10px;
margin-left: 5px; /* ボタン間のスペース */
border: none;
border-radius: 3px;
cursor: pointer;
font-size: 13px;
color: white;
}
.delete-button {
background-color: #dc3545; /* 赤色 */
}
.edit-button {
background-color: #ffc107; /* 黄色 */
color: black; /* 黄色には黒文字が見やすい */
}
/* --- モーダルウィンドウのスタイル --- */
.modal {
display: none; /* 初期状態では非表示 */
position: fixed; /* 画面に固定 */
z-index: 1; /* 最前面に表示 */
left: 0;
top: 0;
width: 100%; /* 全幅 */
height: 100%; /* 全高 */
overflow: auto; /* スクロール可能にする */
background-color: rgba(0,0,0,0.4); /* 半透明の黒背景 */
justify-content: center;
align-items: center;
}
.modal-content {
background-color: #fefefe;
margin: auto; /* 中央寄せ */
padding: 20px;
border: 1px solid #888;
width: 80%; /* 幅 */
max-width: 500px; /* 最大幅 */
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0,0,0,0.2);
position: relative;
}
.close-button {
color: #aaa;
float: right;
font-size: 28px;
font-weight: bold;
}
.close-button:hover,
.close-button:focus {
color: black;
text-decoration: none;
cursor: pointer;
}
.modal-buttons {
margin-top: 20px;
text-align: right;
}
.modal-buttons button {
padding: 8px 15px;
margin-left: 10px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.modal-buttons .save-button {
background-color: #28a745;
color: white;
}
.modal-buttons .cancel-button {
background-color: #6c757d;
color: white;
}
</style>
</head>
<body>
<form id="userForm">
<div class="form-group">
<label class="label" for="studentName">名前</label>
<input type="text" id="studentName" name="studentName">
<div id="nameError" class="error-message">名前を入力してください</div>
</div>
<div class="form-group">
<label class="label" for="studentAge">年齢</label>
<input type="number" id="studentAge" name="studentAge">
</div>
<div class="form-group">
<label class="label" for="studentEmail">メール</label>
<input type="email" id="studentEmail" name="studentEmail">
</div>
<div class="form-group">
<label class="label" for="studentnumber">電話</label>
<input type="text" id="studentnumber" name="studentnumber">
</div>
<button type="button" id="addButon">追加</button>
</form>
<hr>
<table id="studentTable">
<thead>
<tr><th>ID</th><th>名前</th><th>年齢</th><th>メール</th><th>電話</th><th>操作</th></tr> </thead>
<tbody></tbody>
</table>
<div id="editModal" class="modal">
<div class="modal-content">
<span class="close-button">×</span>
<h2>データ編集</h2>
<input type="hidden" id="editId">
<div class="form-group">
<label class="label" for="editName">名前</label>
<input type="text" id="editName" name="editName">
</div>
<div class="form-group">
<label class="label" for="editAge">年齢</label>
<input type="number" id="editAge" name="editAge">
</div>
<div class="form-group">
<label class="label" for="editEmail">メール</label>
<input type="email" id="editEmail" name="editEmail">
</div>
<div class="form-group">
<label class="label" for="editPhone">電話</label>
<input type="text" id="editPhone" name="editPhone">
</div>
<div class="modal-buttons">
<button class="save-button" id="saveEditButton">保存</button>
<button class="cancel-button" id="cancelEditButton">キャンセル</button>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const LOCAL_STORAGE_KEY_TABLE_DATA = 'studentData'; // テーブルデータのローカルストレージキー
const LOCAL_STORAGE_KEY_FORM_DRAFT = 'userFormDraft'; // フォーム入力値のローカルストレージキー
const userForm = document.getElementById('userForm'); // フォーム要素全体を取得
const addButton = document.getElementById('addButon');
const nameInput = document.getElementById('studentName');
const ageInput = document.getElementById('studentAge');
const emailInput = document.getElementById('studentEmail');
const phoneInput = document.getElementById('studentnumber');
const nameError = document.getElementById('nameError');
const tableBody = document.querySelector('#studentTable tbody');
// モーダル関連要素
const editModal = document.getElementById('editModal');
const closeButton = document.querySelector('.close-button');
const saveEditButton = document.getElementById('saveEditButton');
const cancelEditButton = document.getElementById('cancelEditButton');
const editIdInput = document.getElementById('editId');
const editNameInput = document.getElementById('editName');
const editAgeInput = document.getElementById('editAge');
const editEmailInput = document.getElementById('editEmail');
const editPhoneInput = document.getElementById('editPhone');
let studentData = []; // アプリケーションのデータ配列
let nextId = 1; // 次に割り当てるID
// --- ローカルストレージとデータ管理 ---
// ローカルストレージからテーブルデータを読み込む
function loadTableDataFromLocalStorage() {
const storedData = localStorage.getItem(LOCAL_STORAGE_KEY_TABLE_DATA);
if (storedData) {
studentData = JSON.parse(storedData);
// 既存データから最大のIDを見つけてnextIdを設定
if (studentData.length > 0) {
nextId = Math.max(...studentData.map(item => item.id)) + 1;
}
renderTable(); // テーブルを初期表示
}
}
// ローカルストレージにテーブルデータを保存する
function saveTableDataToLocalStorage() {
localStorage.setItem(LOCAL_STORAGE_KEY_TABLE_DATA, JSON.stringify(studentData));
}
// フォーム入力値をローカルストレージに自動保存する
function setupAutoSaveForm(formElement, localStorageKey) {
formElement.addEventListener('input', () => {
const formData = new FormData(formElement);
const dataToSave = {};
for (let [key, value] of formData.entries()) {
dataToSave[key] = value;
}
localStorage.setItem(localStorageKey, JSON.stringify(dataToSave));
// console.log('フォームの入力値をローカルストレージに自動保存しました:', dataToSave);
});
}
// ローカルストレージからフォームの入力値をロードしてフォームに設定する
function loadAndFillForm(formElement, localStorageKey) {
const savedData = localStorage.getItem(localStorageKey);
if (savedData) {
const data = JSON.parse(savedData);
for (const key in data) {
// name属性が一致するinput要素を探して値を設定
const inputElement = formElement.querySelector(`[name="${key}"]`);
if (inputElement) {
inputElement.value = data[key];
}
}
// console.log('ローカルストレージからフォームの入力値をロードしました:', data);
}
}
// --- 関数定義 ---
// 入力データ取得
function getFormData() {
return {
id: nextId, // 現在のnextIdを割り当てる
name: nameInput.value.trim(),
age: parseInt(ageInput.value.trim()) || 0, // 数値に変換、無効な場合は0
email: emailInput.value.trim(),
phone: phoneInput.value.trim()
};
}
// バリデーション
function validateForm(data) {
if (data.name === '') {
nameError.style.display = 'inline';
setTimeout(() => { nameError.style.display = 'none'; }, 2000);
return false;
}
if (isNaN(data.age) || data.age <= 0) {
alert('正しい年齢を入力してください');
return false;
}
return true;
}
// バリデーションエラー初期化
function clearErrors() {
nameError.style.display = 'none';
}
// --- CRUD操作(Axiosを使用) ---
// データ新規追加 (Create)
async function createData(data) {
try {
const response = await axios.post('https://jsonplaceholder.typicode.com/users', data); // ダミーAPI
console.log('APIから新規データが追加されました:', response.data);
// 成功したらローカルデータとLocalStorageを更新
studentData.push({ ...data, id: nextId++ }); // IDを更新
saveTableDataToLocalStorage();
renderTable();
clearFormInputs();
localStorage.removeItem(LOCAL_STORAGE_KEY_FORM_DRAFT); // フォーム送信成功後、下書きをクリア
} catch (error) {
console.error('データ追加に失敗しました:', error);
alert('データ追加に失敗しました。');
}
}
// データ編集 (Update)
async function updateData(id, updatedData) {
try {
// ダミーAPIはPUT/PATCHしても実際のデータは更新しないため、擬似的な成功とします
const response = await axios.put(`https://jsonplaceholder.typicode.com/users/${id}`, updatedData); // ダミーAPI
console.log('APIでデータが更新されました:', response.data);
// 成功したらローカルデータとLocalStorageを更新
const index = studentData.findIndex(item => item.id === id);
if (index !== -1) {
studentData[index] = { ...studentData[index], ...updatedData };
saveTableDataToLocalStorage();
renderTable();
}
closeEditModal(); // 編集成功後にモーダルを閉じる
} catch (error) {
console.error('データ更新に失敗しました:', error);
alert('データ更新に失敗しました。');
}
}
// データ削除 (Delete)
async function deleteData(id) {
const confirmDelete = confirm('本当に削除しますか?');
if (!confirmDelete) return;
try {
await axios.delete(`https://jsonplaceholder.typicode.com/users/${id}`); // ダミーAPI
console.log(`APIからID ${id} のデータが削除されました。`);
// 成功したらローカルデータとLocalStorageを更新
studentData = studentData.filter(item => item.id !== id);
saveTableDataToLocalStorage();
renderTable();
} catch (error) {
console.error('データ削除に失敗しました:', error);
alert('データ削除に失敗しました。');
}
}
// --- UI操作 ---
// テーブルに行追加(編集・削除ボタン付き)
function addTableRow(data) {
const row = document.createElement('tr');
row.setAttribute('data-id', data.id); // 行にデータのIDを設定
row.innerHTML = `
<td>${data.id}</td>
<td>${data.name}</td>
<td>${data.age}</td>
<td>${data.email}</td>
<td>${data.phone}</td>
<td>
<button class="action-button edit-button">編集</button>
<button class="action-button delete-button">削除</button>
</td>
`;
tableBody.appendChild(row);
// 追加したばかりのボタンにイベントリスナーを設定
const editButton = row.querySelector('.edit-button');
const deleteButton = row.querySelector('.delete-button');
editButton.addEventListener('click', () => {
openEditModal(data.id); // 編集モーダルを開く
});
deleteButton.addEventListener('click', () => {
deleteData(data.id); // データ削除関数を呼び出す
});
}
// 入力欄クリア
function clearFormInputs() {
nameInput.value = '';
ageInput.value = '';
emailInput.value = '';
phoneInput.value = '';
}
// テーブルを再描画する関数
function renderTable() {
tableBody.innerHTML = ''; // テーブルの中身をクリア
studentData.forEach(data => addTableRow(data)); // 全てのデータを再描画
}
// 編集モーダルを開く関数
function openEditModal(id) {
const dataToEdit = studentData.find(item => item.id === id);
if (dataToEdit) {
editIdInput.value = dataToEdit.id;
editNameInput.value = dataToEdit.name;
editAgeInput.value = dataToEdit.age;
editEmailInput.value = dataToEdit.email;
editPhoneInput.value = dataToEdit.phone;
editModal.style.display = 'flex'; // flexにして中央寄せを有効に
}
}
// 編集モーダルを閉じる関数
function closeEditModal() {
editModal.style.display = 'none';
}
// --- イベントリスナー ---
// ページロード時にローカルストレージからテーブルデータを読み込み、テーブルを表示
loadTableDataFromLocalStorage();
// フォームの入力値を自動保存する機能をセットアップ
setupAutoSaveForm(userForm, LOCAL_STORAGE_KEY_FORM_DRAFT);
// ページロード時にローカルストレージからフォームの値をロードして入力欄に設定
loadAndFillForm(userForm, LOCAL_STORAGE_KEY_FORM_DRAFT);
// 「追加」ボタンのクリックイベント
addButton.addEventListener('click', () => {
// ボタンの色を青に変更 (今回はクリック後に緑に変える例)
addButton.classList.add('clicked-button');
const formData = getFormData();
clearErrors();
if (!validateForm(formData)) return;
createData(formData); // データ追加関数を呼び出す
});
// モーダルを閉じるボタン
closeButton.addEventListener('click', closeEditModal);
cancelEditButton.addEventListener('click', closeEditModal);
// モーダル内の保存ボタン
saveEditButton.addEventListener('click', () => {
const id = parseInt(editIdInput.value);
const updatedData = {
name: editNameInput.value.trim(),
age: parseInt(editAgeInput.value.trim()),
email: editEmailInput.value.trim(),
phone: editPhoneInput.value.trim()
};
if (!validateForm(updatedData)) return; // 編集フォームのバリデーションも既存関数を利用
updateData(id, updatedData); // データ更新関数を呼び出す
});
});
</script>
</body>
</html>