如何在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列后的网格:

- 搜索时的网格:

- 取消搜索后的网格(列已重置):

- 页面刷新后的网格(复选框未勾选但列仍显示):

解决方案
核心思路是放弃直接操作DOM控制列显示,改用Grid.js原生的列配置管理可见性,同时把列状态持久化到localStorage,并且监听Grid的渲染事件,确保每次Grid更新(搜索、排序等)后都能保持用户的列选择。
关键改进步骤
- 用Grid.js的
hidden属性控制列可见性:不再修改DOM元素的display样式,而是给列配置添加hidden属性,从根源避免Grid重渲染时的状态丢失 - 持久化列状态到localStorage:初始化时加载保存的状态,状态变化时同步保存
- 状态变化时更新Grid配置:复选框切换时,更新列配置并强制Grid重新渲染
- 监听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
相关产品推荐
相关产品推荐

