Tooltip behavior on line chart with Date as the x-axis - javascript

I'm trying to create a line chart with a tooltip where the x-axis is a date.
I would like the line and tooltip to change after it is halfway (or similar) to the next tick on the x-axis.
Mainly I would like the behavior to be the same as this bl.ock: http://bl.ocks.org/wdickerson/64535aff478e8a9fd9d9facccfef8929
You can view the behavior I currently have on my bl.ock: https://bl.ocks.org/JulienAssouline/574a52ee2034bcdc1e56ed926f36dd52
It mostly works but the data only changes after it passes to the September month and it never reaches the October month.
I have tried to adapt my code to the bl.ock. The problem is the bl.ock displayed is using years and I am using a date format which seems to be my main problem.
Here is the main part of the code:
var tipBox = svg.append("rect")
.attr("width", width)
.attr("height", height)
.attr("opacity", 0)
.on("mousemove", drawTooltip)
.on("mouseout", removeTooltip)
function removeTooltip() {
if (tooltip) tooltip.style('display', 'none');
if (tooltipLine) tooltipLine.attr('stroke', 'none');
}
function drawTooltip(){
const line_hover = xScale.invert(d3.mouse(this)[0]);
// console.log(d3.mouse(this)[0])
console.log(xScale.invert(d3.mouse(this)[0]).getMonth())
console.log(Math.floor(xScale.invert(d3.mouse(this)[0])))
const date_hover = xScale.invert(d3.mouse(this)[0]).getMonth()
// yScale.invert(pos.y)
tooltipLine.attr("stroke", "grey")
.attr("x1", xScale(line_hover))
.attr("x2", xScale(line_hover))
.attr("y1", 0)
.attr("y2", height)
.attr("class", "line_hover")
.style('stroke-width', 1)
tooltip.html(date_hover)
.style("position", "absolute")
.style("background-color", "lightgrey")
.style('display', 'block')
.style('left', d3.event.pageX - 100+ "px")
.style('top', d3.event.pageY - 20+"px")
.selectAll()
.data(dataNest).enter()
.append('div')
.style('color', "black")
.html(function(e){ return e.key + ': ' + e.values.find(function(h){ return (h.Date.getMonth() + 0.5) == (date_hover + 0.5) }).randNumCol})
}
You can again view all of the code on my bl.ock: https://bl.ocks.org/JulienAssouline/574a52ee2034bcdc1e56ed926f36dd52

GetMonth will always give the month. Get the date and display based on the date. Not ideal, but works.
Example here

Related

Add horizontal crosshair to d3.js chart

I am successful in getting crosshair in D3.js chart but issue is I am only getting vertical line, how do I add code for horizontal line as well?
Image of chart
JSFiddle code chart is not plotting in JSFiddle
Basically code to add vertical line crosshair is as below:-
var vertical = d3.select("body")
.append("div")
.attr("class", "remove")
.style("position", "absolute")
.style("z-index", "19")
.style("width", "1px")
.style("height", "450px")
.style("top", "47px")
.style("bottom", "1px")
.style("left", "8px")
.style("background", "#000");
Can I add horizontal crosshair as well same way?
P.S. also wanted a way to keep this vertical line only in chart area, but is coming in whole body, i.e. empty area next to chart in right and left.
Your approach is too complicated. This is simpler:
First, create a transparent rectangle to get the mouse movements:
var transpRect = svg.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", width)
.attr("height", height)
.attr("fill", "white")
.attr("opacity", 0);
Then, create the lines:
var verticalLine = svg.append("line")
.attr("opacity", 0)
.attr("y1", 0)
.attr("y2", height)
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("pointer-events", "none");
var horizontalLine = svg.append("line")
.attr("opacity", 0)
.attr("x1", 0)
.attr("x2", width)
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("pointer-events", "none");
Finally, position the lines on mousemove:
transpRect.on("mousemove", function(){
mouse = d3.mouse(this);
mousex = mouse[0];
mousey = mouse[1];
verticalLine.attr("x1", mousex).attr("x2", mousex).attr("opacity", 1);
horizontalLine.attr("y1", mousey).attr("y2", mousey).attr("opacity", 1)
}).on("mouseout", function(){
verticalLine.attr("opacity", 0);
horizontalLine.attr("opacity", 0);
});
Here is your fiddle: https://jsfiddle.net/xrf1ro1a/
PS: to avoid killing your tooltips, I put the mousemove both on the rectangle and on the svg as well, which has the undesirable effect of making the lines going outside the chart area. To avoid this, set pointer-events = none to the elements outside the chart area.

D3.js Dynamically change the color of arrows in Force Directed Graph [duplicate]

I am trying to do the obvious thing of getting the arrowhead colors of my directed graph's links to match the edge colors. Surprisingly I have not found a complete solution for doing this, although this older post seems like an excellent starting point. I would be fine with adapting that solution to work as outlined below, or if there is a superior method for creating arrowheads that achieves this effect I would be most thankful.
First, I have a linear gradient color function to color my edges by property like this:
var gradientColor = d3.scale.linear().domain([0,1]).range(["#08519c","#bdd7e7"]);
Then, like that previous post I have a function for adding markers:
function marker (color) {
var reference;
svg.append("svg:defs").selectAll("marker")
.data([reference])
.enter().append("svg:marker")
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15) // This sets how far back it sits, kinda
.attr("refY", 0)
.attr("markerWidth", 9)
.attr("markerHeight", 9)
.attr("orient", "auto")
.attr("markerUnits", "userSpaceOnUse")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.style("fill", color);
return "url(#" + reference + ")"; };
And then the links definition I have is this one based on the Curved Links example.
var link = svg.selectAll(".link")
.data(bilinks)
.enter().append("path")
.attr("class", "link")
.style("fill", "none")
.style("opacity", "0.5")
.style("stroke-width", "2")
.style("stroke", function(d) { return gradientColor(d[3]); } )
.attr("marker-end", marker( "#FFCC33" ) );
This DOES NOT work as written; the browser gives me an "Uncaught TypeError: Cannot read property '5' of undefined" (where 'd[5]' refers to the fifth property in a list of properties that the links have). The problem is clearly passing the data function to the marker function in this case. If I feed in a static color like "#FFCC33" then the arrowheads DO change color (now). Unfortunately the person who posted this "marker function" solution 1.5 years ago didn't include the bit about passing the color to the marker function at all.
I don't know how to feed in the link's color properly. Ideally I would be able to use a reference to the color of the link that the arrowhead is attached to rather than inputting the same color function (because eventually I'm going to be coloring the links via different schemes based on button presses).
I've created a JS Fiddle that includes all the necessary bits to see and solve the problem. Currently I'm passing a static color to the markers, but it should be whatever is the color of the link it is attached to. I've also included features for another question on properly positioning the arrowheads and edge tails.
I don't believe you're able to define a single SVG marker and change it's colour. Instead you need to define the marker many times (1 for each colour that you need to use). There's a nice example that recently popped up onto the D3 website.
The way this works, is by having lots if different markers, each defining the colour of the marker. Here's a screenshot of all the markers that are defined:
Then this particular example, cycles the CSS classes on the paths. The particular colored marker that each path is using is defined within the CSS class that's being applied to a path at any given time.
I've modified your example to add a new marker per path (and changed the colors slightly in the gradient to prove that it's working). Here's what I've got:
var width = 960,
height = 500;
var color = d3.scale.category20();
var gradientColor = d3.scale.linear().domain([0, 15]).range(["#ff0000", "#0000ff"]);
var force = d3.layout.force()
.linkDistance(10)
.linkStrength(2)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var defs = svg.append("svg:defs");
d3.json("http://bost.ocks.org/mike/miserables/miserables.json", function (error, graph) {
if (error) throw error;
function marker(color) {
defs.append("svg:marker")
.attr("id", color.replace("#", ""))
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15) // This sets how far back it sits, kinda
.attr("refY", 0)
.attr("markerWidth", 9)
.attr("markerHeight", 9)
.attr("orient", "auto")
.attr("markerUnits", "userSpaceOnUse")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.style("fill", color);
return "url(" + color + ")";
};
var nodes = graph.nodes.slice(),
links = [],
bilinks = [];
graph.links.forEach(function (link) {
var s = nodes[link.source],
t = nodes[link.target],
i = {}, // intermediate node
linkValue = link.value // for transfering value from the links to the bilinks
;
nodes.push(i);
links.push({
source: s,
target: i
}, {
source: i,
target: t
});
bilinks.push([s, i, t, linkValue]);
});
force.nodes(nodes)
.links(links)
.start();
var link = svg.selectAll(".link")
.data(bilinks).enter().append("path")
.attr("class", "link")
.style("fill", "none")
.style("opacity", "0.5")
.style("stroke-width", "2")
.each(function(d) {
var color = gradientColor(d[3]);
console.log(d[3]);
d3.select(this).style("stroke", color)
.attr("marker-end", marker(color));
});
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("circle")
.attr("r", function (d) {
return 2 + d.group;
})
.style("opacity", 0.5)
.style("fill", function (d) {
return color(d.group);
});
node.append("title")
.text(function (d) {
return d.name;
});
force.on("tick", function () {
link.attr("d", function (d) {
return "M" + d[0].x + "," + d[0].y + "S" + d[1].x + "," + d[1].y + " " + d[2].x + "," + d[2].y;
});
node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Mouseover Effect D3.js

I have been trying to modify the examples provided by D3.js to create a step plot where I can hover over each step to get details of the value.
Currently I am looking at:
http://bl.ocks.org/mbostock/3902569
and my plot looks like:
http://jsfiddle.net/q47r3pyk/
after hours of playing with the JavaScript. It is close to my final result but if you try to hover over the points, you only get a value on the left handle side of the screen.
How do you get the hover effect to appear over where you place your mouse?
Any advice would be appreciated on what I am doing incorrectly.
My mouse over section looks like:
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(formatted_data, x0, 1),
d0 = formatted_data[i - 1],
d1 = formatted_data[i],
d = x0 - d0.x > d1.x - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.x) + "," + y(d.y) + ")");
focus.select("text").text(d.y);
I think you want to adjust your bisectDate function (as can be seen in the jsfiddle you linked).
If you use:
bisectDate = d3.bisector(function(d) { return d.x; }).left;
(using d.x instead of d.date), it's working for me.
This is due to the fact that you are storing the x coords in x (in formatted_data), whereas Mike Bostock's example uses .date. Thus, d3.bisect can't find the proper value.

d3js: Get hover overlay over multiple graphs

I have a set of graphs that can be dynamically added and removed from the page. Each one has an invisible 'rect' element appended to the base svg hosting each graph, and on that rect element I can append mouseover elements. However, these are all limited to the single svg/rect that the mouse is hovering over; I'd like to extend them to cover all visible graphs. Here's the main code affecting that:
var focus = svg.append('g') // An invisible layer over the top. Problem is, it only overlays on one graph at a time...
.style('display', 'none');
svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all")
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
// append the x line
focus.append("line")
.attr("class", "x")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("y1", 0)
.attr("y2", height);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(dataset, x0, 1),
d0 = dataset[i - 1],
d1 = dataset[i],
d = x0 - d0.time > d1.time - x0 ? d1 : d0;
focus.select(".x")
.attr("transform", function() {
return "translate(" + x(d.time) + "," + rightDomain(symbol, d) + ")";
})
.attr("y2", height - y(d[symbol]));
}
All of this is inside a forEach() loop, where it loops over an array containing the names of the graphs to be shown, so multiple graphs (albeit in their separate svgs) show up.
I also have a plunker here: http://plnkr.co/edit/s4K84f5HGRjHFWMwiuIA?p=preview. I'm not sure why it's failing to work since I've copied and pasted my code, which I know works elsewhere.
Edit: I've managed to attach another svg element to the body but for some reason I can't get it to overlay on top of the existing svgs (the graphs). Here's my code (where I've tried several ways of adjusting the position):
var overlay = d3.select('html')
.append('div')
.attr('height', function() { return (symbols.length - 1) * 135 + 130; })
.attr('width', 1000)
.attr('z-index', 2)
//.attr('transform', 'translate(' + margin.left + ',' + extraBuffer/2 + ')');
//.attr('x', margin.left)
//.attr('y', extraBuffer/2);
.attr('position', 'absolute')
.attr('top', '20')
.attr('right', '40');
Looking at this in chrome devtools I always see it below existing graphs, even if I explicitly set its x/y values.

Floating line disturbs the mousemove function

My quest for d3.js wisdom continues!
This time, I have added a guide line which is hovering in a vertical direction as a tool close to the pointer. The problem is that the line disturbs the mousemove functions since it adds an extra layer on top of the rest of the graph, which makes the the code run the mouseout event on sudden pointer movements. Is there a solution for this?
I have implemented the function in the following manner:
svg.on("mousemove", function(d) {
svg.select(".guideline").remove();
//svg.select(".valuelabel").remove();
svg.append("line")
.attr("class", "guideline")
.attr("x1", d3.mouse(this)[0]-3)
.attr("x2", d3.mouse(this)[0]-3)
.attr("y1", margin[0])
.attr("y2", height+margin[0])
.attr("opacity", originOpacity)
.attr("stroke", "#333");
});
And as an example of an event it is disturbing:
//Highlight each stack when hovering, and calculate y value for legend
stacks.on("mousemove", function(d) {
svg.select(".label").remove();
//Calculate the closest index when hovering
var perValue = width / data[0].data.length;
var index = Math.ceil((d3.mouse(this)[0]-margin[3]) / perValue - 0.5);
chart.selectAll(".valuelabel").each(function(data) {
if (data.name == d.name) {
d3.select(this).text(Math.round(data.data[index].y) + "%");
}
});
d3.select(this).attr("opacity", "1");
svg.selectAll("." + d3.select(this).attr("class")).attr("opacity", "1");
svg.append("text")
.attr("class", "label")
.attr("width", "100px")
.attr("height", "20px")
.attr("x", d3.mouse(this)[0] + 40)
.attr("y", d3.mouse(this)[1] - 5)
.text(d.group + ": " + d.name);
});
stacks.on("mouseout", function(d) {
groups.selectAll("." + d.name).text(d.name);
svg.select(".label").remove();
svg.selectAll("." + d3.select(this).attr("class")).attr("opacity", originOpacity);
});
Looks like you want pointer-events none on the guide line.

Categories

Resources