fetchでバリデーションとテーブル表示

### 最初に、いつもこれを書く
```php:
最初の、お決まり文
document.addEventListener('DOMContentLoaded', () => {

送信処理　お決まり文
fetch('https://jsonplaceholder.typicode.com/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
    .then(() => {
    データ送信に成功したら   //処理
```

### やり方

```php:
 お決まり文
 formのid取得
 ボタンのid取得
 テーブルに表示するid取得 (名前 電話番号　住所　メール)
 バリデーションするdiv id取得
 テーブルのid表示

 # ボタンが押されたら
  1.テーブルの名前 電話番号　住所　メール表示
  2.バリデーションエラー初期化
  3.名前が空欄なら、バリデーションメッセージ表示
  4.エラー消す
  5.オブジェクト初期化処理　新規作成

  送信処理　お決まり文発動

  テーブル tr要素　作成
  td要素作成 + 表示
  td要素をtr要素に追加

  data送信が成功し、テーブルに表示されたら
  入力欄を空欄にする

  data送信されなかった時のエラー処理 記入


                ↓
  //code化すると
  お決まり文
  document.addEventListener('DOMContentLoaded', () => {  

  formのid取得
  const form = document.getElementById('userForm');
              ↓
  ボタンのid取得
  const button = document.getElementById('addButon');
             
             ↓
 テーブルに表示するid取得 (名前 電話番号　住所　メール)
 const nameInput = document.getElementById('studentName');
  const ageInput = document.getElementById('studentAge');
  const emailInput = document.getElementById('studentEmail');
  const phoneInput = document.getElementById('studentnumber');
                     ↓
 バリデーションするdiv id取得
 const nameError = document.getElementById('nameError');
                     ↓
 テーブルid生成
 //テーブルid: studentTableの中のtbodyタグを取得するから
 　この書き方 (getElementbyIDだとエラーに)
 const tableBody = document.querySelector('#studentTable tbody');
                     ↓

 # ボタンが押されたら
  1.テーブルの名前 電話番号　住所　メール表示
   const name = nameInput.value.trim();
    const age = ageInput.value.trim();
    const email = emailInput.value.trim();
    const phone = phoneInput.value.trim();
                     ↓
  2.バリデーションエラー初期化
    → 初期化しないとバリデーションできないからやってる
    nameError.style.display = 'none';
    nameError.textContent = '';
                  ↓
  3.名前が空欄なら、バリデーションメッセージ表示
   if (name === '') {
      nameError.textContent = '名前を入力してください';
      nameError.style.display = 'inline';
                  ↓
  4.エラー消す
  setTimeout(() => { nameError.style.display = 'none'; }, 2000);
      return;
    }      
                    ↓
  5.オブジェクト初期化処理　新規作成
  const data = { name, age, email, phone };
                    ↓

  送信処理　お決まり文発動
  fetch('https://jsonplaceholder.typicode.com/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
                    ↓

  data送信成功したら、テーブル tr要素　作成
  .then(() => {
      const row = document.createElement('tr');
                     ↓
  td要素作成 + 表示
  row.innerHTML = `
        <td>${name}</td>
        <td>${age}</td>
        <td>${email}</td>
        <td>${phone}</td>
      `;
                   ↓
  td要素をtr要素に追加
  tableBody.appendChild(row);
                  ↓
  テーブルに表示されたら、入力欄 空にする
      nameInput.value = '';
      ageInput.value = '';
      emailInput.value = '';
      phoneInput.value = '';
                   ↓
  data送信されなかった時のエラー処理 記入
   .catch(() => {
      alert('送信に失敗しました');
    });
```

### code全文

```php:
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>テーブル表示練習</title>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
  <style>
    .form-group { margin-bottom: 10px; }
    .label { display: inline-block; width: 80px; }
    .error-message { display:none; color:red; margin-left:5px; }
    table, th, td { border:1px solid #000; border-collapse:collapse; padding:5px; }
  </style>
</head>
<body>

<form id="userForm">
  <div class="form-group">
    <label class="label" for="studentName">名前</label>
    <input type="text" id="studentName">
    <div id="nameError" class="error-message" style="display:none;color:red;"></div>
  </div>
  <div class="form-group">
    <label class="label" for="studentAge">年齢</label>
    <input type="number" id="studentAge">
  </div>
  <div class="form-group">
    <label class="label" for="studentEmail">メール</label>
    <input type="email" id="studentEmail">
  </div>
  <div class="form-group">
    <label class="label" for="studentnumber">電話</label>
    <input type="text" id="studentnumber">
  </div>
  <button type="button" id="addButon">追加</button>
</form>

<hr>

<table id="studentTable">
  <thead>
    <tr><th>名前</th><th>年齢</th><th>メール</th><th>電話</th></tr>
  </thead>
  <tbody></tbody>
</table>

<script>
document.addEventListener('DOMContentLoaded', () => {
  const form = document.getElementById('userForm');
  const button = 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');

  button.addEventListener('click', () => {
    // 入力値取得
    const name = nameInput.value.trim();
    const age = ageInput.value.trim();
    const email = emailInput.value.trim();
    const phone = phoneInput.value.trim();

    // エラー初期化
    nameError.style.display = 'none';
    nameError.textContent = '';

    // バリデーション
    if (name === '') {
      nameError.textContent = '名前を入力してください';
      nameError.style.display = 'inline';
      setTimeout(() => { nameError.style.display = 'none'; }, 2000);
      return;
    }

    if (!age || isNaN(age) || Number(age) <= 0) {
      alert('正しい年齢を入力してください');
      return;
    }

    // データオブジェクト作成
    const data = { name, age, email, phone };

    // fetch（実際はダミー送信として使う）
    fetch('https://jsonplaceholder.typicode.com/users', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data)
    })
    .then(() => {
      // 成功したとみなしてテーブルに追加
      const row = document.createElement('tr');
      row.innerHTML = `
        <td>${name}</td>
        <td>${age}</td>
        <td>${email}</td>
        <td>${phone}</td>
      `;
      tableBody.appendChild(row);

      // フォーム初期化
      nameInput.value = '';
      ageInput.value = '';
      emailInput.value = '';
      phoneInput.value = '';
    })
    .catch(() => {
      alert('送信に失敗しました');
    });
  });
});
</script>


</body>
</html>
```