You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Grid.js中实现列切换并保持状态不丢失?

问题

我用Grid.js展示多列表格,想给用户加个切换指定列可见性的功能。现在已经做了带复选框的下拉菜单来控制列的显示/隐藏,但有两个问题:

  • 页面刷新后列状态保存不住
  • 用Grid.js内置搜索功能后,列会恢复默认状态

我试过用DOM操作控制列显示,但效果不好,现在求正确实现方案,要求能持久化用户的列选择,而且搜索的时候不会重置列状态。

下面是我当前的简化代码:

function updateTableWithDBData() {
  fetch("/getTransactions")
    //Code for grabbing data to populate columns
      });

      // Dropdown for showing/hiding columns
      const dropdown = document.createElement('div');
      dropdown.classList.add('dropdown');

      const dropdownButton = document.createElement('button');
      dropdownButton.classList.add('dropbtn');
      dropdownButton.textContent = 'Toggle Columns';

      const dropdownContent = document.createElement('div');
      dropdownContent.classList.add('dropdown-content');

      const dropdownList = document.createElement('ul');

      for (const column of columns) {
        const listItem = document.createElement('li');

        const checkboxLabel = document.createElement('label');
        checkboxLabel.classList.add('checkbox-label');
        checkboxLabel.textContent = column;

        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.checked = true;

        checkbox.addEventListener('change', function () {
          const columnIndex = columns.indexOf(column);
          const headerCell = document.querySelector('#grid th:nth-child(' + (columnIndex + 1) + ')');
          const cells = document.querySelectorAll('#grid td:nth-child(' + (columnIndex + 1) + '), #grid th:nth-child(' + (columnIndex + 1) + ')');

          if (checkbox.checked) {
            headerCell.style.display = '';
            for (const cell of cells) {
              cell.style.display = '';
            }
          } else {
            headerCell.style.display = 'none';
            for (const cell of cells) {
              cell.style.display = 'none';
            }
          }
        });

        checkboxLabel.appendChild(checkbox);
        listItem.appendChild(checkboxLabel);
        dropdownList.appendChild(listItem);
      }

      dropdownContent.appendChild(dropdownList);
      dropdown.appendChild(dropdownButton);
      dropdown.appendChild(dropdownContent);

      // Hide all headers and columns initially
      const headerCells = document.querySelectorAll('#grid th');
      const dataCells = document.querySelectorAll('#grid td');
      for (const headerCell of headerCells) {
        headerCell.style.display = 'none';
      }
      for (const cell of dataCells) {
        cell.style.display = 'none';
      }

      document.getElementById('grid-dropdown').appendChild(dropdown);

      const grid = new gridjs.Grid({
        columns: columns.map((column) => ({ name: column, id: column, formatter: 'html' })),
        data: gridData,
        sort: true,
        search: true,
        storage: 'local'
      });

         // Function to update the grid with the current state of the toggle boxes
    const updateGridWithCheckboxState = () => {
      const headerCells = document.querySelectorAll('#grid th');
      const dataCells = document.querySelectorAll('#grid td');

      for (const [index, column] of columns.entries()) {
        const checkbox = document.querySelector(`#grid-dropdown input:nth-of-type(${index + 1})`);
        const headerCell = headerCells[index];
        const cells = document.querySelectorAll(`#grid td:nth-child(${index + 1}), #grid th:nth-child(${index + 1})`);

        if (checkbox && headerCell && cells.length > 0) {
          if (checkbox.checked) {
            headerCell.style.display = '';
            for (const cell of cells) {
              cell.style.display = '';
            }
          } else {
            headerCell.style.display = 'none';
            for (const cell of cells) {
              cell.style.display = 'none';
            }
          }
        }
      }
    };

    // Function to save the state of the toggle boxes to local storage
    const saveCheckboxStateToLocalStorage = () => {
      const checkboxStates = {};
      const checkboxes = document.querySelectorAll('#grid-dropdown input[type="checkbox"]');

      for (const [index, checkbox] of checkboxes.entries()) {
        const column = columns[index];
        checkboxStates[column] = checkbox.checked;
      }

      localStorage.setItem('checkboxStates', JSON.stringify(checkboxStates));
    };

    // Function to load the state of the toggle boxes from local storage
    const loadCheckboxStateFromLocalStorage = () => {
      const checkboxStates = JSON.parse(localStorage.getItem('checkboxStates'));

      if (checkboxStates) {
        const checkboxes = document.querySelectorAll('#grid-dropdown input[type="checkbox"]');

        for (const [index, checkbox] of checkboxes.entries()) {
          const column = columns[index];
          if (checkboxStates[column] !== undefined) {
            checkbox.checked = checkboxStates[column];
          }
        }
      }
    };

    // Attach event listener to checkboxes to update grid and save state to local storage
    const checkboxes = document.querySelectorAll('#grid-dropdown input[type="checkbox"]');
    checkboxes.forEach((checkbox) => {
      checkbox.addEventListener('change', () => {
        updateGridWithCheckboxState();
        saveCheckboxStateToLocalStorage();
      });
    });

    // Load the checkbox state from local storage and update the grid
    loadCheckboxStateFromLocalStorage();
    updateGridWithCheckboxState();

      grid.render(gridContainer);
    })
    .catch((error) => {
      console.error('Error:', error);
    });
}

document.addEventListener('DOMContentLoaded', () => {
  updateTableWithDBData();
});

场景截图

  • 正常状态下的网格:正常状态下的网格
  • 隐藏2列后的网格:隐藏2列后的网格
  • 搜索时的网格:搜索时的网格
  • 取消搜索后的网格(列已重置):取消搜索后的网格(列已重置)
  • 页面刷新后的网格(复选框未勾选但列仍显示):页面刷新后的网格(复选框未勾选但列仍显示)
解决方案

核心思路是放弃直接操作DOM控制列显示,改用Grid.js原生的列配置管理可见性,同时把列状态持久化到localStorage,并且监听Grid的渲染事件,确保每次Grid更新(搜索、排序等)后都能保持用户的列选择。

关键改进步骤

  1. 用Grid.js的hidden属性控制列可见性:不再修改DOM元素的display样式,而是给列配置添加hidden属性,从根源避免Grid重渲染时的状态丢失
  2. 持久化列状态到localStorage:初始化时加载保存的状态,状态变化时同步保存
  3. 状态变化时更新Grid配置:复选框切换时,更新列配置并强制Grid重新渲染
  4. 监听Grid渲染事件:同步复选框和列状态,避免出现不一致的情况

完整修改后的代码示例

function updateTableWithDBData() {
  fetch("/getTransactions")
    .then(response => response.json())
    .then(data => {
      const columns = data.columns;
      const gridData = data.rows;

      // 定义存储键名
      const STORAGE_KEY = 'gridColumnVisibility';

      // 加载保存的列可见性状态,默认全部显示
      const loadColumnVisibility = () => {
        const saved = localStorage.getItem(STORAGE_KEY);
        return saved ? JSON.parse(saved) : columns.reduce((acc, col) => ({...acc, [col]: true}), {});
      };

      // 保存列可见性状态到localStorage
      const saveColumnVisibility = (state) => {
        localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
      };

      // 初始化列状态
      let columnVisibility = loadColumnVisibility();

      // 创建下拉复选框
      const dropdown = document.createElement('div');
      dropdown.classList.add('dropdown');

      const dropdownButton = document.createElement('button');
      dropdownButton.classList.add('dropbtn');
      dropdownButton.textContent = '切换列显示';

      const dropdownContent = document.createElement('div');
      dropdownContent.classList.add('dropdown-content');

      const dropdownList = document.createElement('ul');

      for (const column of columns) {
        const listItem = document.createElement('li');
        const checkboxLabel = document.createElement('label');
        checkboxLabel.classList.add('checkbox-label');
        checkboxLabel.textContent = column;

        const checkbox = document.createElement('input');
        checkbox.type = 'checkbox';
        checkbox.checked = columnVisibility[column];

        // 复选框切换事件:更新状态并重新渲染Grid
        checkbox.addEventListener('change', () => {
          columnVisibility[column] = checkbox.checked;
          saveColumnVisibility(columnVisibility);
          updateGridColumns();
        });

        checkboxLabel.appendChild(checkbox);
        listItem.appendChild(checkboxLabel);
        dropdownList.appendChild(listItem);
      }

      dropdownContent.appendChild(dropdownList);
      dropdown.appendChild(dropdownButton);
      dropdown.appendChild(dropdownContent);
      document.getElementById('grid-dropdown').appendChild(dropdown);

      // Grid实例和渲染容器
      let grid;
      const gridContainer = document.getElementById('grid');

      // 更新Grid列配置并渲染的函数
      const updateGridColumns = () => {
        const updatedColumns = columns.map(col => ({
          name: col,
          id: col,
          formatter: 'html',
          hidden: !columnVisibility[col] // 用hidden属性控制列可见性
        }));

        if (grid) {
          // 已存在Grid实例,更新配置并强制渲染
          grid.updateConfig({ columns: updatedColumns }).forceRender();
        } else {
          // 首次创建Grid实例
          grid = new gridjs.Grid({
            columns: updatedColumns,
            data: gridData,
            sort: true,
            search: true,
            storage: 'local'
          });
          grid.render(gridContainer);
        }
      };

      // 首次渲染Grid
      updateGridColumns();

      // 监听Grid渲染完成事件,同步复选框和列状态
      if (grid) {
        grid.on('rendered', () => {
          const checkboxes = document.querySelectorAll('#grid-dropdown input[type="checkbox"]');
          checkboxes.forEach((checkbox, index) => {
            const col = columns[index];
            checkbox.checked = columnVisibility[col];
          });
        });
      }
    })
    .catch((error) => {
      console.error('加载数据出错:', error);
    });
}

document.addEventListener('DOMContentLoaded', () => {
  updateTableWithDBData();
});

效果说明

  • 页面刷新后,会自动加载用户之前保存的列可见性状态
  • 使用Grid.js的搜索、排序功能时,列状态不会被重置
  • 复选框和列显示状态始终保持一致

内容的提问来源于stack exchange,提问作者Stupie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 08:35:58