为何创建Tabulator对象后立即调用addColumn()失败,后续调用正常?
问题原因
- Tabulator实例化属于异步渲染流程:即便构造函数执行完毕、初始列已显示,表格内部的核心DOM结构(比如表头容器、表格骨架节点)仍未完全初始化完成。此时调用
addColumn(),方法会尝试向尚未创建的DOM节点执行appendChild操作,从而抛出Cannot read properties of null错误。 - 手动延迟能临时解决问题,本质是给Tabulator预留了足够的内部DOM构建时间,但这种方案不可靠——不同环境的加载速度差异会导致延迟时长难以适配。
合理解决方法
利用Tabulator官方提供的**tableBuilt事件**,该事件会在表格完全构建完成(包含DOM渲染、初始列加载)后触发,此时执行列添加操作可彻底规避竞态条件。
修改后的代码示例
- 在Tabulator配置中添加
tableBuilt回调:
AJS.toInit(function () { gToday = new Date(); gToday.setMinutes(gToday.getMinutes() - gToday.getTimezoneOffset()); // Prepare Tabulator tables dataTable = new Tabulator("#HoursTabulatorDiv", { autoColumns: false, height: 500, columns: [ // 你的初始列配置(保持原代码不变) { title:"Selected", field:"Selected", hozAlign:"center", formatter:"tickCross", width:90, frozen:true, formatterParams:{ allowEmpty:false, allowTruthy:false, }, cellClick: function(e, cell) {} }, // 其他初始列... ], // 新增:表格构建完成后触发回调 tableBuilt:function(){ run(); // 此时再执行添加列操作 } }); });
run()函数保持原逻辑即可:
function run() { dataTable.addColumn({title:"test", field:"test"}); for (let i=1; i<=MAXWEEKS; i++) { dataTable.addColumn({ title:"Week "+i.toString()+" Hours", field:"week"+i.toString()+"hours", minWidth: 50, resizable:true, editable: true, editor: "number", visible: true }).then(function(column){ console.log("the column was added", i); }).catch(function(error){ console.log("there was a problem", i, error.message); }); console.log("adding column to data table", i); } }
替代方案(适配高版本Tabulator)
部分Tabulator新版本支持实例的promise属性,可通过await等待初始化完成:
AJS.toInit(async function () { gToday = new Date(); gToday.setMinutes(gToday.getMinutes() - gToday.getTimezoneOffset()); // Prepare Tabulator tables dataTable = new Tabulator("#HoursTabulatorDiv", { autoColumns: false, height: 500, columns: [/* 你的初始列配置 */] }); // 等待表格初始化完成 await dataTable.promise; run(); });
补充说明
- 禁止在Tabulator构造函数执行后立即调用DOM相关API(如
addColumn、updateColumn),必须等待内部渲染流程彻底完成。 tableBuilt事件是官方推荐的表格就绪监听方式,兼容性更强,适用于所有Tabulator版本。
内容的提问来源于stack exchange,提问作者krj
相关产品推荐
相关产品推荐

