💻

[jQuery] 一覧画面の各行頭にチェックボックスを設置して一括削除するUIの作り方

2020/06/18に公開

jQueryが話題 なのでたまにはjQueryネタを書いてみます!

こんな感じの、行を複数選択して一括削除するUIをjQueryで作る方法を紹介します✋

1. マークアップ

デモ動画はBootstrap4の見た目になっていますが、ここでは素のHTMLで書きます。

マークアップはこんな感じにしておきます。

  • 少しでも安全性を上げるためにフォームには onsubmit="return confirm('本当に削除してよいですか?')" を仕込んでおく
  • idの羅列を送信する用の <input type="hidden" name="ids"> を用意しておく
  • 一括削除... ボタンは最初は非表示にしておく
  • 全選択用と個別選択用のチェックボックスにそれぞれクラス名を付けておく
  • 個別選択用のチェックボックスの値はその行のidとしておく

あたりがポイントです。

<form action="/path/to/delete_multiple" method="post" onsubmit="return confirm('本当に削除してよいですか?')">
  <input type="hidden" name="ids">
  <button class="button-delete-all" style="display:none">一括削除...</button>
</form>

<table>
  <thead>
  <tr>
    <th><input type="checkbox" class="check-delete-all"></th>
    <th>ID</th>
    <th>xxx</th>
    <th>xxx</th>
  </tr>
  </thead>
  <tbody>
  <tr>
    <td><input type="checkbox" class="check-delete" value="1"></td>
    <td>1</td>
    <td>xxx</td>
    <td>xxx</td>
  </tr>
  <tr>
    <td><input type="checkbox" class="check-delete" value="2"></td>
    <td>2</td>
    <td>xxx</td>
    <td>xxx</td>
  </tr>
  <tr>
    <td><input type="checkbox" class="check-delete" value="3"></td>
    <td>3</td>
    <td>xxx</td>
    <td>xxx</td>
  </tr>
  :
  :
  </tbody>
</table>

CSRF対策とかバックエンド側のことはこの記事ではとりあえず無視していますのでご注意ください🙏

2. とりあえずチェックボックスの振る舞いを実装

まずは全選択・個別選択のチェックボックスの振る舞いを実装しましょう。

以下のような感じで行けます。

$(function () {
  $('.check-delete-all').on('click', function () {
    $('.check-delete').prop('checked', !allChecked());
  });
  
  $('.check-delete').on('click', function (e) {
    $('.check-delete-all').prop('checked', allChecked());
  });
  
  function allChecked() {
    let result = true;
  
    $('.check-delete').each(function () {
      if (!$(this).prop('checked')) {
        result = false;
        return false;
      }
    });
  
    return result;
  }
});

3. チェックボックスの状態が変わったら <input> の値と 削除... ボタンの表示/非表示を更新する

次に、チェックボックスの状態が変わったら自動で

  • <input type="hidden" name="ids"> に選択されているidを列挙した文字列をセットする
  • 削除... ボタンの表示/非表示を切り替える

ようにします。

  $(function () {
    $('.check-delete-all').on('click', function () {
      $('.check-delete').prop('checked', !allChecked());
+     updateView();
    });
    
    $('.check-delete').on('click', function (e) {
      $('.check-delete-all').prop('checked', allChecked());
+     updateView();
    });
    
    function allChecked() {
      let result = true;
    
      $('.check-delete').each(function () {
        if (!$(this).prop('checked')) {
          result = false;
          return false;
        }
      });
    
      return result;
    }
+   
+   function updateView() {
+     let ids = [];
+  
+     $('.check-delete:checked').each(function () {
+       ids.push($(this).val());
+     });
+  
+     $('input[name="ids"]').val(ids.join(','));
+  
+     if (ids.length > 0) {
+       $('.button-delete-all').show();
+     } else {
+       $('.button-delete-all').hide();
+     }
+   }
  });

4. Shift+クリックで複数選択できるようにする

ここまででとりあえず機能としては正常に動きますが、使い勝手をよくするために Shift+クリック で複数選択できるようにしてあげましょう。

.on('click', function (e) { ... }) という感じでイベントを受け取って、 e.shiftKey でShiftキーを押しながらのクリックだったかどうかの真偽値が取れます。

  $(function () {
    $('.check-delete-all').on('click', function () {
      $('.check-delete').prop('checked', !allChecked());
      updateView();
    });
    
    $('.check-delete').on('click', function (e) {
+     if (e.shiftKey) {
+       let $target = $(this);
+       while (true) {
+         $target = $target.closest('tr').prev('tr').find('.check-delete');
+   
+         if ($target.length === 0 || $target.prop('checked')) {
+           break;
+         }
+   
+         $target.prop('checked', true);
+       }
+     }
+   
      $('.check-delete-all').prop('checked', allChecked());
      updateView();
    });
    
    function allChecked() {
      let result = true;
    
      $('.check-delete').each(function () {
        if (!$(this).prop('checked')) {
          result = false;
          return false;
        }
      });
    
      return result;
    }
    
    function updateView() {
      let ids = [];
    
      $('.check-delete:checked').each(function () {
        ids.push($(this).val());
      });
    
      $('input[name="ids"]').val(ids.join(','));
    
      if (ids.length > 0) {
        $('.button-delete-all').show();
      } else {
        $('.button-delete-all').hide();
      }
    }
  });

まとめ

最終的なコードは以下のようになりました。

<form action="/path/to/delete_multiple" method="post" onsubmit="return confirm('本当に削除してよいですか?')">
  <input type="hidden" name="ids">
  <button class="button-delete-all" style="display:none">一括削除...</button>
</form>

<table>
  <thead>
  <tr>
    <th><input type="checkbox" class="check-delete-all"></th>
    <th>ID</th>
    <th>xxx</th>
    <th>xxx</th>
  </tr>
  </thead>
  <tbody>
  <tr>
    <td><input type="checkbox" class="check-delete" value="1"></td>
    <td>1</td>
    <td>xxx</td>
    <td>xxx</td>
  </tr>
  <tr>
    <td><input type="checkbox" class="check-delete" value="2"></td>
    <td>2</td>
    <td>xxx</td>
    <td>xxx</td>
  </tr>
  <tr>
    <td><input type="checkbox" class="check-delete" value="3"></td>
    <td>3</td>
    <td>xxx</td>
    <td>xxx</td>
  </tr>
  :
  :
  </tbody>
</table>
$(function () {
  $('.check-delete-all').on('click', function () {
    $('.check-delete').prop('checked', !allChecked());
    updateView();
  });
  
  $('.check-delete').on('click', function (e) {
    if (e.shiftKey) {
      let $target = $(this);
      while (true) {
        $target = $target.closest('tr').prev('tr').find('.check-delete');
  
        if ($target.length === 0 || $target.prop('checked')) {
          break;
        }
  
        $target.prop('checked', true);
      }
    }
  
    $('.check-delete-all').prop('checked', allChecked());
    updateView();
  });
  
  function allChecked() {
    let result = true;
  
    $('.check-delete').each(function () {
      if (!$(this).prop('checked')) {
        result = false;
        return false;
      }
    });
  
    return result;
  }
  
  function updateView() {
    let ids = [];
  
    $('.check-delete:checked').each(function () {
      ids.push($(this).val());
    });
  
    $('input[name="ids"]').val(ids.join(','));
  
    if (ids.length > 0) {
      $('.button-delete-all').show();
    } else {
      $('.button-delete-all').hide();
    }
  }
});

jQuery便利〜😇

GitHubで編集を提案

Discussion