The expected behaviour of the chart is to change and show a different series, when a different option gets selected from the drop down. While changing, it does not animate, even though i have included the "animate" element in the options. Following is my code:
jsfiddle.net/7C53q/
I had this same problem. Finally, I tried re-using the same chart object for the second draw() call, and it worked. It makes sense, when you think about it: how's it supposed to know what the original data was, unless you pass in the chart object drawn with it (or something else containing the original data)?
If you look in the animate example, they create one chart object, then do the draw() in two separate functions, without re-creating the chart object. Please don't ask me why they don't mention this little fact.
Related
I'd like to have dc.js chart which slides along a selection, e.g. in the Nasdaq example https://dc-js.github.io/dc.js/ you would select a sub-selection of time then click "animate" button and the selection filter would slide along the x-axis at a pre-determined step size.
I'm a bit lost as to where one would start...does anyone have any ideas?
Most of what you need to do is set the current filter on the relevant chart based on a timer, instead of based on user interaction.
I've copied the relevant parts of the Nasdaq example into a fiddle to illustrate this: https://jsfiddle.net/0zkbyyqu/9/
Once the charts are in place, the animation is just a matter of changing the filter based on a setInterval. For obscure reasons, we want to use the focus method of the moveChart, not the filter method, but it's essentially doing the same thing, with a little more code to reflect the changes in the range chart:
var beginYear = 1985;
window.setInterval(function() {
moveChart.focus([
new Date(beginYear, 0,0,0,0,0,0),
new Date(beginYear+3, 0,0,0,0,0,0)]);
if(++beginYear > 2009)
beginYear = 1985;
}, 1000);
(If you were using the filter method, you'd have to construct a dc.filters.RangedFilter, as detailed here: Initial Range selection in DC.js chart)
I have left off your idea about the initial selection of the range coming from the user, and just gone with a range of 3 years. This example just starts animating as soon as it is loaded. You can fetch the current filter using chart.filter(); the result will be a RangedFilter, which is an array of two dates. Hopefully it is clear how to add start/stop buttons to the animation.
A couple of things are tricky about this approach:
It's tricky using a chart with transitions when you also have a timer or streaming data. In this case, I had to reduce the transitionDuration to 500ms for it to make any sense, but the cause-and-effect is still a little confusing. Not sure what to do about this.
The area chart transitions are incorrect so you get some weird artifacts.
I am trying to understand and implement the D3 reusable chart pattern.
See sample implementation.
Here the updatableChart is called after selecting the id '#updatableChart'.
Question 1:
If I want to adjust the height parameter by calling as below, in subsequent code, it duplicates the chart.
d3.select('#updatableChart')
.call(updatableChart.height(450));
How should I use this pattern and update the height without affecting the original chart ?
Question 2:
Now if I need another chart, say, under div id '#updatableChart2', I can still call and get the chart using code below.
d3.select('#updatableChart2')
.call(updatableChart)`
But when I try to adjust the height by simply saying updatableChart.height(500); it affects only the second chart. How can I be specific in choosing the chart instance to work on ?
Thank you.
Answer 1:
The reason the chart is drawn again, is because I am calling the updatableChart.height(450) within a D3 selection. Instead it should have been called simply as updatableChart.height(450), to adjust the height.
Answer 2:
The problem was that the same chart object is attached into different div elements.
If another chart is needed under a different div, then the original barchart() should have been instantiated into another copy, say var updatableChart2 = barChart() and used further.
I have a number of graphs, for simplicity take this example on jsfiddle, where there is a brush-on graph, a bar chart keyed on time and a row chart keyed on some categories. In addition, I use the d3-tip library for the tooltips (in the link above a very simplified version of my tip).
In order to avoid the creation of a bar-row in a rowChart, I used the fake-group as outlined in the FAQ of dc-js (and here as well).
The fake group works well, not displaying the C category on the row chart.
However, if I brush on some months with 0 data, when I reset the filter (just click anywhere but the filtered region on the brushon chart), the d3-tip on the row chart disappears.
Notice the if the group is created without the fake-grouping-function, this problem does not arise.
Any explanation why this happens?
How to avoid this (without loosing the remove_empty_bins)?
Although you can use dc.js and d3.js interchangeably, and dc.js is intentionally a "leaky abstraction", some things will go better if you do them the idiomatic dc.js way.
I have two suggestions:
Apply your tooltips in response to dc.js events so that they will get reapplied when new graphical objects are created (or re-created).
Use chart.selectAll instead of d3.selectAll when modifying the charts.
Okay, #2 actually has no bearing on this question, but it does help scope the selects better so that it's harder for them to miss the chart or accidentally modify stuff elsewhere in the page.
Implementing #1 looks something like this:
month_chart.on('pretransition', function(chart) {
chart.selectAll('rect.bar').call(month_tip)
.on('mouseover', month_tip.show).on('mouseout', month_tip.hide);
});
loc_chart1.on('pretransition', function(chart) {
chart.selectAll('g.row').call(loc_tip)
.on('mouseover', loc_tip.show).on('mouseout', loc_tip.hide);
});
The pretransition event fires right after a render or redraw, so it's usually the best moment to manipulate dc's elements. Much better than just running the code globally. I like to set everything up, then call dc.renderAll(), then allow the renders and redraws to take care of themselves later on.
In particular, when those bars get added back in when remove_empty_bins stops removing them, these events will pick them up and re-tip them.
Fork of your fiddle: https://jsfiddle.net/gordonwoodhull/5feL3gko/4/
Could some one please suggest how to do column-drilldown with D3 JS library,
below example is from Hightchart,
http://www.highcharts.com/demo/column-drilldown
A complete code example for this problem is probably quite extensive, so I'll mostly keep to how you would approach it and assume you know enough of D3 to turn the concept into code.
Lets assume you have the functionality for drawing a general bar chart.
Part of that functionality would priobably be things like
Setting up your svg element and containers
Setting up your scales (one for x and one for y)
Adding axes based on the scales you have created
Adding your bars to the svg container
4.1 Make sure you have your data set available as an array
4.2 Create an enter selection for the available data and append rectelements
4.3 Update attributes like x, y for all your available bar nodes
4.4 Remove any nodes on your exit selection
Voila you have a simple bar chart. Nothing new in that and you can have a look at the code details here -> https://bl.ocks.org/mbostock/3885304
Now in order to do the drill down:
In order to avoid lots of code repetition it probably makes sense to separate the above steps into functions. So for example a setup function that just creates your svg and containers as well as your scales.
Important about the setup function is that you do not need to rerun it on drill through.
Second you will want an update function. This contains steps 3+, which you will need to rerun in order to update your charts on drill through.
One addition here would be adding functionality for updating your scale domain in the beginning (as your data changes on drill through and you want to reflect that in your scales).
Now that you have those two functions all you really need to do is:
Add a click handler to your axis labels or your bars (click on bars might be easier for now). You cans use d3's .on() function for that.
In that event you will want to subset your data by the value of the clicked bar (or get a new data set for the bar value depending on how your data is structured) and then run the update function we created above with the new data.
It could look something like this:
d3.selectAll('.bar-nodes')
.on('click', function(d) {
var updatedData = updateData(d);
updateChart(updatedData);
});
If anything is unclear some more specific questions would be good.
Hope that helps.
I am working in d3.js, making a chart object. My chart has an update() method that refreshes the data, using a transition to animate the change. Because I have several different objects to animate within one chart, I wanted to store the transition as a member of the Chart's prototype, then use transition.each wrapped around my transitions to allow all the transitions to inherit the settings of the outer one. I am using setTimeout to schedule updates to the chart over time, so it becomes an animation.
The chart is supposed to show the distribution of means of a skew population, as more and more means are taken. Here's a working version, where the transition is not saved as a member of the chart prototype: http://jsfiddle.net/pmwxcpz7/
Now, I try to modify the above code to store the transition during the constructor of the Chart. When I fetch the saved transition object and call transition.each() in the chart's update method, it works for the first several hundred calls, then stops working. Here's the above code, with the creation of the transition moved to the constructor: http://jsfiddle.net/whtfny15/1/
When I run the second version of the code, the chart stops animating part of the way through, and the console shows many errors saying
TypeError: t.__transition__[e] is undefined (d3.v3.min.js:5)
I have reduced the code to the minimum needed to generate the error here: http://jsfiddle.net/bw1e2vLo/1/ (note that the error shows in the console, but this script doesn't produce any visual output.)
When I run the code (in Firefox), watching the console, I get 200 errors, all of which say
TypeError: t.__transition__[e] is undefined (d3.v3.min.js:5)
I'm not sure what's going on. I would like to be able to save the transition object into my chart's prototype, so I can reuse the delay and duration settings for future animations.
Thanks for the help!
It's not a good idea to store D3 transitions because they are stateful -- what happens when you operate on them depends on whether they are scheduled, running, or finished. If they are finished you can't in fact do a lot with them at all, as you've discovered. In general, you should only ever manipulate a transition when creating it.
The D3 way to do multiple transitions is to re-select the elements to operate on and initialise a new transition. You can store the parameters (delay and duration) in separate variables if you want to reuse them. If you're using a custom function for the transition itself, you can also save that separately and "assign" it to the transition using .attrTween().
I thought I would add that I opened an issue for this question on the d3 GitHub, and Mr. Bostock suggested using transition.call() to apply the settings in a callback right before using the transition.
The code would look something like this:
function myTransitionSettings(transition) {
transition
.duration(100)
.delay(0);
}
// Then later…
d3.transition().call(myTransitionSettings)…
The callback has access to the current transition object, so it can apply the same settings to each new transition that invokes it.