Tooltip does not appear for the overlapped circles in d3.js - d3.js

I am creating a scatter Plot in which the points sometimes overlap each other. On mouse-over on any of those points, either the tooltip flickers or sometimes it does not appear. Can any one help me out with this?
dots.on("mouseenter", function(d) {
d3.select(this).attr({
r: radius * 2
});
d3.selectAll(".crosshair")
.style("display", "block");
var xCoord = d3.mouse(this)[0];
var yCoord = d3.mouse(this)[1];
addCrossHair(xCoord, yCoord);
tooltipDiv
.style("top", (d3.event.pageY + 2) + "px")
.style("left", (d3.event.pageX - 28) + "px")
.style("opacity", 0.9)
.style("display", "block")
.html(content);
});
dots.on("mouseout", function(d) {
d3.select(this).attr({
r: radius
});
d3.selectAll(".crosshair")
.style("display", "none");
tooltipDiv.transition()
.duration(100)
.style("display", "none");
});
//tooltip //
var tooltipDiv = d3.select("#scatterChart")
.append("div")
.attr("class", "d3-tip n")
.style("opacity", 0)
.style("position","fixed")
.style("display", "block")
.style("top", 100)
.style("left", 100)
.style("pointer-events","none");
//crossHair//
function addCrossHair(xCoord, yCoord) {
if(!xCoord || !yCoord){ // don't draw cross hair if no valid coordinates given
return;
}
d3.select("#h_crosshair")
.attr("x1", 0)
.attr("y1", yCoord)
.attr("x2", width)
.attr("y2", yCoord)
.style("display", "block");
d3.select("#v_crosshair")
.attr("x1", xCoord)
.attr("y1", 0)
.attr("x2", xCoord)
.attr("y2", height)
.style("display", "block");
}

I got the solution for the above question asked by me.
On mouse-out the duration given to the tooltip was causing the flickering issue.
dots.on("mouseout", function(d) {
d3.select(this).attr({
r: radius
});
d3.selectAll(".crosshair")
.style("display", "none");
tooltipDiv.transition()
.style("display", "none");
});
On removing the duration(100), the above raised issue was solved.

Related

D3.js: Mouseover is not triggered on zoomed topojson features

I have a US map in topojson format. Users select the state and counties from
dropdownlists. When a user selects the county, the map zooms in to the selected
county.
To do: I need to display the county name when users mouseover the county. I'm
able to do this when the map is first loaded but not when the features are zoomed in.
I'm using a tooltip to display text on mouseover. Following is my code: (A sample version can be viewed at: https://realtimeceap.brc.tamus.edu).
<script>
//globals: create tooptip div
var div = d3.select("body").append("xhtml:div")
.attr("id", "divTip")
.style("position", "absolute")
.attr("class", "tooltip")
.style("opacity", 0);
function zoomToCounty(stname, cnty) {
d3.json("/topo/us-wgs84.json", function (us) {
var conus = topojson.feature(us, us.objects.counties);
//initialize selected county text
d3.select("text").remove();
//reset active to inactive size and color
d3.select("#svgMap2").select("#counties").select(".active")
.style("stroke-width", "0.5px")
.style("stroke", "#808080");
//initialize rect
svg.select("rect").remove();
//zoom to selected county
d3.selectAll(".county")
.classed("active", function (d) {
if (d.properties.StateName === stname && d.properties.County === cnty) {
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.call(d3.zoom()
.scaleExtent([.05, 8])
.on("zoom", moveWheel))
//zoom in to max scale extent
var zoom_in = d3.zoom()
.scaleExtent([.05, 8])
.on("zoom", zoomedIn);
//get path of selected county
var bounds = path.bounds(d),
dx = bounds[1][0] - bounds[0][0],
dy = bounds[1][1] - bounds[0][1],
x = (bounds[0][0] + bounds[1][0]) / 2,
y = (bounds[0][1] + bounds[1][1]) / 2,
scale = Math.max(1, Math.min(8, 0.9 / Math.max(dx / width, dy / height))),
translate = [width / 2 - scale * x, height / 2 - scale * y];
//add text at coordinates inside a group
d3.select("#svgMap2").select("#counties")
.append("text")
.style("fill", "black")
.style("font-size", "20px")
.attr("x", width / 2) //center the text
.attr("y", height / 2)
.attr("text-anchor", "middle") //set anchor y justification
.text($("#countySelect").val());
d3.select("#svgMap2").select("#counties")
.transition()
.duration(1000)
.call(zoom_in.transform, d3.zoomIdentity.translate(translate[0], translate[1]).scale(scale)); //center
return true;
}
});
//hover over counties
d3.select("#counties").selectAll(".county")
.enter().append("path")
.attr("d", path)
.attr("name", function (d) { return d.properties.County; }, true)
.attr("id", function (d) { return d.properties.FIPS; }, true)
.style("stroke-width", "0.5px") //inactive border width
.on("mouseover", function () {
d3.select(this)
.style("stroke", "#ff5800");
div.transition()
.duration(300)
div.text(function () {
if (d.properties.StateName === stname && d.properties.County === cnty) {
return $("#countySelect").val();
} else {
return d.properties.County;
}
})
.style("opacity", 1)
.style("class", "label")
.style("left", (d3.event.pageX) - 10 + "px")
.style("top", (d3.event.pageY) - 5 + "px");
})
.on("mouseout", function () {
d3.select(this)
.style("stroke", "#808080");
div.transition()
.duration(300)
.style("opacity", 0);
});
}); //end d3.json
}
Found a solution. I needed to "attach" the mouseover code on the "rect". I replaced the codes on "hover over counties" portion as follows:
//initialize rect
svg.select("rect").remove();
//hover over counties
d3.selectAll(".county", function (d) {
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.call(d3.zoom()
.scaleExtent([.05, 8])
.on("zoom", moveWheel))
.on("mouseover", function (d, i) {
d3.select(this)
.style("stroke", "#ff5800")
div.transition().duration(300)
div.text(function (d) { return conus.features[i].properties.County })
.style("opacity", 1)
.style("class", "label")
.style("left", (d3.event.pageX) - 10 + "px")
.style("top", (d3.event.pageY) - 5 + "px");
})
.on("mouseout", function (d) {
div.transition().duration(300)
.style("opacity", 0);
})
});

Can't add title to stacked bar graph on D3

I creating a stacked bar chart using this example. The chart works and renders but I can't add a mouseover label.
I tried this...
DATE.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
.append("svg:title")
.text(functino(d){return "foo"});
But this after adding the .append("svg:title... the graph stops rendering. If I remove the .style("fill... line, the graph renders, however it's not stacked and there's no mouseover feature.
I have also tried using the tooltip route. (Source)
.on("mouseover", function() { tooltip.style("display", null); })
.on("mouseout", function() { tooltip.style("display", "none"); })
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
But still not luck. Is there a library I need to load? Not sure what's going on.
The graph stop rendering when you try to append the title because you have a typo: it's function, not functino.
Besides that, this is what you need to get the value of each stacked bar:
.append("title")
.text(function(d){
return d[1]-d[0]
});
Here is the demo: https://bl.ocks.org/anonymous/raw/886d1749c4e01e191b94df23d97dcaf7/
But I don't like <title>s. They are not very versatile. Thus, instead of creating another <text>, as the second code you linked does, I prefer creating a div:
var tooltip = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
Which we position and set the HTML text this way:
.on("mousemove", function(d) {
tooltip.html("Value: " + (d[1] - d[0]))
.style('top', d3.event.pageY - 10 + 'px')
.style('left', d3.event.pageX + 10 + 'px')
.style("opacity", 0.9);
}).on("mouseout", function() {
tooltip.style("opacity", 0)
});
And here is the demo: https://bl.ocks.org/anonymous/raw/f6294c4d8513dbbd8152770e0750efd9/

How to add D3 tooltips

Currently I am looking in to D3 sample trying to alter it to add tooltips https://plnkr.co/edit/stHnntCknDUs0Xw6MlQ2?p=preview but cant manage it myself.
I want to add tooltip and pointers to this the way like it is done here http://c3js.org/samples/timeseries.html
//Add tooltips
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Add the scatterplot
countryEnter.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.stat); })
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html(formatTime(d.date) + "<br/>" + d.date)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
Just cant get it working
You are putting the hover on the wrong element. You are putting them on the dots (which don't exist as in your data you as you have no date attribute).
What I have done is place them on the path like so :
countryEnter.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); })
.on("mouseover", function(d) {
console.log(d)
divTooltip.transition()
.duration(200)
.style("opacity", .9)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px")
.innerHTML(formatTime(d.date) + "<br/>" + d.close)
})
.on("mouseout", function(d) {
divTooltip.transition()
.duration(500)
.style("opacity", 0);
});
But I still get the error formatTime is not defined. But the problem is solved on getting the tooltip to be viewed. So solving the rest shouldn't be difficult :)
Updated plunkr : https://plnkr.co/edit/zQY0Wen1plIMuwOMq3PE?p=preview

Line chart - how to display data points whilst line path is being drawn

I'm having a go at my first line chart in d3.js and would like to draw the path between the data points gradually, but also display each data point as a solid circle.
I have already got this working, but what I would really like to try and do is show each circle appear as the line hits each data point.
Right now the data points appear first and then the line path is drawn between them as per this snippet of my code:
var path = g.append("svg:path")
.attr("d", line(data));
var totalLength = path.node().getTotalLength();
console.log(totalLength);
path
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
.attr("stroke-dashoffset", 0);
Working example - http://codepen.io/anon/pen/JYqPvZ
Is what I'm trying to achieve possible?
Thanks
You could write a custom tween function:
path
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
// custom tween
.tween("line", function() {
// set up an interp function
var interp = d3.interpolateNumber(totalLength, 0);
var self = d3.select(this);
// this is called on each animation frame
return function(t) {
// calculate offset and apply it
var offset = interp(t);
self.attr("stroke-dashoffset", offset);
// calculate current x position of line
var xPos = this.getPointAtLength(totalLength - offset).x;
// for each point see if we can "show" the circle
g.selectAll(".point").each(function(){
var point = d3.select(this);
if (xPos > (+point.attr('cx'))){
point.style('opacity',1);
}
})
};
});
Working code:
var data = [3, 6, 2, 7, 5, 2, 1, 3, 8, 9, 2, 5, 7],
w = 600,
h = 400,
margin = 20,
y = d3.scale.linear().domain([0, d3.max(data)]).range([0 + margin, h - margin]),
x = d3.scale.linear().domain([0, data.length]).range([0 + margin, w - margin]);
var vis = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h)
var g = vis.append("svg:g")
.attr("transform", "translate(0, 400)");
var line = d3.svg.line()
//.interpolate("cardinal")
.x(function(d,i) { return x(i); })
.y(function(d) { return -1 * y(d); });
var path = g.append("svg:path")
.attr("d", line(data));
var totalLength = path.node().getTotalLength();
console.log(totalLength);
path
.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength)
.transition()
.duration(2000)
.ease("linear")
.tween("line", function() {
var interp = d3.interpolateNumber(totalLength, 0);
var self = d3.select(this);
return function(t) {
var offset = interp(t);
self.attr("stroke-dashoffset", offset);
var xPos = this.getPointAtLength(totalLength - offset).x;
g.selectAll(".point").each(function(){
var point = d3.select(this);
if (xPos > (+point.attr('cx'))){
point.style('opacity',1);
}
})
};
});
g.append("svg:line")
.attr("x1", x(0))
.attr("y1", -1 * y(0))
.attr("x2", x(w))
.attr("y2", -1 * y(0))
g.append("svg:line")
.attr("x1", x(0))
.attr("y1", -1 * y(0))
.attr("x2", x(0))
.attr("y2", -1 * y(d3.max(data)));
g.selectAll(".xLabel")
.data(x.ticks(5))
.enter().append("svg:text")
.attr("class", "xLabel")
.text(String)
.attr("x", function(d) { return x(d) })
.attr("y", 0)
.attr("text-anchor", "middle")
g.selectAll(".yLabel")
.data(y.ticks(4))
.enter().append("svg:text")
.attr("class", "yLabel")
.text(String)
.attr("x", 0)
.attr("y", function(d) { return -1 * y(d) })
.attr("text-anchor", "right")
.attr("dy", 4)
g.selectAll(".xTicks")
.data(x.ticks(5))
.enter().append("svg:line")
.attr("class", "xTicks")
.attr("x1", function(d) { return x(d); })
.attr("y1", -1 * y(0))
.attr("x2", function(d) { return x(d); })
.attr("y2", -1 * y(-0.3))
g.selectAll(".yTicks")
.data(y.ticks(4))
.enter().append("svg:line")
.attr("class", "yTicks")
.attr("y1", function(d) { return -1 * y(d); })
.attr("x1", x(-0.3))
.attr("y2", function(d) { return -1 * y(d); })
.attr("x2", x(0))
var points = g.selectAll(".point")
.data(data)
.enter().append("svg:circle")
.attr("class","point")
.attr("stroke", "steelblue")
.attr("fill", function(d, i) { return "steelblue" })
.attr("cx", function(d, i) { return x(i); })
.attr("cy", function(d, i) { return -1 * y(d); })
.attr("r", function(d, i) { return 5 })
.style("opacity", 0)
.on('click', function(p){
console.log('clicked! - ' + p );
})
.on("mouseover", function(d) {
d3.select(this).attr("r", 8).style("fill", "red").attr("stroke", "red");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5).style("fill", "steelblue").attr("stroke", "steelblue");
});
var val = points.append('g');
val.selectAll('text')
.data(function(d,i){ return d.values})
.enter().append('text')
.attr("x", function(d, i) {
return x(i) + x.rangeBand() / 2;})
.attr("y", function(d, i) { return y(d.value) })
.attr('dy', -10)
.attr("text-anchor", "middle")
.text(function(d) { return d.value; })
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
line {
stroke: black;
}
text {
font-family: Arial;
font-size: 9pt;
}
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>

Using d3js to make a candlestick or ohlc chart

Is it possible to make an OHLC or Candlestick chart with d3js or plugins built from one of it or its forks? d3.js is a very powerful charting library built in javascript and it would be nice to customize charts built using it down further using its amazing abilities.
I found this one, and looks very nice.
http://phrogz.net/js/d3-playground/#StockPrice_HTML
Have a look at this example. It does exactly what you want.
Update: The link above is currently broken, but #lakenen was so kind as to provide a fixed version here.
I know this question is an old question but I spent lots of time looking for a working example in 2017 and found very little.
Below is a working format for a candlestick chart using d3.js v4. The data in the code below is an array which in my case was being brought in from c# in the back.
var twoHundredDayCandleStickChart = [];
var width = 900;
var height = 500;
var margin = 50;
function min(a, b) { return a < b ? a : b; }
function max(a, b) { return a > b ? a : b; }
var y = d3.scaleLinear().range([height - margin, margin]);
var x = d3.scaleTime().range([margin, width - margin]);
//line for the sma
var line1 = d3.line()
.x(function (d) { return x(d["date"]); })
.y(function (d) { return y(d["sma"]); });
function buildChart(data) {
data.forEach(function (d) {
d.date = new Date(d.date);
d.high = +d.high;
d.low = +d.low;
d.open = +d.open;
d.close = +d.close;
d.sma = +d.sma;
});
var chart = d3.select("#divId")
.append("svg")
.attr("class", "chart")
.attr("width", width)
.attr("height", height);
y.domain([d3.min(data.map(function (x) { return x["low"]; })), d3.max(data.map(function (x) { return x["high"]; }))])
x.domain(d3.extent(data, function (d) { return d["date"]; }))
chart.selectAll("line.x")
.data(x.ticks(10))
.enter().append("line")
.attr("class", "x")
//.text(String)
.attr("x1", x)
.attr("x2", x)
.attr("y1", margin)
.attr("y2", height - margin)
.attr("stroke", "#ccc");
chart.selectAll("line.y")
.data(y.ticks(10))
.enter().append("line")
.attr("class", "y")
.attr("x1", margin)
.attr("x2", width - margin)
.attr("y1", y)
.attr("y2", y)
.attr("stroke", "#ccc");
chart.append("g")
.attr("transform", "translate(0," + 450 + ")") //need to change this 450 to a variable- it is how far down the axis will go
.attr("class", "xrule")
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function (d) {
return "rotate(-65)"
});
chart.selectAll("text.yrule")
.data(y.ticks(10))
.enter()
.append("text")
.attr("class", "yrule")
.attr("x", 0)
.attr("y", y)
.attr("dy", 0)
.attr("dx", 20)
.attr("text-anchor", "middle")
.text(String);
chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function (d) { return x(d["date"]); })
.attr("y", function (d) { return y(max(d["open"], d["close"])); })
.attr("height", function (d) { return y(min(d["open"], d["close"])) - y(max(d["open"], d["close"])); })
.attr("width", function (d) { return 0.5 * (width - 2 * margin) / data.length; })
.attr("fill", function (d) { return d["open"] > d["close"] ? "red" : "green"; });
chart.selectAll("line.stem")
.data(data)
.enter().append("line")
.attr("class", "stem")
.attr("x1", function (d) { return x(d["date"]) + 0.25 * (width - 2 * margin) / data.length; })
.attr("x2", function (d) { return x(d["date"]) + 0.25 * (width - 2 * margin) / data.length; })
.attr("y1", function (d) { return y(d["high"]); })
.attr("y2", function (d) { return y(d["low"]); })
.attr("stroke", function (d) { return d.open > d.close ? "red" : "green"; });
chart.append("path")
.data([data])
.attr("d", line1)
.attr("class", "line")
.style("stroke", "white")
.attr("fill", "none")
.attr("stroke-width", 2);
}
techan.js does what you need. As shown on their home page:
A visual, stock charting (Candlestick, OHLC, indicators) and technical analysis library built on D3. Build interactive financial charts for modern and mobile browsers.
There is another working example by Andre Dumas shown here, with example code. That was last updated 2017-07-07.

Resources