Closed timelyportfolio closed 7 years ago
Good question. I didn't realize this as an issue until now.
When using d2b charts without vuejs it's pretty easy to dynamically add your own event binding. Because the chart is rendered synchronously you can just programmatically setup the events after you have rendered the chart. E.g.
var sunburst = d2b.chartSunburst();
var chart = d3.select('.chart');
chart.call(sunburst);
chart.selectAll('.d2b-sunburst-arc').on('mouseover.custom', function (d) {
// do something when a sunburst arc is moused over..
console.log(d);
});
The vue.js components have watchers to check when the data or config have changed and then automatically call the chart updater. Probably the best way to implement this would be to have a beforeRender
and rendered
event hook on the vue components. So that would allow you to do something like..
<div id = 'app'>
<sunburst-chart
:data = 'sunburstChartData'
@rendered = 'sunburstChartRendered'>
</sunburst-chart>
</div>
var app = new Vue({
el: '#app',
components: {
'sunburst-chart': d2b.vueChartSunburst
},
data: {
sunburstChartData: {
// some sunburst data
}
},
methods: {
sunburstChartRendered: function (el, chart) {
d3.select(el).selectAll('.d2b-sunburst-arc')
.on('mouseover.custom', function (d) {
// do something when a sunburst arc is moused over..
console.log(d);
});
}
}
});
I'll think some more about this and work on getting something implemented tomorrow.
One additional note per to your original question. I've found it difficult to add events to the d2b generators themselves. E.g. chart.on(...)
. This is because a generator might be used for rendering many charts and then the events could have been invoked from anyone of them.
I published a d2b update this morning on v0.0.28. You should be able to bind events whenever the vueChartSunburst component emits a rendered
event. I updated my previous answer to show how this can be used. The beforeRender
and rendered
events are passed the element node el
and chart generator chart
.
Another thing to note is that most d2b internal events are bound to the default event namespace. This means that if you are working with the d2b-sunburst-arc elements and you bind an event like d3.select('.d2b-sunburst-arc').on('click', ...)
this will likely override the internal click event and you won't be able to click to zoom anymore. Name-spacing the event like d3.select('.d2b-sunburst-arc').on('click.custom', ...)
should do the trick though.
Let me know if this is what you were looking for. Thanks!
Working through it now. Will report back. Thanks!!!
@kevinwarne, here is what I ended up doing even though the result is still not perfect.
sunburstChartRendered: function (el, chart) {
var that = this;
d3.select(el).selectAll('.d2b-sunburst-chart')
.on('mouseover', function (d) {
if(d3.event.target.classList[0] === 'd2b-sunburst-arc'){
that.filtered_tree = d3.select(d3.event.target).datum().data;
}
});
When selecting .d2b-sunburst-arc
, I would lose the click.custom
handler after a couple of clicks, so I chose to move the handler to the .d2b-sunburst-chart
level. Here is the live example https://bl.ocks.org/timelyportfolio/8c9a77da3e9ca57827a8801fb8a16ba9.
Thanks again!
@timelyportfolio This is a great example of extending the sunburst to a treemap on mouseover.
I can see why the click event is failing to work after a couple tries. It's because the internal chart zooming is actually 'exiting' nodes and then they arn't being rebound upon entering again. I'll think more about how best to achieve something like this as it is probably a common use case.
In the meantime this is a solution that would work, however it would require disabling the sunburst zooming so that nodes don't get removed..
// disable zooming on the chart
sunburstChartConfig: function () {
chart.label(function(d){return d.name});
chart.color(function(d){return color(d.name);})
chart.sunburst().size(function(d){return d.x}).zoomable(false);
// // another option if you wanted to remove the breadcrumbs for this particular visualization
// chart.chartFrame().breadcrumbsEnabled(false);
// // yet another option for horizontal breadcrumbs across the top
// chart.chartFrame().breadcrumbsOrient('top');
// chart.breadcrumbs().vertical(false);
}
// apply custom events
sunburstChartRendered: function (el, chart) {
var that = this;
d3.select(el).selectAll('.d2b-sunburst-arc')
.on('click.custom', function (d) { // click or mouseover can be used here
that.filtered_tree = d.data;
});
}
Cheers!
@timelyportfolio I think I've come up with a solution to this d2b application.
Any d2b generators that render content to the page will fire an event when they are applied either externally or internally. This event is bubbled up through the DOM. For example:
var sunburst = d2b.chartSunburst();
var chart = d3.select('.chart').on('breadcrumbs-updated', customizeBreadcrumbs);
chart.call(sunburst);
function customizeBreadcrumbs() {
// This function will be called whenever the breadcrumbs are updated.
};
So for your purposes we want to rebind the custom events whenever the arcs are updated (entered or exited). The d2b.svgSunburst is in charge of updating the sunburst arcs so we can expect a 'svg-sunburst-updated' event to bubble up through the DOM whenever this d2b component is updated. Something like this "should" do the trick as of v0.0.29.
<div id = 'app'>
<sunburst-chart
:data = 'sunburstChartData'
ref = 'sunburst'
@svg-sunburst-updated.native = 'bindArcEvents'
>
</sunburst-chart>
</div>
var app = new Vue({
el: '#app',
components: {
'sunburst-chart': d2b.vueChartSunburst
},
data: {
sunburstChartData: {
// some sunburst data
}
},
methods: {
bindArcEvents: function () {
var that = this,
el = this.$refs.sunburst.$el;
d3.select(el).selectAll('.d2b-sunburst-arc')
.on('mouseover.custom', function (d) { // click or mouseover can be used here
that.filtered_tree = d.data;
});
}
}
});
Very interesting, so d3
events bubble up as native
? I really appreciate you looking at this!
So the native
identifier used when binding an event for a vue component means that the event should be bound to the component's root node $el
rather than the vue instance itself. So if you were listening for a click
event on the entire sunburst chart component you could just use @click.native = 'someFunction'
.
The vue documentation briefly touches on it here
@timelyportfolio I'll go ahead and close this issue for now. Feel free to reopen if you have further question or issues on the matter.
I want get the Data(the object) on which bar/point(in case of line chart) the user have clicked. is there any way to get the exact object?
I am also interested in an answer to kjhatis question. So how can I get the event if a user clicks on a certain bar of a barchart?
Thanks...
Sorry, I must have missed @kjhatis message. This can be done with plain d3 after the chart has been rendered. Just bind a click event and listen for the datum
in the handler.
note: the datum will actually be a wrapper of the original user supplied datum with some other d2b specific attributes available. If you want the original datum just console.log
d.data
in the example below instead ofd
d3.selectAll('.d2b-bar-group').on('click', function (d) { console.log(d) })
Are there any examples of event handling, such as
click
ormouseover
, withvuejs
charts? I looked forchart.on
but it is not defined.