I have different html buttons, which call different sets of DC.js graphs/charts/numbers, etc., but at the moment, I don't know how to add an eventlistener to trigger them.
The below code is two of the buttons: all-seasons and s_06. I need to be able to switch between them, by simply clicking the buttons, without having to physically hardcode them in, and by "hardcode" I mean using //
to block out the one I don't want and then reload the page!
I have show_all_info
running on page load, as you can see below.
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);
});
At the moment, show_all_info(ndx)
is loading fine and as it should, but then nothing happens when I click the s_06 button.
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.
}
Here's the actual code (I will paste only 1 pie from each):
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);
}
After addressing Gordon's answer and comments below, I ended up with the following, but the season buttons still don't do anything:
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();
}
}
It's usually okay to change a bunch of parameters and re-render dc.js charts. If you're just changing the data, you can redraw instead of rendering - that way you get transitions.
We've had a conversation in the comments, and I have edited it into your question and my answer.
In the first version you attempted to re-construct the charts by calling the chart constructor (dc.pieChart
, dc.barChart
). The new chart instance may not be able to steal the DOM from the last one.
I don't have an example I can easily test, but I think that if you make sure the charts are only initialized once, that should get things working:
// 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)
In the comments, I mentioned that you should render only the first time, and then redraw whenever the data or filters change.
When you first initialize the chart and set all its parameters, you need to render the chart in order to create the SVG elements.
Later on, when you change the data, you want to redraw the chart so that it will animate from the old data to the new data. (This works with many but not all chart parameters too.)
In version 2 of your code, the only problem should be (hopefully) that you need to set the new data before you redraw.
So instead of
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();
This ought to do the trick:
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();
}
Looking at your repo, I found a few problems:
.on()
with a namespace DOMContentLoaded
didn't fire for me, but you don't need it there because you are already waiting for the data, which will take longer.ndx
, so you need to wrap them in some sort of function that provides them thatevent.preventDefault
is the solution here.script.js event handlers:
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));
// ...
(I guess you'll need to do the same for any events you want to handle twice. Some functionality seems to be duplicated between the two scripts.)
custom_jQuery.js handlers look like this:
d3.select("#s-10").on('click.foo', function() {
Disposing dimension and group on redraw:
if(allPolesPie) {
allPolesPie.dimension().dispose(); // will dispose group as well
allPolesPie
.group(group)
.dimension(dim)
.redraw();
} else {
I've tested this in my clone of your repo, but I'll let you apply it to your own code since I think you are leaning on me a bit too much... there are many parts of this which I think you should learn how to diagnose yourself.
If you continue working with dc.js and d3, you will need to learn how to use the developer tools, especially breakpoints and/or logging, to see what code is getting hit and what the values are.