I have a multi-bar chart in which I've assigned a click event to the bars. This works fine until a user changes the chart type from grouped to stacked, at which point I've discovered that I need to reassign the onClick handler. This all seems to work correctly.
The problem is that after my click handler runs, whether or not the user has changed the chart type yet previously, attempting to change the chart type will result in a "groups.exit(...).watchTransition is not a function" JS error.
Chart definition:
nv.addGraph(function() {
// Defining the chart itself
var chart = nv.models.multiBarHorizontalChart()
.x(function(d) { return d.label })
.y(function(d) { return d.value })
.margin({top: 30, right: 20, bottom: 50, left: 275})
.showValues(true) //Show bar value next to each bar.
.tooltips(true) //Show tooltips on hover.
.valueFormat(d3.format('$,.2f'))
.groupSpacing(0.5)
.showControls(true); //Allow user to switch between "Grouped" and "Stacked" mode.
chart.yAxis
.tickFormat(d3.format('$,.2f'));
d3.select('#chart2 svg')
.datum(barData)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
},
function(){
// Set the click handler. This part works fine, but the onclick handler goes away after changing the chart type and thus needs redefined below.
// PROBLEM POINT: once this code is run, the user can no longer change the chart type. They just keep getting "groups.exit(...).watchTransition is not a function"
d3.selectAll(".nv-bar").on('click',
function(e){
var canName = e.label.split('(');
var canName = $.trim(canName[0]);
var searchTerm = canName + ' ' + e.key;
var detUrl = "/details.cfm?canName=" + encodeURIComponent(canName) + "&searchTerm=" + encodeURIComponent(searchTerm);
$("#detailsDiv").html("Loading...");
$("#detailsDiv").load(detUrl);
location.href = "#details";
});
// If I try to redefine the bar click handler in the radio button's "click" event it overwrites the built in JS used to change the chart type, so instead
// I handle it onMouseUp.
d3.selectAll(".nv-series").on('mouseup',
function(e){
setTimeout(function(){
// Just running this directly on mouseUp doesn't work. Apparently the chart needs time to load first. So we do it 100ms later, which works fine.
d3.selectAll(".nv-bar").on('click',
function(e){
var canName = e.label.split('(');
var canName = $.trim(canName[0]);
var searchTerm = canName + ' ' + e.key;
var detUrl = "/details.cfm?canName=" + encodeURIComponent(canName) + "&searchTerm=" + encodeURIComponent(searchTerm);
$("#detailsDiv").html("Loading...");
$("#detailsDiv").load(detUrl);
location.href = "#details";
});
}, 100);
});
});
watchTransition is defined by nvd3 on D3's selection prototype, if you have nv.d3.js loaded in the browser, you should be able to step with the debugger into the following code before, any chart is rendered:
d3.selection.prototype.watchTransition = function(renderWatch){
var args = [this].concat([].slice.call(arguments, 1));
return renderWatch.transition.apply(renderWatch, args);
};
I had the same issue. The reason was that I was using webpack, which bundled D3 inside my application, so the D3 that was used to draw the chart was not the D3 that NVD3 visits to add the function to the prototype. So if you are using webpack or browserify make sure to exclude D3 and add it only as reference script.
We solved this issue by downgrading to d3 3.4.4, as advised by this comment.
I am trying to make it so that the name of each item shows up in the tooltip when you hover. I am sure there is a straightforward answer to this, but I am new to D3 so I am not sure what it is.
Example here: http://www.chloesilver.ca/favouritethings/object/
You can see that when you hover, some crazy code shows up so I obviously did it wrong.
In the D3 script, I did this:
$('svg circle').tipsy({
gravity: 'w',
html: true,
title: function() {
var o = colors.domain;
return o;
}
});
Where I am trying to call a specific domain label that was specified previously in the code. I was able to do this with a CSV, but the sticky bit here is that all the information is held within the script inside the HTML document.
Delete the code for the tooltips and after line 236: .call(force.drag) add the following:
.on("mouseover", function(d) {
$(this).tipsy({
gravity: 'w',
html: true,
title: function() {
return d.name;
}
})
});
You should now see the name of each item as a tooltip.
I have the following code that allows me to increase the size of a circle when it is clicked. I want to add a transition effect, but it isn't working:
D3.js
d3.selectAll(".bubble-node")
.on("click", function (d) {
$("#circle-" + d.id).transition().duration(1000).attr("r", r);
d.forceR = r; // forceR is a property on the data object
});
HTML/svg
<a class="bubble-node" id="bubble-id-3" style="fill: #62d5f4">
<circle id="circle-8" r="65"></circle>
</a>
What am I missing?
not sure exactly you're trying to do but i think the selector is wrong for d.id
try using this.id
$(".bubble-node")
.on("click", function (d) {
$("#circle-" + this.id).transition().duration(1000).attr("r", r);
d.forceR = r; // forceR is a property on the data object
});
but then the id would not match up when you concatenate "#circle-bubble-id-3" doesn't exist in the DOM. So you might want to change the id of the A element to "8" then it would concatenate to "#circle-8" effectively selecting your circle object.
Also if you're using jQuery I would stick with animate() unless transition() is a plugin you're using.
I am just learning and trying to code it at the same time, this is the code I have, If I take out the drill method and take out the .click(drill) then everything works so far, it draws some silly bar charts from the data I am sending to it
$( document ).ready(function() {
var dataset = gon.data;
d3.select("body").selectAll("div")
.data(dataset)
.enter()
.append("div")
.attr("class", "bar")
.click(drill)
.style("height", function(d) {
return d.brand_name + "px";
});
function drill (event) {
var target = event.currentTarget;
var data = $(target).data();
console.log(data);
}
});
But I am not able to add "click" event to those bar charts such that when I click on them I can know which chart I clicked on. The code above is result of my unsuccessful attempt to add "click" event to the charts I have drawn .... What is the correct way?
You can use .on("click", drill) if you want D3 to pass the object and data attached.
function drill(d, i) {
console.log(d); //data object
console.log(i); //array position
console.log(this); //DOM element
}
You can also use .attr("onclick", "drill()") syntax if you want to follow the standard HTML without the D3 wrapper.
I'm trying like that (also at https://gist.github.com/1703994):
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?1.27.2"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.time.js?1.27.2"></script>
<script type="text/javascript" src="js-libs/jquery-1.7.js"></script>
<style>
<!--
#test {
width: 400px;
height: 500px;
}
-->
</style>
</head>
<body>
<script type="text/javascript">
$(function() {
var w = 600,
h = 350;
var vis = d3.select("#test").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + w / 2 + "," + h / 2 + ")");
var g = vis.selectAll("g")
.data([ { x:1 , y: 2} ])
.enter().append("svg:g");
g.append("svg:path")
.attr("fill", "red")
.attr("stroke", "red")
.attr("stroke-width", "10")
.attr("d", "M 100 350 l 150 -300")
g.select("path")
.on("click", function() { console.log("Hello"); });
// XXX: how to execute click programmaticaly?
})
</script>
<div id="test"></div>
</body>
</html>
But doesn't work
I think we may use https://github.com/mbostock/d3/wiki/Internals#wiki-dispatch_on
But how to do it?
not sure why, but there appears to be a discrepancy with the way jQuery and d3 handle events that causes a jQuery induced click event $("#some-d3-element").click() to not dispatch to the d3 element.
a workaround:
jQuery.fn.d3Click = function () {
this.each(function (i, e) {
var evt = new MouseEvent("click");
e.dispatchEvent(evt);
});
};
and then call it:
$("#some-d3-element").d3Click();
Simply call the .on method as a getter for the registered value (i.e. your handler function), then call the result of that:
g.select("path").on("click")();
It gets a little more complicated if your handler uses the bound data and/or event fields, or if you've got multiple event listeners bound (e.g "click.thing1" and "click.thing2"). In that case, you're probably best off just firing a fake event using the standard DOM methods:
var e = document.createEvent('UIEvents');
e.initUIEvent('click', true, true, /* ... */);
g.select("path").node().dispatchEvent(e);
With D3 v4 you will likely want this:
d3.select('#some-id').dispatch('click');
Ref.: https://github.com/d3/d3-selection#selection_dispatch
This works. I'm using pie charts, so I'm selecting all the "selected" pie slices, and for each of them, retrieving the attached "click" callback (that I have attached in another portion of code not included here, using d3's .on() method) and then invoking with the expected parameters in the correct context.
d3.selectAll("g.selected").each(function(d, i) {
var onClickFunc = d3.select(this).on("click");
onClickFunc.apply(this, [d, i]);
});
This answer might be somewhat unrelated - but hopefully useful to someone searching for how to invoke a click event of a SVG element - since jQuery $(mySvgElement).trigger("click") won't work.
This is how you would programmatically trigger/invoke/raise a click event for a SVG element:
var ev = document.createEvent("SVGEvents");
ev.initEvent("click",true,true);
var target = $("svg>g>path[fill='#0011cc']").get(0); // get the SVG element here
target.dispatchEvent(ev); // like $(target).trigger('click') - but working!
I came this thread looking for a d3 mousemove event for angular unit testing.
#natevw answer
g.select("path").on("click")();
helped a lot on mouseover event. But, applying that to mousemove was giving an e.source null error.
The work around was to set the d3 event programmatically.
d3.event = document.createEvent('MouseEvent');
d3.event.initMouseEvent("mousemove");
d3.select(elm[0]).select("rect").on("mousemove")();
Hope this helps.
You can go super manual by getting the mouse event and passing it the arguments that d3 would otherwise provide for you. This gives you a fairly clean way to do it while still using d3 constructs. For a single element use the following:
var path = g.select('path');
path.on('click').call(path.node(), path.datum());
For multiple elements, you can trigger each one in turn:
g.selectAll('path').each(function(d, i) {
d3.select(this).on('click').apply(this, arguments);
});
The latter can also be used for a single element if your selector is specific enough, or if you use .select() instead of .selectAll() to only return the first element.
#handler answer did not work for me entirely. It would click the svg element but additional simulated events would not register. This is what worked for me:
function eventFire(el, etype){
if (el.fireEvent) {
el.fireEvent('on' + etype);
} else {
var evObj = document.createEvent('Events');
evObj.initEvent(etype, true, false);
el.dispatchEvent(evObj);
}
}
Usage:
eventFire(document.getElementById(element_id), 'click');
This is how I do it.
g.selectAll("path").on("click", function(d, i){
my_function(d, i);
});
I've found the the callbacks work with anonymous functions. So for the code above, any path that is clicked will call my_function and pass in the current datum d and index i of the path that was clicked.
I find next workaround:
d3.selectAll("path").each(function(d, i) {
onClickFunc.apply(this, [d, i]);
});
Where d is data and i is index this data
Try g.select("path").trigger("click")