d3.js mouseover mouseout issue - javascript

again I am struggling with d3.js. I have a working Line Chart and partially working mouseover. The goal is to limit the mouseover solely to the svg element, like Mark has it working in his answer Multiseries line chart with mouseover tooltip
I have created a Plunker with it. My is-situation is like that.
http://plnkr.co/edit/Jt5jZhnPQy4VpjwY3YBv?p=preview
And I have tried things like:
http://plnkr.co/edit/lRMfa0OiDWEXWYBAjoPd?p=preview
by adding:
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
But it's always pushing the circles and the bar out of the chart, I am fiddling for some days now and would be extremely glad if someone happens to point me in the right direction.
Thank you in advance.

Here is the plunker:
http://plnkr.co/edit/MEtbBqN5qr82yr0CNhUN?p=preview
I simply changed the size of your rectangle:
mouseG.append('rect')
.attr("x", margin.left)
.attr("y", margin.top)
.attr('width', w - margin.left - margin.right)
.attr('height', height - margin.bottom - margin.top)
PS: I don't know if you want the line limited to the chart area as well, but if you want, this is the plunker: http://plnkr.co/edit/RP4uYKBYnHtX1SvYsLKq?p=preview

Instead of giving width as width :
mouseG.append('svg:rect')
.attr('width', width)
do this (give the width of the group same as domain x for the line chart)
mouseG.append('svg:rect')
.attr('width', w - padding * 2)
Reason:
var xScale = d3.time.scale()
.domain([xExtents[0], xExtents[1]])
.range([padding, w - padding * 2]);
Your width of the x scale is w - padding * 2 so the width of the group listening to the mouse event should be same.
working code here

Related

d3.js Box Plot Positioning Issue with box.js

I'm trying to implement box plots as part of a data visualization interface that uses d3 and AngularJS. I'm working with this box plot package: https://bl.ocks.org/mbostock/4061502.
However, I can't figure out which part of the sample code controls the positioning of the box plots. In the example, the five box plots are arranged sequentially. When I try to generate my plots, they all appear on top of each other.
Here is the code that I'm using to generate the box plots:
boxplots = svg.selectAll("svg")
.data(boxPlotData)
.enter().append("svg")
.attr("class", "box")
.attr("width", boxWidth + margin.left + margin.right)
.attr("height", boxHeight + margin.bottom + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
Here's the code for how my svg canvas is created. This is done in an angular directive:
template:"<svg width='825' height='600'></svg>",
link: function($scope, elem){
var d3 = $window.d3;
var rawSvg=elem.find('svg'); // this is the svg created in the template
var width = rawSvg[0].attributes[0].value;
var height = rawSvg[0].attributes[1].value;
var svg = d3.select(rawSvg[0]);
Edit: not perfect yet but getting there:
What you need is an ordinal scale to position the svg-elements for the boxes within the parent svg. Assuming width represents the width of your parent svg element and data is an array of your data elements, you can use this to create the scale:
const x = d3.scaleBand()
.range( [0, width] )
.domain( data.map( (el,i) => i ) );
Within the svg creation you can now use
boxplots = svg.selectAll("svg")
.data(boxPlotData)
.enter().append("svg")
.attr( "x", (d,i) => x(i) ) // this is added
.attr("class", "box")
.attr("width", boxWidth + margin.left + margin.right)
.attr("height", boxHeight + margin.bottom + margin.top)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(chart);
PS: This assumes you use v4 of d3js. The syntax in v3 for the scale is different.
PPS: I currently can not test the code, but it should work like described.

Resize function for responsive d3

I am trying to make my d3 multi-series line chart responsive follwoing this reference and here is the full code of this reference.
I add the following resize function to the bottom of my code:
$(window).on("resize", function() {
//update width
var main_width = parseInt(d3.select('#myChart').style('width'), 10);
main_width = main_width - main_margin.left - main_margin.right;
//resize the chart
main_x.range([0, main_width]);
mini_x.range([0, main_width]);
d3.select('#myChart').append("svg")
.attr("width", main_width + main_margin.left + main_margin.right)
.attr("height", main_height + main_margin.top + main_margin.bottom);
svg.selectAll('defs.clipPath.rect')
.attr("width", main_width);
svg.selectAll('rect.overlay')
.attr("width", main_width);
}).trigger("resize");
But nothing is changed when I adjust the screen. No idea why, please advise!
Thanks a lot.
Just detect all svg elements and attributes that have something to do with width main_width, because responsive mean the width will be adjusted when screen resize.
here is the resize() function that works well:
//----------- responsive d3, resize functionality -----------
$(window).on("resize", function() {
//update width
main_width = parseInt(d3.select('#myChart').style('width'), 10);
main_width = main_width - main_margin.left - main_margin.right;
//resize main and min time axis range
main_x.range([0, main_width]);
mini_x.range([0, main_width]);
//pointpoint the 'rect' element about brush functionality and adjust its width
svg.selectAll('rect.brushrect').attr("width", main_width);
//pinpoint the 'rect' element about mousemove and adjust its width
svg.selectAll('rect.overlay').attr("width", main_width);
//update x axis
svg.selectAll("g.x.axis").transition().call(main_xAxis);
//update right y axis
svg.selectAll('g.y.axisRight').attr("transform", "translate(" + main_width + ", 0)").call(main_yAxisRight);
// update main line0 and line4
svg.selectAll("path.line.line0").datum(data).transition().attr("d", main_line0);
svg.selectAll("path.line.line4").datum(data).transition().attr("d", main_line4);
//update min line0 and line4
mini.selectAll("path.mini_line0").datum(data).transition().attr("d", mini_line0);
mini.selectAll("path.mini_line4").datum(data).transition().attr("d", mini_line4);
}).trigger("resize");
Two things to note
The rect attribute with respect to brushing, i.e,
svg.append("defs")
.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", main_width)
.attr("height", main_height);
does not have a name, so the following class attribute should be added for pinpointing:
.attr('class', "brushrect")
The class for mini lines are changed to mini_line0 and mini_line4, some the codes have some tiny update related to that.
then all done.

D3 Panning Chart with Dates

I'm trying to get a multi-line date based chart to pan nicely across the X date axis and I simply cannot figure out what the problem is.
I have the zoom behaviour set up in the code but it's just not performing as expected. If you click on a point in a line and scroll it appears to be scrolling the axis, if it click on the labels in the axis it also scrolls but the actual visualisation of data doesn't scroll.
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, 1])
.on("zoom", function () {
svg.select(".x.axis").call(xAxis);
svg.select(".lines").call(xAxis);
});
svg.call(zoom);
Also if you click directly on the back ground the mouse event doesn't seem to make it's way to the control at all.
I read a few examples on this and each seem to take a vastly different approach which I've tried but none have worked for my chart.
There are possibly a number of issues that exist as barriers to getting this working so I thought the best way to illustrate the problem was in a JsFiddle.
D3 Chart Panning Fiddle
What I'm trying to achieve is when there is a lot of data to visualise the chart can adapt to the data set and allow the data to extend beyond the bounds of the chart.
Currently clicking on the background does not allow panning because you have applied zoom behavior to the g element not to the svg.
var svg = d3.select('#host')
.data(plotData)
.append("svg")
.attr("id", "history-chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(zoom);
Right now on zoom you have updated x and y axes but not the visualization. So you have update the lines and circles also like this.
var zoom = d3.behavior.zoom()
.x(x)
.scaleExtent([1, 1])
.on("zoom", function () {
svg.select(".x.axis").call(xAxis);
svg.select(".lines").call(xAxis);
svg.selectAll("path.lines")
.attr("d", function(d) { return line(d.values); });
svg.selectAll("circle")
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.value); });
});
Since you are panning the map you will have to use clip path for restricting visualization from moving outside the chart
var clip = svg.append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
Apply clip path to the g elment which contains lines and cicrles.
var attribute = svg.selectAll(".attribute")
.data(plotData)
.enter().append("svg:g")
.attr("clip-path", "url(#clip)")
.attr("class", "attribute");

How to fit variable length tick labels on a D3 line chart?

Here's a JSFiddle: http://jsfiddle.net/8p2yc/ (A slightly modified example from here: http://bl.ocks.org/mbostock/3883245)
As you can see in the JSFiddle tick labels along the y axis do not fit in the svg. I know I can increase the left margin, but the thing is I don't know what the data will be in advance. If I just make the margin very large the chart will look awkward if the numbers are short in length.
Is there a way to precompute the maximum label width when creating the chart to set the margin correctly? Or perhaps there's an entirely different solution?
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 400 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Thanks!
You can do this by appending the text for the largest label, measuring it and removing it immediately afterwards:
var maxLabel = d3.max(data, function(d) { return d.close; }),
maxWidth;
svg.append("text").text(maxLabel)
.each(function() { maxWidth = this.getBBox().width; })
.remove();
Then you can use that width to do the translation of the g element:
svg.attr("transform", "translate(" + Math.max(margin.left, maxWidth) + "," + margin.top + ")");
Complete example here.
Edit: Getting the maximum length of the actual labels is a bit more involved because you have to generate them (with the right formatting) and measure them all. This is a better way to do it though as you're measuring what's actually displayed. The code is similar:
var maxWidth = 0;
svg.selectAll("text.foo").data(y.ticks())
.enter().append("text").text(function(d) { return y.tickFormat()(d); })
.each(function(d) {
maxWidth = Math.max(this.getBBox().width + yAxis.tickSize() + yAxis.tickPadding(), maxWidth);
})
.remove();
I'm adding the size of the tick line and the padding between tick line and label to the width here. Complete example of that here.
A chicken-and-egg problem prevents this from being precomputed reliably, assuming you want your axes and plot to combine to fill a fixed space, and your tick labels to be generated automatically.
The problem is that the tick labels, and therefore the space they require, depend on the scale. A time scale, for example, may include longer month names (e.g. 'September') over a short domain, but shorter month names or just years over a long domain. But the scale, at least on the opposite axis, depends on the space left for the range, which depends on the space left over after determining the tick labels.
In a lot of cases, the minimum and maximum values may give you an idea of the widest tick labels, and you can use Lars' method. For example, if the domain is –50 to 10, any tick labels between those will be narrower. But this assumes they're integers! Also, be careful with 'nice' scales; if your maximum value is 8, D3 may try to make the greatest tick label 10, which is a wider string.
d3 v4 Solution for Lars' Method.
calculateMarginForYScaleTicks() {
let maxWidth = 0;
let textFormatter = d3.format(",");
let tempYScale = d3
.scaleLinear()
.range([0, 0])
.domain([0, d3.max(data, d => d.value)]);
d3
.select("#svg")
.selectAll("text.foo")
.data(tempYScale.ticks())
.enter()
.append("text")
.text(d => textFormatter(d))
.each(function() {
maxWidth = Math.max(this.getBBox().width, maxWidth);
})
.remove();
return maxWidth;
}

Move position of tree using javascript d3

I'm creating a diagram with D3 and JSON which is based on this:
http://bl.ocks.org/mbostock/4063550
I'm completely new to this...and I just can't seem to figure out how to move the position of the tree to the right or left of the page. Seems simple enough?
Reason I'm asking is because some of my text are cut out on the left side of screen.
Any help appreciated!
The easiest way to do this is to adjust the transform parameter of the top-level g element. In the example, it is created like this.
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter - 150)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
To move everything to the right, you would need to add something to the x translation, e.g.
var svg = d3.select("body").append("svg")
.attr("width", diameter)
.attr("height", diameter - 150)
.append("g")
.attr("transform", "translate(" + (diameter / 2 + 10) + "," + diameter / 2 + ")");

Categories

Resources