チェックボックス対応、複数選択

以下、コードの詳細な説明です。

##HTMLセクション
```.html
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script  src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/bootstrap-table.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/extensions/filter-control/bootstrap-table-filter-control.min.js"></script>
```
### 概要:

	•	必要なライブラリをインポートしています。
	•	Bootstrap: テーブルのデザインやスタイルに使用。
	•	jQuery: DOM操作やイベントハンドリングを簡素化するライブラリ。
	•	Bootstrap Table: テーブルのフィルタリングや検索、ソートを提供するライブラリ。
	•	Filter Control: テーブルにフィルタリング機能を追加する拡張機能。


```.html
<table id="table" 
       data-toggle="table" 
       data-filter-control="true" 
       data-show-search-clear-button="true" 
       data-pagination="true" 
       data-side-pagination="client"></table>
```
### 概要:

	•	id="table" のテーブル要素を定義。
	•	属性に基づいて Bootstrap Table が有効化されます。
	•	data-toggle="table": Bootstrap Tableを適用。
	•	data-filter-control="true": 各カラムにフィルタリング機能を有効化。
	•	data-pagination="true": ページネーションを有効化。
	•	data-side-pagination="client": クライアントサイドでのページネーション処理を指定。

## JavaScriptセクション

### 1. カラムの定義
```.js
const columns = [
  { field: 'id', title: 'ID', sortable: true, filterControl: 'input' },
  { field: 'name', title: 'Name', sortable: true, filterControl: 'select' },
  { field: 'price', title: 'Price', sortable: true, filterControl: 'select' }
];
```
	•	columns:
	•	テーブルのカラム構成を定義。
	•	各カラムは以下を指定可能:
	•	field: データ項目名。
	•	title: 表示名。
	•	sortable: ソートを有効化。
	•	filterControl: フィルタリングの入力形式 (input や select など)。

### 2. データキャッシュ

let dataCache = [];

	•	dataCache:
	•	テーブルに表示するデータを保持。
	•	フィルタリング処理を効率化するために使用。

### 3. 元の <select> 要素を非表示
```.js
function hideSelectElements() {
  $('#table thead').find('select').css({
    display: 'none',
    visibility: 'hidden',
    pointerEvents: 'none',
    position: 'absolute',
    height: '0',
    width: '0',
    overflow: 'hidden',
  });
}
```
	•	目的:
	•	Bootstrap Tableが生成するデフォルトの <select> フィルタリング要素を隠す。
	•	独自のフィルタリングUIを利用するため。

### 4. フィルタリングUIの追加
```.js
function addFilterDropdowns() {
  const $thead = $('#table thead');
  $thead.find('th').each(function () {
    const field = $(this).data('field');
    const column = columns.find(col => col.field === field && col.filterControl === 'select');

    if (column) {
      const $dropdown = $(`
        <div class="dropdown d-inline-block ms-2">
          <button class="btn btn-sm btn-primary dropdown-toggle" type="button" id="dropdown-${field}" data-bs-toggle="dropdown" aria-expanded="false">
            Filter
          </button>
          <ul class="dropdown-menu p-2" aria-labelledby="dropdown-${field}" style="max-height: 200px; overflow-y: auto;">
            <li>
              <label class="form-check-label">
                <input type="checkbox" class="form-check-input select-all-checkbox" data-field="${field}">
                Select All
              </label>
            </li>
          </ul>
        </div>
      `);

      $(this).append($dropdown);

      const $dropdownMenu = $dropdown.find('.dropdown-menu');
      const values = [...new Set(dataCache.map(item => item[field]))];
      values.forEach(value => {
        $dropdownMenu.append(`
          <li>
            <label class="form-check-label">
              <input type="checkbox" class="form-check-input filter-checkbox" data-field="${field}" value="${value}">
              ${value}
            </label>
          </li>
        `);
      });

      $dropdownMenu.on('change', 'input.filter-checkbox', applyFilters);
      $dropdownMenu.on('change', 'input.select-all-checkbox', function () {
        const isChecked = $(this).is(':checked');
        $dropdownMenu.find('.filter-checkbox').prop('checked', isChecked);
        applyFilters();
      });

      hideSelectElements();
    }
  });
}
```
#### 詳細:

	1.	目的:
	•	カスタムドロップダウンを生成して各カラムにフィルタリングUIを追加。
	•	「全選択/選択解除」機能を含む。
	2.	dataCache.map:
	•	データキャッシュから一意な値を抽出し、チェックボックスを生成。
	3.	イベントハンドリング:
	•	filter-checkbox:
	•	チェックされた値を基にフィルタリング処理。
	•	select-all-checkbox:
	•	全てのチェックボックスを選択/解除。

### 5. フィルタリング処理
```.js
function applyFilters() {
  const filters = {};
  $('.filter-checkbox:checked').each(function () {
    const field = $(this).data('field');
    const value = $(this).val();

    if (!filters[field]) {
      filters[field] = [];
    }
    filters[field].push(value);
  });

  const filteredData = dataCache.filter(item =>
    Object.entries(filters).every(([field, values]) => values.includes(String(item[field])))
  );

  $('#table').bootstrapTable('load', filteredData);
  hideSelectElements();
}
```
#### 概要:

	1.	filters:
	•	現在チェックされたフィルタリング条件を収集。
	2.	dataCache.filter:
	•	フィルタリング条件を満たすデータのみ抽出。
	3.	$('#table').bootstrapTable('load'):
	•	抽出したデータをテーブルに再ロード。

### 6. テーブルの再構築
```.js
function rebuildTable() {
  $.ajax({
    url: "https://examples.bootstrap-table.com/json/data1.json",
    method: 'GET',
    success: function (data) {
      dataCache = data;
      $('#table').bootstrapTable('destroy').bootstrapTable({
        data: data,
        columns: columns,
        pagination: true,
        search: true,
        filterControl: true,
        showSearchClearButton: true,
      });
      addFilterDropdowns();
    },
    error: function () {
      console.error("Failed to fetch data from API");
    }
  });
}
```
#### 概要:

	1.	データ取得:
	•	APIからデータを取得してキャッシュに保存。
	2.	テーブル再生成:
	•	Bootstrap Table の再初期化。
	3.	フィルタリングUIの追加:
	•	カラムごとにカスタムUIを追加。

#### 7. 初期化

```.js
$(function () {
  rebuildTable();
});
```
### 概要:

	•	ページ読み込み時にテーブルを構築する関数を呼び出し。

動作の流れ

	1.	テーブルを初期化してデータを表示。
	2.	各カラムにカスタムフィルタリングUIを追加。
	3.	チェックボックス操作によりデータをフィルタリング。
	4.	「全選択/選択解除」ボタンでチェックボックスの状態を一括変更。


- コード全体
```.html
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">

<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/bootstrap-table.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/extensions/filter-control/bootstrap-table-filter-control.min.js"></script>

<table id="table" 
       data-toggle="table" 
       data-filter-control="true" 
       data-show-search-clear-button="true" 
       data-pagination="true" 
       data-side-pagination="client"></table>

<script>
  const columns = [
    { field: 'id', title: 'ID', sortable: true, filterControl: 'input' },
    { field: 'name', title: 'Name', sortable: true, filterControl: 'select' },
    { field: 'price', title: 'Price', sortable: true, filterControl: 'select' }
  ];

  let dataCache = [];

  function hideSelectElements() {
    $('#table thead').find('select').css({
      display: 'none',
      visibility: 'hidden',
      pointerEvents: 'none',
      position: 'absolute',
      height: '0',
      width: '0',
      overflow: 'hidden',
    });
  }

  function addFilterDropdowns() {
    const $thead = $('#table thead');
    $thead.find('th').each(function () {
      const field = $(this).data('field');
      const column = columns.find(col => col.field === field && col.filterControl === 'select');

      if (column) {
        const $select = $(this).find('select');

        const $dropdown = $(`
          <div class="dropdown d-inline-block ms-2">
            <button class="btn btn-sm btn-primary dropdown-toggle" type="button" id="dropdown-${field}" data-bs-toggle="dropdown" aria-expanded="false">
              Filter
            </button>
            <ul class="dropdown-menu p-2" aria-labelledby="dropdown-${field}" style="max-height: 200px; overflow-y: auto;">
              <li>
                <label class="form-check-label">
                  <input type="checkbox" class="form-check-input select-all-checkbox" data-field="${field}">
                  Select All
                </label>
              </li>
            </ul>
          </div>
        `);

        $(this).append($dropdown);

        const $dropdownMenu = $dropdown.find('.dropdown-menu');
        const values = [...new Set(dataCache.map(item => item[field]))];
        values.forEach(value => {
          $dropdownMenu.append(`
            <li>
              <label class="form-check-label">
                <input type="checkbox" class="form-check-input filter-checkbox" data-field="${field}" value="${value}">
                ${value}
              </label>
            </li>
          `);
        });

        $dropdownMenu.on('change', 'input.filter-checkbox', applyFilters);
        $dropdownMenu.on('change', 'input.select-all-checkbox', function () {
          const isChecked = $(this).is(':checked');
          $dropdownMenu.find('.filter-checkbox').prop('checked', isChecked);
          applyFilters();
        });

        hideSelectElements();
      }
    });
  }

  function applyFilters() {
    const filters = {};
    $('.filter-checkbox:checked').each(function () {
      const field = $(this).data('field');
      const value = $(this).val();

      if (!filters[field]) {
        filters[field] = [];
      }
      filters[field].push(value);
    });

    const filteredData = dataCache.filter(item =>
      Object.entries(filters).every(([field, values]) => values.includes(String(item[field])))
    );
    $('#table').bootstrapTable('load', filteredData);
    hideSelectElements();
  }

  function rebuildTable() {
    $.ajax({
      url: "https://examples.bootstrap-table.com/json/data1.json",
      method: 'GET',
      success: function (data) {
        dataCache = data;
        $('#table').bootstrapTable('destroy').bootstrapTable({
          data: data,
          columns: columns,
          pagination: true,
          search: true,
          filterControl: true,
          showSearchClearButton: true,
        });
        addFilterDropdowns();
      },
      error: function () {
        console.error("Failed to fetch data from API");
      }
    });
  }

  $(function () {
    rebuildTable();
  });
</script>
```
Bootstrap Tableのリフレッシュボタンを押した際にカスタムドロップダウンの条件が解除される問題を解決するには、リフレッシュ時にカスタムフィルタの条件を保持して再適用する必要があります。

以下に修正版のコードを示します。このコードでは、リフレッシュボタン押下時にカスタムフィルタリングの条件を保存し、リフレッシュ後に再適用します。


```.js

修正版コード

<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.6.0/dist/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha3/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/bootstrap-table.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap-table@1.23.5/dist/extensions/filter-control/bootstrap-table-filter-control.min.js"></script>

<table id="table" 
       data-toggle="table" 
       data-filter-control="true" 
       data-show-search-clear-button="true" 
       data-pagination="true" 
       data-side-pagination="client" 
       data-toolbar="#toolbar">
</table>

<div id="toolbar">
  <button id="refresh-btn" class="btn btn-primary">Refresh Table</button>
</div>

<script>
  const columns = [
    { field: 'id', title: 'ID', sortable: true, filterControl: 'input' },
    { field: 'name', title: 'Name', sortable: true, filterControl: 'input' },
    { field: 'price', title: 'Price', sortable: true, filterControl: 'select' }
  ];

  let dataCache = []; // データキャッシュを保持
  let activeFilters = {}; // 現在のフィルタ条件を保持

  function saveActiveFilters() {
    activeFilters = {};
    $('.filter-checkbox:checked').each(function () {
      const field = $(this).data('field');
      if (!activeFilters[field]) {
        activeFilters[field] = [];
      }
      activeFilters[field].push($(this).val());
    });
  }

  function applySavedFilters() {
    const filters = activeFilters;
    const filteredData = dataCache.filter(item =>
      Object.entries(filters).every(([field, values]) => values.includes(String(item[field])))
    );
    $('#table').bootstrapTable('load', filteredData);
    // カスタムドロップダウンのチェック状態を復元
    for (const [field, values] of Object.entries(filters)) {
      $(`.filter-checkbox[data-field="${field}"]`).each(function () {
        if (values.includes($(this).val())) {
          $(this).prop('checked', true);
        }
      });
    }
  }

  function addFilterDropdowns() {
    const $thead = $('#table thead');
    $thead.find('th').each(function () {
      const field = $(this).data('field');
      const column = columns.find(col => col.field === field && col.filterControl === 'select');

      if (column) {
        const $select = $(this).find('select');
        if ($select.siblings('.dropdown').length === 0) {
          const $dropdown = $(`
            <div class="dropdown d-inline-block ms-2">
              <button class="btn btn-sm btn-primary dropdown-toggle" type="button" id="dropdown-${field}" data-bs-toggle="dropdown" aria-expanded="false">
                Filter
              </button>
              <ul class="dropdown-menu p-2" aria-labelledby="dropdown-${field}" style="max-height: 200px; overflow-y: auto;">
              </ul>
            </div>
          `);
          $select.after($dropdown);
          const $dropdownMenu = $dropdown.find('.dropdown-menu');
          const values = [...new Set(dataCache.map(item => item[field]))];
          values.forEach(value => {
            $dropdownMenu.append(`
              <li>
                <label class="form-check-label">
                  <input type="checkbox" class="form-check-input filter-checkbox" data-field="${field}" value="${value}">
                  ${value}
                </label>
              </li>
            `);
          });
          $dropdownMenu.on('change', 'input.filter-checkbox', () => {
            saveActiveFilters();
            applySavedFilters();
          });
        }
      }
    });
  }

  function rebuildTable() {
    $.ajax({
      url: "https://examples.bootstrap-table.com/json/data1.json",
      method: 'GET',
      success: function (data) {
        dataCache = data;
        $('#table').bootstrapTable('destroy').bootstrapTable({
          data: data,
          columns: columns,
          pagination: true,
          search: true,
          filterControl: true,
          showSearchClearButton: true,
        });
        addFilterDropdowns();
        applySavedFilters(); // フィルタを再適用
      },
      error: function () {
        console.error("Failed to fetch data from API");
      }
    });
  }

  $(function () {
    rebuildTable();

    $('#refresh-btn').on('click', function () {
      saveActiveFilters(); // リフレッシュ前にフィルタを保存
      rebuildTable();
    });
  });
</script>


```

修正内容

1. フィルタ条件の保存

チェックボックスの状態をactiveFiltersオブジェクトに保存する関数saveActiveFiltersを追加。



2. フィルタの再適用

テーブルリフレッシュ後に保存された条件を再適用するapplySavedFiltersを追加。



3. リフレッシュボタン

リフレッシュ時に現在のフィルタ条件を保存し、リフレッシュ後に再適用。



4. フィルタ状態の復元

カスタムドロップダウン内のチェックボックス状態を復元。





---

動作確認ポイント

1. リフレッシュ時の動作

リフレッシュボタンを押した後でも、カスタムドロップダウンの条件が保持される。



2. フィルタリングの動作

選択されたフィルタ条件が正しく適用されている。



3. UIの一貫性

チェックボックスの選択状態がリフレッシュ後も維持されている。





---

この修正版により、リフレッシュボタンを押してもカスタムドロップダウンの条件が解除されなくなります。必要に応じてさらに調整してください！