解决DataTable重复初始化引发的Maximum call stack size exceeded错误
解决DataTable重复初始化引发的栈溢出错误
错误现象
控制台抛出栈溢出错误:
code.jquery.com_jquery-3.7.0.js:3871 Uncaught RangeError: Maximum call stack size exceeded at access (code.jquery.com_jquery-3.7.0.js:3871:22) at jQuery.fn.init.attr (code.jquery.com_jquery-3.7.0.js:7862:10) at containsPmdClassPrefix (pro.propeller.in_docs_js_propeller-user-manual.js:181:15) at processNode (pro.propeller.in_docs_js_propeller-user-manual.js:149:8) at pro.propeller.in_docs_js_propeller-user-manual.js:173:6 at pro.propeller.in_docs_js_propeller-user-manual.js:139:5 at pro.propeller.in_docs_js_propeller-user-manual.js:174:7 at pro.propeller.in_docs_js_propeller-user-manual.js:174:7
问题定位
报错源于多处重复销毁/初始化DataTable实例,以及冗余的循环检测逻辑:
- 下拉框
change事件中手动销毁表格后,又调用create_certain_ipc_log_table,而该函数内部再次执行销毁+初始化操作,导致重复触发。 checkSelectChange函数通过requestAnimationFrame无限循环,且内部直接操作表格时dataToUse未定义,触发无效的表格初始化逻辑,加剧循环调用。- 原表格初始化代码未彻底清理实例状态,导致DOM与DataTable实例绑定混乱。
优化解决方案
核心修改点
- 移除冗余的
checkSelectChange循环检测逻辑,下拉框change事件已足够处理表格更新。 - 统一表格初始化逻辑,在
create_certain_ipc_log_table中确保实例彻底销毁后再重建,避免重复操作。 - 简化下拉框
change事件代码,不再手动销毁表格,交由create函数统一处理。
修正后的关键代码段
// 移除原有的checkSelectChange函数及调用 // function checkSelectChange() { ... } // requestAnimationFrame(checkSelectChange); // 修正下拉框change事件 $("#exampleFormControlSelect1").change(function() { var selectedOption = $(this).find(":selected"); selectedDeviceId = selectedOption.data("device_id")?.toString() || ""; sending_data_to_api_log_file_list.device_id = selectedDeviceId; // 直接调用create函数,不再手动销毁表格 create_certain_ipc_log_table(sending_data_to_api_log_file_list); }); // 修正create_certain_ipc_log_table中的表格初始化逻辑 function create_certain_ipc_log_table(sending_data_to_api_log_file_list) { // ... 原有defaultData和dataToUse逻辑保持不变 ... // 统一处理表格实例:先销毁再重建,确保状态干净 if (certain_ipc_log_table) { // 先销毁实例,解除DOM绑定 certain_ipc_log_table.destroy(true); // true参数会清空表格DOM certain_ipc_log_table = null; } // 重新初始化表格 certain_ipc_log_table = $("#certain_ipc_log_table_example").DataTable(dataToUse); }
完整修正代码
$(document).ready(() => { const getToken = localStorage.getItem("token"); var rowData; var selectedDeviceId = ""; newValue = localStorage.getItem("newValue"); var accountType = localStorage.getItem("acc_type"); var certain_ipc_log_table; let sending_data_to_api_log_file_list = { "draw": 1, "length": 10, "start": 0, "device_id": "", "search": "", "start_datetime": "", "end_datetime": "" } let df = { draw: 0, columns: ["device_id", "name", "group"], }; $.ajax({ type: "post", url: newValue + '/api/ipc/select', async: true, headers: { "Content-Type": "application/json", "accept": "application/json", Authorization: "Bearer " + getToken, }, data: JSON.stringify(df), success: function(res) { var selectElement = document.getElementById("exampleFormControlSelect1"); selectElement.innerHTML = ""; var defaultOption = document.createElement("option"); defaultOption.textContent = "Server"; defaultOption.value = ''; defaultOption.dataset.device_id = ''; selectElement.appendChild(defaultOption); res.data.forEach(function(item) { var option = document.createElement("option"); option.textContent = item.group + " - " + item.device_id + " - " + item.name; option.dataset.group = item.group; option.dataset.device_id = item.device_id; option.dataset.name = item.name; selectElement.appendChild(option); }); }, error: function(error) { console.trace("ERROR", error); } }); // 初始化默认表格 create_certain_ipc_log_table(); // 下拉框change事件处理 $("#exampleFormControlSelect1").change(function() { var selectedOption = $(this).find(":selected"); selectedDeviceId = selectedOption.data("device_id")?.toString() || ""; sending_data_to_api_log_file_list.device_id = selectedDeviceId; // 直接调用创建表格函数,内部统一处理销毁/重建 create_certain_ipc_log_table(sending_data_to_api_log_file_list); }); function create_certain_ipc_log_table(sending_data_to_api_log_file_list) { let defaultOrder = "desc"; let defaultOrderColumn = "id"; newValue = localStorage.getItem("newValue"); var defaultData = { processing: true, lengthChange: true, serverSide: true, lengthMenu: [10,100], ajax: { url: newValue + '/api/log/files/list', type: "POST", contentType: 'application/json', data: function(d) { d.search = d.search.value; d.order_by = defaultOrder; d.order_column = defaultOrderColumn; return JSON.stringify(d); }, headers: { Authorization: "Bearer " + getToken, } }, columns: [ { data: "filepath", title: "filepath" }, { data: "filename", title: "filename" }, { data: "extension", title: "extension" }, { data: "modified", title: "modified" }, { data: "size", title: "size" }, { data: null, title: accountType === "OP" ? "" : "ACTION", visible: accountType !== "OP", render: function(data, type, row) { if (accountType === "OP") { return ""; } else { return ( '<button type="button" class="btn btn-outline-light btn-edit-delete" id="Edit_Data" data-toggle="modal" data-target="#exampleModal" data-whatever="" onclick="download_certain_ipc_log_to_txt(' + selectedDeviceId + ', \'' + row.filepath + '\', \'' + row.filename + '\')">Download</button>' ); } }, } ], error: function(xhr, status, error) { console.trace("xhr ERROR: ", xhr.responseText); console.trace("status ERROR: ", status); console.trace("error ERROR: ", error); } }; var dataToUse = sending_data_to_api_log_file_list ? { ...defaultData, ajax: { ...defaultData.ajax, data: function(data) { sending_data_to_api_log_file_list.search = data.search.value; return JSON.stringify(sending_data_to_api_log_file_list); }, }, } : defaultData; // 统一处理表格实例:彻底销毁后重建 if (certain_ipc_log_table) { certain_ipc_log_table.destroy(true); // true参数自动清空表格DOM certain_ipc_log_table = null; } certain_ipc_log_table = $("#certain_ipc_log_table_example").DataTable(dataToUse); } });
说明
- 使用
destroy(true)替代手动empty(),DataTable会自动清理表格DOM元素及绑定的事件,避免残留状态导致冲突。 - 移除冗余的循环检测逻辑,减少不必要的函数调用,从根源避免无限循环触发栈溢出。
- 统一表格初始化入口,所有表格更新操作都通过
create_certain_ipc_log_table处理,避免多处操作实例导致的混乱。
内容的提问来源于stack exchange,提问作者080_coder_from_jap
相关产品推荐
相关产品推荐

