如何使用Javascript事件监听器调用绘制dc.jscharts/graphs等函数?

How to use Javascript eventlistener to call functions which draw dc.js charts/graphs, etc?

我有不同的html按钮,它们调用不同的DC.jsgraphs/charts/numbers等集合,但目前,我不知道如何添加事件监听器触发它们。

下面的代码是其中两个按钮:all-seasons 和 s_06。我需要能够在它们之间切换,只需单击按钮,而不必对它们进行物理硬编码,"hardcode" 我的意思是使用 // 来屏蔽我不想要的那个然后重新加载页面!

我有 show_all_info 运行 页面加载,如下所示。

show_all_info(ndx);

document.addEventListener("DOMContentLoaded", function() {
    document.getElementById("all-seasons").addEventListener("click", show_all_info);
    document.getElementById("s_06").addEventListener("click", show_06_info);
});

目前,show_all_info(ndx) 加载正常,应该是这样,但是当我单击 s_06 按钮时没有任何反应。

function show_all_info(ndx) {
  show_all_wins_pie(ndx);
  show_all_poles_pie(ndx);
  show_all_fast_laps_pie(ndx);
  show_all_constructor_points(ndx);
}

function show_06_info(ndx) {
  show_wins_pie(ndx, "06");
  show_poles_pie(ndx, "06");
  show_fast_laps_pie(ndx, "06");
  show_points(ndx, "06");
  show_driver_world_champ_chart(ndx, "06", "Fernando Alonso");
  // etc.
}

版本 1:重新初始化图表

这是实际代码(我将只粘贴每个饼图的 1 个):

function show_all_wins_pie(ndx) {
  var dim = ndx.dimension(dc.pluck("win_car"));
  var group = dim.group().reduce(
    function(p, v) {
      p.count++;
      if(v.win_car != "N/A") {
        p.match++;
      } else {
        return 0;
      }
      return p;
    },
    function(p, v) {
      p.count--;
      if(v.win_car != "N/A") {
        p.match--;
      } else {
        return 0;
      }
      return p;
    },
    function() {
      return { count: 0, match: 0 };
    }
  );

  dc.pieChart("#wins-pie")
    .height(200)
    .width(200)
    .radius(100)
    .innerRadius(40)
    .dimension(dim)
    .valueAccessor(function(d) {
      if(d.value.count > 0) {
        return d.value.match;
      } else {
        return 0;
      }
    })
    .group(group)
    .transitionDuration(1000);
}
function show_wins_pie(ndx, season) {
  var dim = ndx.dimension(dc.pluck("win_car"));
  var group = dim.group().reduce(
    function(p, v) {
      if(v.season == season) {
        p.count++;
        if(v.win_car != "N/A") {
          p.match++;
        } else {
          return 0;
        }
      }
      return p;
    },
    function(p, v) {
      if(v.season == season) {
        p.count--;
        if(v.win_car != "N/A") {
          p.match--;
        } else {
          return 0;
        }
      }
      return p;
    },
    function() {
      return { count: 0, match: 0 };
    }
  );

  dc.pieChart("#wins-pie")
    .height(200)
    .width(200)
    .radius(100)
    .innerRadius(40)
    .dimension(dim)
    .valueAccessor(function(d) {
      if(d.value.count > 0) {
        return d.value.match;
      } else {
        return 0;
      }
    })
    .group(group)
    .transitionDuration(1000);
}

版本 2:只渲染第一次

在解决了 Gordon 的回答和下面的评论之后,我得到了以下结果,但季节按钮仍然没有任何作用:

function show_all_wins_pie(ndx) {
  var dim = ndx.dimension(dc.pluck("win_car"));
  var group = dim.group().reduce(
    function(p, v) {
      p.count++;
      if(v.win_car != "N/A") {
        p.match++;
      } else {
        return 0;
      }
      return p;
    },
    function(p, v) {
      p.count--;
      if(v.win_car != "N/A") {
        p.match--;
      } else {
        return 0;
      }
      return p;
    },
    function() {
      return { count: 0, match: 0 };
    }
  );

  if(allWinsPie) {
    allWinsPie.redraw();
  } else {
    allWinsPie = dc.pieChart("#wins-pie");
  allWinsPie
    .height(200)
    .width(200)
    .radius(100)
    .innerRadius(40)
    .dimension(dim)
    .valueAccessor(function(d) {
      if(d.value.count > 0) {
        return d.value.match;
      } else {
        return 0;
      }
    })
    .group(group)
    .transitionDuration(1000);

  allWinsPie.render();
  }
}
function show_wins_pie(ndx, season) {
  var dim = ndx.dimension(dc.pluck("win_car"));
  var group = dim.group().reduce(
    function(p, v) {
      if(v.season == season) {
        p.count++;
        if(v.win_car != "N/A") {
          p.match++;
        } else {
          return 0;
        }
      }
      return p;
    },
    function(p, v) {
      if(v.season == season) {
        p.count--;
        if(v.win_car != "N/A") {
          p.match--;
        } else {
          return 0;
        }
      }
      return p;
    },
    function() {
      return { count: 0, match: 0 };
    }
  );

  if(winsPie) {
    winsPie.redraw();
  } else {
    winsPie = dc.pieChart("#wins-pie")
  winsPie
    .height(200)
    .width(200)
    .radius(100)
    .innerRadius(40)
    .dimension(dim)
    .valueAccessor(function(d) {
      if(d.value.count > 0) {
        return d.value.match;
      } else {
        return 0;
      }
    })
    .group(group)
    .transitionDuration(1000);

  winsPie.render();
  }
}

通常可以更改一堆参数并重新渲染 dc.js 图表。如果您只是更改数据,则可以重绘而不是渲染 - 这样您就可以获得过渡。

我们在评论中进行了对话,我已将其编辑为您的问题和我的回答。

版本 1:不要重新构建图表

在第一个版本中,您尝试通过调用图表构造函数(dc.pieChartdc.barChart)重新构建 图表。新的图表实例可能无法从上一个图表实例中窃取 DOM。

我没有可以轻松测试的示例,但我认为如果您确保图表只初始化一次,那应该可以正常工作:

// global (top) level
let stackedChart, pie;
// ...
function show_all_constructor_points(ndx) {
// ...
  if(!stackedChart)
    stackedChart = dc.barChart("#all-constructor-points");
  // as before, but don't re-construct:
  stackedChart
    .width(width)
    .height(400)
  // ...

function show_wins_pie(ndx, season) {
  // ...
  if(!pie)
    pie = dc.pieChart("#wins-pie")
  pie
    .height(200)
    .width(200)
    .radius(100)

在评论中,我提到您应该只渲染第一次,然后在数据或过滤器发生变化时重新绘制。

版本 2:重绘前设置数据

当您第一次初始化图表并设置其所有参数时,您需要渲染图表以创建 SVG 元素。

稍后,当您更改数据时,您想要重绘 图表,以便它可以从旧数据动画到新数据。 (这也适用于许多但不是所有图表参数。)

在您的代码版本 2 中,唯一的问题应该是(希望如此)您需要在重绘之前设置新数据。

所以不用

  if(winsPie) {
    winsPie.redraw();
  } else {
    winsPie = dc.pieChart("#wins-pie")
  winsPie
    .height(200)
    .width(200)
    .radius(100)
    .innerRadius(40)
    .dimension(dim)
    .valueAccessor(function(d) {
      if(d.value.count > 0) {
        return d.value.match;
      } else {
        return 0;
      }
    })
    .group(group)
    .transitionDuration(1000);

  winsPie.render();

这应该可以解决问题:

  if(winsPie) {
    winsPie
      .group(group)
      .dimension(dim)
      .redraw();
  } else {
    winsPie = dc.pieChart("#wins-pie")
    winsPie
      .height(200)
      .width(200)
      .radius(100)
      .innerRadius(40)
      .dimension(dim)
      .valueAccessor(function(d) {
        if(d.value.count > 0) {
          return d.value.match;
        } else {
          return 0;
        }
      })
      .group(group)
      .transitionDuration(1000)
      .render();
  }

版本 3:事件处理程序已激活且不会相互干扰

查看您的回购协议,我发现了一些问题:

  1. 如您所料,两个文件中的事件处理程序相互干扰。避免这种情况的一种方法是使用 d3 的 .on() with a namespace
  2. 另外 DOMContentLoaded 没有为我开火,但你不需要它,因为你已经在等待数据,这将需要更长的时间。
  3. 此外,您的事件处理程序采用 ndx,因此您需要将它们包装在某种函数中,为它们提供
  4. 最后,当不再需要旧维度时,您需要处理它们,因为 crossfilter 仅支持 32(并且这些对象很重)。
  5. 一个小故障:如果您让锚点点击触发默认处理程序,则页面将在点击时滚动到顶部。 event.preventDefault 是这里的解决方案。

script.js 个事件处理程序:

function wrap_handler(h) { // #3
    return function() {
        d3.event.preventDefault(); // #5
        h(ndx);
    };
}

document.getElementById("all-seasons").addEventListener("click", show_all_info);
d3.select("#s-06").on('click.bar' /* #1 */, wrap_handler(show_06_info));
d3.select("#s-07").on('click.bar', wrap_handler(show_07_info));
// ...

(我想您需要对任何要处理两次的事件执行相同的操作。某些功能似乎在两个脚本之间是重复的。)

custom_jQuery.js 处理程序如下所示:

d3.select("#s-10").on('click.foo', function() {

重绘时处理维度和组:

  if(allPolesPie) {
    allPolesPie.dimension().dispose(); // will dispose group as well
    allPolesPie
      .group(group)
      .dimension(dim)
      .redraw();
  } else {

我已经在您的存储库的克隆中对此进行了测试,但我会让您将其应用到您自己的代码中,因为我认为您对我的依赖有点过分了……其中有很多部分我认为你应该学会如何自我诊断。

如果您继续使用 dc.js 和 d3,您将需要学习如何使用开发人员工具,尤其是断点 and/or 日志记录,以查看命中了哪些代码以及哪些值是。