IDが連番になるように振り分ける

解決済

回答 3

投稿 編集

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

hoshinokawa

score 1

前提・実現したいこと

「削除ボタンをクリックした際に、連番になるように番号(ID)を振り直したい。」

初心者、且つ初質問になります。
ToDoリストを作っています。
内容はサンプルをコピペしたもので、自作の部分はほぼありません。
「削除ボタン押下→タスクの追加」、に伴うIDの振り分けが上手くいきません。

現在エラーは出ておらず、解決の糸口に繋がるアイデイアが中々生まれずに立ち往生しております。
ヒントやググり方など関節的なモノでも構いません、宜しくお願い致します!

発生している問題・エラーメッセージ

例)
①タスクを追加していく(IDが1,2,3,4…と連番になる)
②2を削除
③IDが134 と表示される 
④③にタスクを追加していく(IDが1,3,4、5…)
⑤削除と追加を繰り返していくと(4,5,6,7,7…)という風に連番にならずIDも被る

該当のソースコード

// 削除ボタンを生成
    const createDeleteButton = task => {
        const deleteButton = document.createElement('button');
        deleteButton.textContent = '削除';
        deleteButton.addEventListener('click', () => {
            task.remove();
            updateId();
        });
        return deleteButton;
    }

    // 番号 再振り分け
    const updateId = () => {
        const taskList = document.getElementsByTagName('tr');
        taskId = 0;
        Array.from(taskList, tr => {
            //
            if (taskId !== 0){
                tr.getElementsByTagName('td').textContent = taskId;
            }
            taskId++
        });
        taskId--;
    }


html

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <title>Javascript Kadai_2</title>
        <link rel="stylesheet" href="css/test_5.css">
    </head>
    <body>
        <h1>ToDoリスト</h1>
        <input type="radio" name="status" id="all" value="all" checked="checked">すべて
        <input type="radio" name="status" id="working" value="working">作業中
        <input type="radio" name="status" id="complete" value="complete">完了 
    <table id="table">
        <tr>
        <thead>
            <th>ID</th>
            <th>コメント</th>
            <th>状態</th>
        </thead>
        </tr>
        <tbody id="tbody"></tbody>
    </table>

    <h2>新規タスクの追加</h2>
    <input id="task" type="text">
    <button type="submit" id="submit">追加</button>
    <script src="js/main_3.js"></script>
</body>

</html>

css

.hide {
    display: none;
}

JavaScript
ソースコード

    // タスクの通し番号定義 0 を代入しないとNaN が表示される
    let taskId = 0;;
    // リストの表示を変える関数
    const changeList = status => {
        const trAll = document.getElementsByTagName('tr');
        Array.from(trAll, tr => {
            tr.classList.remove('hide');
        });
        if (status === 'working'){
            const trComplete = document.getElementsByClassName('complete');
            Array.from(trComplete, tr => {
                tr.classList.add('hide');
            });
        } else if (status === 'complete'){
            const trWorking = document.getElementsByClassName('working');
            Array.from(trWorking, tr => {
                tr.classList.add('hide');               
            });
        }
    }

    // ラジオボタンをクリックした場合にstatusの中身を取得して表示を変える関数
    const statusRadioButtons = document.getElementsByName('status');
    Array.from(statusRadioButtons, statusRadioButton => {
        statusRadioButton.addEventListener('click', e => {
            const status = e.target.value;
            changeList(status);
        });
    });

    // チェックボタンの状態を取置
    const getRadioButtonStatus = () => {
        const status = document.getElementsByName('status:checked').value;
        return status;
    }

    // 削除ボタンを生成
    const createDeleteButton = task => {
        const deleteButton = document.createElement('button');
        deleteButton.textContent = '削除';
        deleteButton.addEventListener('click', () => {
            task.remove();
            updateId();
        });
        return deleteButton;
    }

    // 番号 再振り分け
    const updateId = () => {
        const taskList = document.getElementsByTagName('tr');
        taskId = 0;
        Array.from(taskList, tr => {
            //
            if (taskId !== 0){
                tr.getElementsByTagName('td').textContent = taskId;
            }
            taskId++
        });
        taskId--;
    }

    // 状態ボタンを生成
    const createStatusButton = task => {
        const statusButton = document.createElement('button');
        statusButton.textContent = '作業中';
        task.classList.add('working');
        // ボタン押下時にタスクの状態を入れ替える
        statusButton.addEventListener('click', () => {
            task.classList.toggle('working');
            task.classList.toggle('complete');
            if (statusButton.textContent === '作業中'){
                statusButton.textContent = '完了';
            } else {
                statusButton.textContent = '作業中';
            }
        });
        return statusButton;
    }

    // タスクを追加する
    document.getElementById('submit').addEventListener('click', () => {
        // フォームからタスクの中身を含む用をを取得
        const taskForm = document.getElementById('task');
        //  フォームの中身=contentsをチェック
        if (taskForm.value !== ''){
            taskStr = taskForm.value;
            taskForm.value = '';
            taskId +=1;
            // タスク部分のDOM作成
            const task = document.createElement('tr');
            const taskIdArea = document.createElement('td');
            const taskTextArea = document.createElement('td');
            const buttonArea = document.createElement('td');
            const deleteButton = createDeleteButton(task);
            const statusButton = createStatusButton(task);
            taskIdArea.textContent = taskId;
            taskTextArea.textContent = taskStr;
            buttonArea.appendChild(statusButton);
            buttonArea.appendChild(deleteButton);
            task.appendChild(taskIdArea);
            task.appendChild(taskTextArea);
            task.appendChild(buttonArea);
            document.getElementById('tbody').appendChild(task);
            changeList(getRadioButtonStatus());
        }
    });

試したこと

以下の部分に追加
let sortNum = []; // 予め用意しておく。

/ タスクを追加する
document.getElementById('submit').addEventListener('click', () => {
    // フォームからタスクの中身を含む用をを取得
    const taskForm = document.getElementById('task');
    //  フォームの中身=contentsをチェック
    if (taskForm.value !== ''){
        taskStr = taskForm.value;
        taskForm.value = '';
        taskId +=1;
        // タスク部分のDOM作成
        const task = document.createElement('tr');
        const taskIdArea = document.createElement('td');
        const taskTextArea = document.createElement('td');
        const buttonArea = document.createElement('td');
        const deleteButton = createDeleteButton(task);
        const statusButton = createStatusButton(task);
        taskIdArea.textContent = taskId;

/////////////////////////////////////////////
        sortNum.push(taskId); // 追加のコード
        sortNum.sort();
//////////////////////////////////////////////

        taskTextArea.textContent = taskStr;
        buttonArea.appendChild(statusButton);
        buttonArea.appendChild(deleteButton);
        task.appendChild(taskIdArea);
        task.appendChild(taskTextArea);
        task.appendChild(buttonArea);
        document.getElementById('tbody').appendChild(task);
        changeList(getRadioButtonStatus());
    }
}); 

✅回答者の方々、時間を割いて頂きまして有難うございました。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

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

  • gouf

    2019/09/12 11:22

    参考にされたコードはどの情報からですか? URL は追記可能ですか

    キャンセル

  • sousuke

    2019/09/12 15:15

    質問者さんがどうしたい、こうあるべきだと思っている形を追記されるべきかと。
    タスクを追加してIDが1,2,3,4とあるときに2を消すと
    ・そもそも1,3,4となるべきなのか
    ・1,2,3ではないのか
    仮に1,3,4となった場合次に追加するIDは
    ・5なのか
    ・4なのか
    仕組み的なものがないと回答もコードになりません。

    キャンセル

  • hoshinokawa

    2019/09/12 20:41

    つい先程、解決済みとなりました。
    ご指摘頂いた点は次回以降、投稿の際に参考にさせていただきます!
    お時間を割いて頂き有難うございました。

    キャンセル

回答 3

checkベストアンサー

+1

これシンプルにバグってるなぁ・・・しかもけっこーひどい設計かも・・・
全部リファクタはめんどいからしないけど、updateIdは以下がいいんじゃないかな。

    // 番号 再振り分け
    const updateId = () => {
        const tbody = document.getElementById("tbody");
        const taskList = tbody.getElementsByTagName('tr');
        taskId = 0;
        Array.from(taskList, tr => {
            taskId++
            tr.getElementsByTagName('td')[0].textContent = taskId;
        });
    }

元のはdocumentからtrタグを全取得してたけど、明らかにいらんもんまでとってたんで、tbody配下の奴だけとると。
んでtaskIdをややこしいいじり方してたんでそこを直したのと、
tr.getElementsByTagName('td').textContenttr.getElementsByTagName('td')[0].textContentにしたと。

1,2,3の1を消したら、1,2になる感じ(2,3じゃなく)。多分これが意図通りだと思うけど・・・

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/09/12 13:44

    ご回答頂きありがとうございます。

    バグってるんですか!?
    かなり参考になりそうで助かります!
    直ぐにでも試してみたい所なのですが、後ほど経過報告させていただきます。

    キャンセル

  • 2019/09/12 20:33

    先程確認いたしました。
    まさに意図した通りの挙動になりました!
    初めてteratailを利用したのですが、ドンピシャな回答を頂いて感激です。
    また機会が御座いましたら何卒宜しくお願い致します。

    キャンセル

  • 2019/09/13 09:18

    よかったです。俺は暇なときしか見てないけど、きっと誰かが助けてくれたりくれなかったりするさ!

    キャンセル

+1

とりあえずここは違うと思います。

// tr.getElementsByTagName('td').textContent = taskId;
//                                ↓
   tr.getElementsByTagName('td')[ 0 ].textContent = taskId;

【Document.getElementsByTagName() - Web API | MDN】
https://developer.mozilla.org/ja/docs/Web/API/Document/getElementsByTagName

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/09/12 13:02

    迅速にご回答頂きまして、ありがとうございます。
    参考にさせて頂きます!

    キャンセル

+1

タグ名で連番を取るのはリスクが高いような気がしますが一応

<script>
window.addEventListener('DOMContentLoaded', ()=>{
  var n=document.getElementsByTagName('div');
  n[2].parentNode.removeChild(n[2]);
  alert('stop');//1,2,4,5が表示
  [].forEach.call(n,(x,y)=>x.textContent=(y+1));
  //1,2,3,4が表示
});
</script>
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
</body>

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2019/09/12 12:55

    ご回答頂きまして、ありがとうございます。
    参考ににさせて頂きます!

    キャンセル

  • 2019/09/12 13:20

    「javascript タグ 連番 リスク」
    で検索してみました。

    ご回答頂いた内容は以下のサイトにある様な意味合いでしょうか?


    https://www.google.com/amp/s/blog.ohgaki.net/javascript-injection-risky-functions-and-features/amp

    キャンセル

  • 2019/09/12 13:24 編集

    リスクについて
    単純に当該するタグが他の箇所にある可能性が否定できないからです
    クラスなどを利用するのが賢明です

    キャンセル

  • 2019/09/12 13:51

    成る程、正しくおっしゃる通りですね!
    完全に腑に落ちました。
    有難うございます。

    キャンセル

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

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