尝试在 Bootstrap 模态内动态生成图表,出现 offsetWidth 错误

Trying to dynamically generate chart inside a Bootstrap modal, getting offsetWidth Error

我正在使用基于 HTML5 的库 chartjs 在 Bootstrap 模态主体内动态绘制图表。一切都很好,我可以看到正确的数据和标签数组已被传递,并且 canvas HTML 节点已创建,但我收到此错误:

Uncaught TypeError: Cannot read property 'offsetWidth' of undefined

我想也许我调用这个函数太早了:

new Chart(--> HTML node <--).get(0).getContext("2d").Bar(--> data <--);

所以我把它放在 setTimeout() 块中,以便在附加 canvas HTML 节点大约 2 秒后调用该函数,但我仍然收到该错误并且看不到图表。

这是完整的代码 (github, Dropbox),这是我调用图表库的部分:

document.getElementById("modalBody").appendChild(canvasElement);

setTimeout(function () {
    var ctx = document.getElementsByTagName("canvas")[0];
    var myNewChart = new Chart(ctx).get(0).getContext("2d").Bar(finalData);
    return finalData;
}, 2000);

我设法让这个在 Plunkr 中工作。

我为 contextualizeForType2()contextualizeForType3() 所做的基本更改如下:

  1. 实例化条形图组件的正确方法是使用您附加的 canvas 元素的 2D 上下文。在代码中,var ctx = ... 行后有 new Chart(ctx.getContext('2d')).Bar(finalData);。但是,由于模态框尚未显示,因此它的高度和宽度将为 0。
  2. 要让图表使用正确的高度和宽度,您应该绑定到 shown.bs.modal 事件,该事件在模态完成动画后触发。在该事件处理程序中是您创建图表的地方。棘手的部分是因为您有两个不同的问题重用相同的模式。所以我添加了一个额外的命名空间 .typeX,当模态被隐藏时我取消注册我的事件。

我知道第二点令人困惑,但希望代码能解决问题:

var ctx = document.getElementsByTagName("canvas")[0];
$('#myModal').on({
  'shown.bs.modal.type2': function() {
    new Chart(ctx.getContext("2d")).Bar(finalData);
  },
  'hidden.bs.modal.type2': function() {
    this.off('.type2');
  }
});

这意味着一旦模态被隐藏,这两个函数将不会在为另一种类型显示相同模态时再次获得 运行。此外,由于每次单击按钮时都会添加一个新的处理程序,因此您不会创建比需要更多的图表。

替代方法

我有 a further play,发现我可以将代码重构为一个函数,可以从 contextualizeForType2()contextualizeForType3() 调用,传入 finalData .这样做的好处是将所有 canvas 和图表初始化代码放在一个地方,稍微简化您的大型函数并使其更易于维护。

函数是:

function showModalChart(data) {
  var canvasElement = document.createElement("CANVAS");
  canvasElement.setAttribute("width", "400");
  canvasElement.setAttribute("height", "400");

  document.getElementById("modalBody").appendChild(canvasElement);

  var ctx = document.getElementsByTagName("canvas")[0];
  $('#myModal').off('.modalchart').on('shown.bs.modal.modalchart', function () {
      new Chart(ctx.getContext('2d')).Bar(data);
  });
}