質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.93%

Todoリスト タスクを削除した後のID番号が連番になるようにしたい。

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 196

gorinesia

score 2

前提・実現したいこと

Todoリストを作成しています。
IDについてお伺いしたいです。
タスクを削除した後のID番号が連番になるようにしたいです。
(フィルタリングしても、idは元々のidが割り振られているままの状態が前提です。)

まず、何行かタスクを追加します。
ID:0,1,2,3となっています。
ID:1のタスクを削除した場合、ID:0,2,3となります。
これはIDが振り直されていない状態です。

現状

削除前
ID   コメント  状態
0      aaaaaa
1      bbbbbb  ←ID:1の行のタスクを消した場合 
2      cccccc 
3      dddddd 

削除後
ID   コメント  状態
0       aaaaaa
2       cccccc 
3       dddddd ←IDは振り直されない, そのまま ID: 0,2,3 となる

次に、実現したい挙動です。
ID:0,1,2,3となっています。
ID:1のタスクを削除した場合、ID:0, 2, 3ではなく、ID:0,1,2となり、IDが新しく振り直されています。
この挙動を実現したいです。

実現したい挙動

削除前
ID   コメント  状態
0      aaaaaa
1      bbbbbb   ←ID:1の行のタスクを消した場合
2      cccccc
3      dddddd

削除後
ID   コメント  状態
0       aaaaaa
1       cccccc ←改めてIDが振り直され、ID: 0,2,3 ではなく、 ID: 0,1,2 となる
2       dddddd

該当のソースコード

<!DOCTYPE html>
<html lang="ja">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>ToDoリスト</title>
</head>

<body>
  <h1>ToDoリスト</h1>

  <form name="radioform">
    <label for="radio-all-select"><input type="radio" name="radio1" value="all" id="radio-all-select" checked>すべて</label>
    <label for="radio-woking-select"><input type="radio" name="radio1" value="working" id="radio-woking-select">作業中</label>
    <label for="radio-done-select"><input type="radio" name="radio1" value="done" id="radio-done-select">完了</label>
  </form>

  <table>
    <thead>
      <tr>
        <th>ID</th>
        <th>コメント</th>
        <th>状態</th>
      </tr>
    </thead>
    <tbody id="todo-body"></tbody>
  </table>

  <h2>新規タスクの追加</h2>
  <label for="input-todo-box">ToDoリスト</label>
  <input id="input-todo-box" type="text">
  <button id="add-button">追加</button>

  <script src='main.js'></script>
</body>

</html>
  // 入力したTodoタスクの一覧を保持する配列を定義する
  // IDのための変数を用意する
  const todos = [];
  let nextId = 0;

  //  HTMLのID値を使って以下のDOM要素を取得する
  //  入力ボックス
  //  追加ボタン
  //  Todoリストを一覧表示するためのtbody
  //  ラジオボタン
  const inputBox = document.getElementById('input-todo-box');
  const addButton = document.getElementById('add-button');
  const tableBody = document.getElementById('todo-body');
  const radioButton = document.getElementsByName('radio1');

  // 「追加」ボタンがクリックされたときの処理を実装する //
  addButton.addEventListener('click', () => {
    const todo = {id: nextId++, comment: inputBox.value, status: '作業中' }
    inputBox.focus();


    // 空文字が入力されたときの処理
    if (inputBox.value === '') {
      alert('タスクを入力してください!');
      return;
    }
    //todoがtrueの場合の処理
    if (todo) {
      todos.push(todo);
      inputBox.value = '';
      showTodos(todos);
      filterTodos();
    }
  });


  // 「todos」の中身を一覧表示するための関数を用意する //
  const showTodos = (todos) => {
    tableBody.textContent = '';

    // 値を1つずつ取り出し、繰り返し処理を実行
    todos.forEach((todo) => {

      // tbody要素に追加するためのtr要素を作成し、子要素としてtrを追加 //
      const tableRecord = document.createElement('tr');
      tableBody.appendChild(tableRecord);

      // tr要素に追加するためのtd要素をそれぞれ作成する //
      const tableId = document.createElement('th');
      const tableComment = document.createElement('td');
      const tableStatus = document.createElement('td');
      const tableAction = document.createElement('td');

      // 要素の中身のテキストを表示するため、オブジェクトの要素から取得する //
      tableId.textContent = todo.id;
      tableComment.textContent = todo.comment;

      // td要素をtr要素の子要素として追加する //
      tableRecord.appendChild(tableId);
      tableRecord.appendChild(tableComment);
      tableRecord.appendChild(tableStatus);
      tableRecord.appendChild(tableAction);

      tableStatus.appendChild(createStatusButton(todo));
      tableAction.appendChild(createDeleteButton(tableRecord, todo.id));
    });
  };

  //「状態機能」を管理するボタンを生成する関数
  const createStatusButton = (todo) => {
    const statusButton = document.createElement('button');
    statusButton.textContent = todo.status;
    statusButton.addEventListener('click', () => {
      if (todo.status === '作業中') {
        todo.status = '完了';
        filterTodos()
      } else {
        todo.status = '作業中';
        filterTodos();
      }
    });
    return statusButton;
  };

  //「削除機能」を管理するボタンを生成する関数
  const createDeleteButton = (tableRecord, id) => {
    let index = tableRecord.rowIndex - 1;
    const deleteButton = document.createElement('button');
    deleteButton.textContent = '削除';
    deleteButton.addEventListener('click', () => {
      const targetIndex = todos.findIndex(todo => {
        return todo.id === id;
      });
      todos.splice(targetIndex, 1);
      filterTodos();
    });
    return deleteButton;
  };

  // 表示・非表示の機能を管理する関数
  const filterTodos = () => {
    if (radioButtonAll.checked) {
      return showTodos(todos);
    } else if (radioButtonWorking.checked) {
      const doingTodos = todos.filter(todo =>  {return todo.status === '作業中'});
      return showTodos(doingTodos);
    } else if (radioButtonDone.checked) {
      const doneTodos = todos.filter(todo => {return todo.status === '完了'});
      return showTodos(doneTodos);
    }
  };

  radioButton.forEach((e, number) => {
    radioButton[number].addEventListener('click', () => {
      filterTodos();
    });
  });

発生している問題

私の実現したい機能は、
フィルタリングしてもIDは変わらない
タスク削除後IDが新しく振り直されるの2つを兼ね備えたものです。

どちらか1つの機能は実現できるのですが、両方の機能を実現できません。

IDを表示するに当たり、以下の2つの方法で試みました。

1. forEachの中でtd要素を作り、todos配列のindex番号をIDの値として使用。
→タスクを削除した場合、IDが振り直される。◯
→フィルタリングした場合、IDは元のIDのまま。✕

2. nextIdを定義し、id: nextId++ とする。
→タスクを削除した場合、IDが振り直される。☓
→フィルタリングした場合、IDは元のIDのまま。◯

現状nextIdのやり方でIDを定義しています。
nextId++としているのでIDが只々増えていってしまうのは承知の上なのですが、
どうすればidを振り直すことができるのか解決方法が見つけられません。

実装できない原因として、以下のものが浮かんでいます。
・定義したidの管理の仕方を把握していない。
・idの取得方法が間違っている。
・idを毎回取得する仕組みを理解していない。

試したこと

以下の2つの箇所で問題があると思い、改善を試みました。
オブジェクトに入れるID
もしくは、
td要素のtextContent

td要素のtext.Contentをtodo.idからnextId++にしてみたり、
indexやnumberを入れ替えたりしてみましたが上手くいかず、
根本的に実装方法が思いつきません。

削除ボタンを押したら、idを0から振り直す、
みたいな関数も作れるような気はするんですが、
どう作ればいいか検討が付きません。

IDをindexにした場合、逆にnextIdとした場合、
どちらも意図した挙動にならないのは理解できます。
しかし、それを解決する方法が見つけられない現状です。

どのように考えていけば良いのか、ヒントや実装方法を教えていただけたら幸いです。
以上です。
よろしくお願いいたします!

補足情報(FW/ツールのバージョンなど)

エディタ:Visual Stadio Code

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • sousuke

    2020/07/06 22:30

    idが増えていくことの何が問題なのかを伝えた方がいいです。連番のidを使ってどうしたいですか?

    キャンセル

  • gorinesia

    2020/07/06 23:17

    行を削除したらIDが振り直されているようにしたいっていうことなんですが、私の表現の仕方がわかりにくいのでしょうか。
    読んで頂く方に伝わるように心がけて、例を書いたりしたのですが、難しいですね。
    もう少し考えて質問を編集します。

    キャンセル

  • sousuke

    2020/07/06 23:37

    そこではなくて、言い方を変えると「欠番になるとなにが問題なのか」がわからないです。

    キャンセル

  • gorinesia

    2020/07/07 00:03

    欠番になることでなにか問題があるわけではありません。
    単純に見本通りに作りたい、といった所でしょうか。
    現在作成しているToDoリストの見本がそのような仕様になっているので、それを目指して作っています。

    キャンセル

回答 2

checkベストアンサー

+1

うーん認識違いな気がする。
表示されている左端の『質問者さんがidと言っているもの』は
『idではなくて表示中のリストの順番』だと思います。私ならそうしますが。

というかソースだといろいろ動かないゾ…試しにこうしてみてください。

  // 「todos」の中身を一覧表示するための関数を用意する //
  const showTodos = (todos) => {
    tableBody.textContent = '';

    // 値を1つずつ取り出し、繰り返し処理を実行
+   // 引数増やしてインデックス取得
-    todos.forEach((todo) => {
+    todos.forEach((todo,i) => {

      // tbody要素に追加するためのtr要素を作成し、子要素としてtrを追加 //
      const tableRecord = document.createElement('tr');
      tableBody.appendChild(tableRecord);

      // tr要素に追加するためのtd要素をそれぞれ作成する //
      const tableId = document.createElement('th');
      const tableComment = document.createElement('td');
      const tableStatus = document.createElement('td');
      const tableAction = document.createElement('td');

      // 要素の中身のテキストを表示するため、オブジェクトの要素から取得する //
-      tableId.textContent = todo.id;
+      // インデックスを表示
+      tableId.textContent = i;
      tableComment.textContent = todo.comment;

      // td要素をtr要素の子要素として追加する //
      tableRecord.appendChild(tableId);
      tableRecord.appendChild(tableComment);
      tableRecord.appendChild(tableStatus);
      tableRecord.appendChild(tableAction);

      tableStatus.appendChild(createStatusButton(todo));
      // deleteのtodo.idはそのまま渡す
      tableAction.appendChild(createDeleteButton(tableRecord, todo.id));
    });
  };

  //「状態機能」を管理するボタンを生成する関数
  const createStatusButton = (todo) => {
    const statusButton = document.createElement('button');
    statusButton.textContent = todo.status;
    statusButton.addEventListener('click', () => {
      if (todo.status === '作業中') {
        todo.status = '完了';
        filterTodos()
      } else {
        todo.status = '作業中';
        filterTodos();
      }
    });
    return statusButton;
  };

  //「削除機能」を管理するボタンを生成する関数
  const createDeleteButton = (tableRecord, id) => {
    let index = tableRecord.rowIndex - 1;
    const deleteButton = document.createElement('button');
    deleteButton.textContent = '削除';
    deleteButton.addEventListener('click', () => {
      const targetIndex = todos.findIndex(todo => {
        return todo.id === id;
      });
      todos.splice(targetIndex, 1);
      filterTodos();
    });
    return deleteButton;
  };
+  // これ未定義だよ!!!!!!!!!!!!!!!!!!!!!!
+  const radioButtonAll = document.getElementById('radio-all-select');
+  const radioButtonWorking = document.getElementById('radio-woking-select');
+  const radioButtonDone = document.getElementById('radio-done-select');
  // 表示・非表示の機能を管理する関数
  const filterTodos = () => {
    if (radioButtonAll.checked) {
      return showTodos(todos);
    } else if (radioButtonWorking.checked) {
      const doingTodos = todos.filter(todo => {
        return todo.status === '作業中'
      });
      return showTodos(doingTodos);
    } else if (radioButtonDone.checked) {
      const doneTodos = todos.filter(todo => {
        return todo.status === '完了'
      });
      return showTodos(doneTodos);
    }
  };

追記

削除後に振りなおすしかないでしょうね。

 // 「追加」ボタンがクリックされたときの処理を実装する //
 addButton.addEventListener('click', () => {
   // ここでidをとるとキャンセルしたときにおかしい
   inputBox.focus();

   // 空文字が入力されたときの処理
   if (inputBox.value === '') {
     alert('タスクを入力してください!');
     return;
   }
   //todoがtrueの場合の処理
   // この書き方はすべてtrueなので意味なし
   //if (todo) {
   // push直前でid取得
   const todo = {
     id: todos.length,
     comment: inputBox.value,
     status: '作業中'
   }
   todos.push(todo);
   inputBox.value = '';
   showTodos(todos);
   filterTodos();
   //}
 });

 //「削除機能」を管理するボタンを生成する関数
 const createDeleteButton = (tableRecord, id) => {
   let index = tableRecord.rowIndex - 1;
   const deleteButton = document.createElement('button');
   deleteButton.textContent = '削除';
   deleteButton.addEventListener('click', () => {
     const targetIndex = todos.findIndex(todo => {
       return todo.id === id;
     });
     todos.splice(targetIndex, 1);
     // 削除後に削除id以上を振りなおす
     for (var i = targetIndex; i < todos.length; i++) {
       todos[i].id = i
     }
     filterTodos();
   });
   return deleteButton;
 };

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/07 00:44 編集

    アドバイスありがとうございます!
    元々はtableId.textContentをindexにしてました。
    それだと削除後の挙動は問題ないのですが、フィルタリングした際にidが0からになってしまうので、考えた末現在のコードになっています。
    ラジオボタンのid取得のコードはteratailに載せる際に間違えて消してしまってました、すいません。
    ご指摘くださり、ありがとうございます。

    キャンセル

  • 2020/07/07 01:25

    とりあえず削除後に正しく振りなおすしかないですね。
    普通はそれをやる必要性を全く感じないので回答が付きにくいのだと思います。

    キャンセル

  • 2020/07/07 07:20

    ご回答ありがとうございます!
    自身のコードに反映させて、実現できました!
    具体的な実装方法や的確なコメントなど、非常にわかりやすいです。
    idの値の書き方や削除後にIDを振り直す方法など、とても参考になります。
    質問自体に対してのアドバイスも今後に活かしていきます。
    伝わりにくい質問にお付き合いくださり、ありがとうございました!

    キャンセル

+1

イマイチなにをどうしたいかがわかりません。
配列なら振り直すのが楽だと思いますが・・・

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/06 22:32

    var data=[
    [1,"aaaaaa"],
    [2,"bbbbbb"],
    [3,"cccccc"],
    [4,"dddddd"],
    ];
    data.splice(2,1);//ccccccを削除
    console.log(data);
    data=data.sort((x,y)=>x[0]-y[0]).map((x,y)=>[y+1,x[1]]);
    console.log(data);

    キャンセル

  • 2020/07/06 23:20

    わかりにくいのに関わらず、ご回答くださり、ありがとうございます。
    もっとわかりやすく質問を編集します。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.93%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る