I have a stackBlitz here - https://stackblitz.com/edit/ng-tootltip?file=src/app/bar-chart.ts
I have a bar chart with tooltips when you hover over the bars.
The tooltips are positioned above the bars but at small screen sizes I would like to position the tooltip in middle of the window.
I can capture when the screen is small and and I also have the width of the tooltip and so the half the width which I think I'll need to position it. I just stuck how to use this in the style attribute.
if (window.innerWidth < 500) {
const toolTipWidth = d3.select('.chart-tooltip').style('width');
const halfToolTipWidth = Number(toolTipWidth)/2;
d3.select('.chart-tooltip')
.style("left", 50 + "%") <- I need to minus the half width here
.style("top", 0 + "px")
.html(html)
}else{
d3.select('.chart-tooltip')
.style("left", d3.event.pageX - 30 + "px")
.style("top", 0 + "px")
.html(html)
}
What you're looking for is the CSS3 calc function.
You can use it to write values like calc(50% - 10px) (operators must be surrounded by whitespaces).
This, plus a fix on the halfToolTipWidth computation that gave NaN and you're done:
Edit: toolTipWidth also fixed.
let tooltip = d3.select('.chart-tooltip');
if (window.innerWidth < 500) {
const toolTipWidth = tooltip.node().getBoundingClientRect().width;
const halfToolTipWidth = 40;
tooltip
.style("position", "absolute")
.style("left", "calc(50% - " + halfToolTipWidth +"px)")
.style("top", 0 + "px")
.html(html)
} else {
tooltip
.style("left", d3.event.pageX - 30 + "px")
.style("top", 0 + "px")
.html(html)
}
Updated Stackblitz.
Related
This is a code for making pie chart.
I have added 2 functionalities to pie chart. First one is to increase the arc size when mouse hovers on it.
Second is that when i hover, tooltip displays y position of mouse and this tooltip moves with mouse.
But problem with me is that. Tooltip Text is not moving with movement of mouse.
I have seen various stack overflow links for this but i am unable to find solution. Below i have also attached links that so that the question is not marked duplicate.
Please do not give me a new code for tooltip. Correct this one.
Below are the links that i have already visited
Why is my D3.js tooltip not working
D3 - Positioning tooltip on SVG element not working
Tooltips not showing on mouse move and mouse over
Link of jsfiddle :- https://jsfiddle.net/uoh1bsrp/
Thanks,
Shubham Sharma
width = 600
height = 600
svg = d3.select('body').append('svg').attr('width',width).attr('height',height).append('g').attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')');
a = [2,5,7]
pie = d3.pie()
radius = 150
arc = d3.arc().innerRadius(0)
.outerRadius(radius);
arcs = svg.selectAll('arc').data(pie(a)).enter().append('g')
arcs.append("path").attr('d',function(d){return arc(d)})
tooltip = svg.append('text')
color = ['red','green','blue']
path = d3.selectAll('path').style('fill',function(d,i){return color[i]})
path.on('mouseover',handlemouseover)
path.on('mouseout',handlemouseout)
path.on('mousemove',handlemousemove)
function handlemouseover(){
d3.select(this).attr('d',function(d){return d3.arc().innerRadius(0)
.outerRadius(180)(d)});
}
function handlemousemove(){
// svg.append('text').text(function(){return 'shubham'}).style('top',(d3.event.layerY + 10)+'px').style('left',(d3.event.layerX + 10) + 'px')
tooltip.text(function(){return d3.event.layerX}).style('top',(d3.event.layerY + 10)+'px').style('left',(d3.event.layerX + 10) + 'px').style('display','block');
}
function handlemouseout(){
d3.select(this).attr('d',function(d){return d3.arc().innerRadius(0).outerRadius(radius)(d)});
tooltip.style('display', 'none');
}
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-path.v1.min.js"></script>
<script src="https://d3js.org/d3-shape.v1.min.js"></script>
</head>
That's because you are trying to increase top property of text tag in SVG. This porperty just doesn't work. You can use translate instead of it or just use tags like div or span.
Check the fiddle https://jsfiddle.net/fr71t0o3/3/:
function handlemousemove() {
// svg.append('text').text(function(){return 'shubham'}).style('top',(d3.event.layerY + 10)+'px').style('left',(d3.event.layerX + 10) + 'px')
tooltip
.text(function() {
return d3.event.layerX;
})
.style('transform', `translate(${d3.event.layerX - 300}px, ${d3.event.layerY - 300}px)`)
.style('display', 'block').style('color','red');
}
I'm not sure about offset of tooltip, if you want it moving with the mouse move you can use mouse coordinates.
Instead of .style('left', /*[...]*/) and .style('top', /*[...]*/) you can use .attr('x', [...]) and .attr('y', [...])
Also, you have to substract height/2 from y and width/2 from x, as the x and y attributes move text relative to its parent:
width = 600
height = 600
svg = d3.select('body').append('svg').attr('width',width).attr('height',height).append('g').attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')');
a = [2,5,7]
pie = d3.pie()
radius = 150
arc = d3.arc().innerRadius(0)
.outerRadius(radius);
arcs = svg.selectAll('arc').data(pie(a)).enter().append('g')
arcs.append("path").attr('d',function(d){return arc(d)})
tooltip = svg.append('text')
color = ['red','green','blue']
path = d3.selectAll('path').style('fill',function(d,i){return color[i]})
path.on('mouseover',handlemouseover)
path.on('mouseout',handlemouseout)
path.on('mousemove',handlemousemove)
function handlemouseover(){
d3.select(this).attr('d',function(d){return d3.arc().innerRadius(0)
.outerRadius(180)(d)});
}
function handlemousemove(){
// svg.append('text').text(function(){return 'shubham'}).style('top',(d3.event.layerY + 10)+'px').style('left',(d3.event.layerX + 10) + 'px')
tooltip.text(function(){return d3.event.layerX}).attr('y',(d3.event.layerY - height/2 + 10)+'px').attr('x',(d3.event.layerX - width/2 + 10) + 'px').style('display','block');
}
function handlemouseout(){
d3.select(this).attr('d',function(d){return d3.arc().innerRadius(0).outerRadius(radius)(d)});
tooltip.style('display', 'none');
}
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-path.v1.min.js"></script>
<script src="https://d3js.org/d3-shape.v1.min.js"></script>
</head>
You have to append a div and add css styles to it.
Working jsfiddle: https://jsfiddle.net/amp42fjn/
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
path.on("mouseover",handlemouseover)
path.on("mousemove", handlemousemove)
path.on("mouseout", handlemouseout);
function handlemouseover(){
d3.select(this).attr('d',function(d){return d3.arc().innerRadius(0)
.outerRadius(180)(d)});
tooltip.style("visibility", "visible");
}
function handlemousemove(){
tooltip.text(function(){return d3.event.layerX}).style('top',(d3.event.layerY + 10)+'px').style("top",
(d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");
}
function handlemouseout(){
d3.select(this).attr('d',function(d){return d3.arc().innerRadius(0).outerRadius(radius)(d)});
tooltip.style("visibility", "hidden");
}
I struggle with positioning an object to the mouse coordinates in a d3 chart I have.
var svg = d3.select(#myChart).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
const tl = svg.append('line').attr("style", "position: absolute;");
.on("mouseover", function(d) {
tl.attr('stroke', 'black')
.attr('x1', (d3.event.pageX + 5) + "px" )
.attr('x2',(d3.event.pageX + 5) + "px")
.attr('y1', 0 )
.attr('y2', height );
})
When the chart is aligned to the left, I can see the line being like 50px to the left of the mouse (as if X of mouse + 40). When the chart is positioned to the right, the line is not even visible. I would need the line to be always on the XMouse position, with 0 being constant (a vertical line).
I have a div tooltip that I would like to centre over SVG circles created using D3. My mouseover event triggers the div's 'hidden' class to false.
Plunker
Because the div is hidden, I am unable to calculate it's width and am therefore unable to centre the div on the circle the way I would like:
var width = document.getElementById("tooltip").offsetWidth;
...
.style("left", xPosition - (width/2) + "px" )
Is there a way I can get around this?
Its probably worth noting that I don't want the div to have a fixed width.
var w = 300, h = 500;
var svg = d3.select('body').append('svg').attr('width', w).attr('height', h)
svg.append('circle').attr('r', 20)
.attr('cx', 200)
.attr('cy', 300)
.on("mouseover", function(d) {
var xPosition = parseFloat(d3.select(this).attr("cx"));
var yPosition = parseFloat(d3.select(this).attr("cy"));
var width = document.getElementById("tooltip").offsetWidth;
d3.select("#tooltip")
.style("left", xPosition - (width/2) + "px" )
.style("top", yPosition + "px")
.select("#value")
.text(d);
d3.select("#tooltip").classed("hidden", false);
})
.on("mouseout", function() {
d3.select("#tooltip").classed("hidden", true);
})
You have to first give the div a display value other than "none". Then you get its width. Then you can position it.
If you don't want the user to see the div jump, give it the style visibility: hidden while you're positioning it:
CSS:
.hidden {
display: none;
visibility: hidden;
}
JS:
d3.select("#tooltip").style("display", "block");
// your div is still invisible, but now has a width.
// get its width and position it now
d3.select("#tooltip").classed("hidden", false); // now it will appear
If you hide it again, don't forget to reset the display style as well!
I want to show stacked area data on mouseover in a way it's implemented in the nvd3 example:
http://nvd3.org/examples/stackedArea.html but in pure d3.
Currently I'm displaying popup and vertical line on mouse over event, but wasn't able to display all data for stacks within the popup.
Coffescript is below.
verticalLine = svg.append('line')
.attr({
'x1': 0,
'y1': 0,
'x2': 0,
'y2': height
})
.attr("stroke", "steelblue")
.attr('class', 'verticalLine')
svg.on "mousemove", () ->
xPos = d3.mouse(this)[0]
svg.select(".verticalLine").attr("transform", () ->
"translate(" + xPos + ",0)")
tooltip.transition()
.duration(200)
.style("font-size", "12px")
.style("opacity", .9)
tooltip.html("Info")
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 28) + "px")
svg.on "mouseout", () ->
tooltip.transition()
.duration(500)
.style "opacity", 0
Here is my fiddle
Looks like not only me struggled with the problem, so I'm posting my solution below.
The idea is to get an intersection of the vertical line with x axis, i.e. find a target date, which will then allow us to grab all other fields related to that date. I used d3.bisector to find an index of the target date.
xPos = d3.mouse(this)[0]
bisectDate = d3.bisector((d) -> d.date).left
date = x.invert(xPos)
currentDateIndex = bisectDate(browsers[0].values, date)
Working code is here https://jsfiddle.net/ovvn/t44qovhg/
I'm using D3 to draw a scatter graph. I would like to show tooltips when the user mouses over each circle.
My problem is that I can append tooltips, but they're positioned using the mouse event d3.event.pageX and d3.event.pageY, so they are not positioned consistently over each circle.
Instead, some are slightly to the left of the circle, some to the right - it depends on how the user's mouse enters the circle.
This is my code:
circles
.on("mouseover", function(d) {
tooltip.html(d)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
tooltip.transition().duration(500).style("opacity", 0);
});
And is a JSFiddle showing the problem: http://jsfiddle.net/WLYUY/5/
Is there some way I can use the centre of the circle itself as the position to orient the tooltip, not the mouse position?
In your particular case you can simply use d to position the tooltip, i.e.
tooltip.html(d)
.style("left", d + "px")
.style("top", d + "px");
To make this a bit more general, you can select the element that is being moused over and get its coordinates to position the tooltip, i.e.
tooltip.html(d)
.style("left", d3.select(this).attr("cx") + "px")
.style("top", d3.select(this).attr("cy") + "px");
Found something here that might address your problem even if <body> and <svg> have different positioning. This is assuming you have absolute position set for your tooltip.
.on("mouseover", function(d) {
var matrix = this.getScreenCTM()
.translate(+ this.getAttribute("cx"), + this.getAttribute("cy"));
tooltip.html(d)
.style("left", (window.pageXOffset + matrix.e + 15) + "px")
.style("top", (window.pageYOffset + matrix.f - 30) + "px");
})
In my experience, the easist solution is as follows:
First, getBoundingClientRect() to get the position of your element.
Then, use window.pageYOffset to adjust the height, relative to where you are.
E.g.
.on('mouseover', function(d) {
let pos = d3.select(this).node().getBoundingClientRect();
d3.select('#tooltip')
.style('left', `${pos['x']}px`)
.style('top', `${(window.pageYOffset + pos['y'] - 100)}px`);
})
In the example above, I don't use X's offset because we rarely need to (unless you're scrolling horizontally).
Adding window.pageYOffset and pos['y'] gives us the current mouse position (wherever we are on the page). I subtract 100 to place the tooltip a little above it.
I'm new to D3 so this may not work for scatterplots... but found it seems to work for Bar charts... where v1 and v2 are the values being plotted.. and it seems to look up the value from the data array.
.on("mouseover", function(d) {
divt .transition()
.duration(200)
.style("opacity", .9);
divt .html(d.v1)
.style("left", x(d.v2)+50 + "px")
.style("top",y(d.v1)+ "px");})