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!
Related
I have a d3js pie chart which shows hover data.
How to get a close icon on hover on the mouse to close that hover.
I tried css to get it but not working.
paths.on("mouseover", function(d){
d3.select("#" + _this.tooltipId)
.style("left", (d3.event.clientX + window.scrollX) + "px")
.style("top", (d3.event.clientY + window.scrollY) + "px")
.select("#value")
.html(_this.config.tooltip(d.data, _this.config.label));
d3.select("#" + _this.tooltipId).classed("crd3Hidden", false);
var endAngle = d.endAngle + 0.05;
var startAngle = d.startAngle - 0.05;
var arcOver = d3.svg.arc().innerRadius(innerRadius)
.outerRadius(outerRadius + 1).endAngle(endAngle).startAngle(startAngle);
this.parentNode.parentNode.appendChild(this.parentNode);//the path group is on the top with in its parent group
this.parentNode.parentNode.parentNode.appendChild(this.parentNode.parentNode);
d3.select(this)
.style("stroke", "black")//#5eecfd
.style("opacity", 10)
.attr("d", arcOver)
.style("stroke-width", "4px");
The most easy way to create tooltips is by appending a title element. The title element will have the calssical behavior of a tooltip.
This could be done without the usage of "mouseover".
paths.append('title').text('');
If you want to stick to your own solution you can revert the "mouseover" event with the usage of the "mouseleave" event.
paths.on("mouseleave", function(d){
d3.select("#" + _this.tooltipId).remove();
})
Addendum to answer your comment.
Well, you could try something like this. But i cannot gurantee you that it will work or have no sideeffects.
paths.on("mouseleave", function(d){
setTimeout(() => {
d3.select("#" + _this.tooltipId).remove();
})
},10000); // 10000ms => 10 seconds
});
Another way would be to use an animation for that:
paths.on("mouseleave", function(d){
d3.select("#" + _this.tooltipId)
.remove()
.transition()
.duration(10000);
});
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 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.
I created a div in my html,
<div id="search"></div>
I want to set its width to 15% of my window width in my Javascript. I'm attempting to use D3 to do this:
var width = 0.15 * window.innerWidth,
height = 0.95 * window.innerHeight;
var searchcolumn = d3.select("#search")
.attr("width", width)
.attr("height", height);
I can see in the DOM, the width and height of my div has been changed:
However, in the rendered html the div is just 10px x 10px.
Why does this happen? I do not have any CSS that overwrites the size of the div. How do I set my div to my desired width?
This is an old post but for anyone else wanting to solve this, the following will work :
var width = 0.15 * window.innerWidth,
height = 0.95 * window.innerHeight;
var searchcolumn = d3.select("#search")
.style("width", width + 'px')
.style("height", height + 'px');
I have added is + 'px'; and also changed .attr to .style as it's the CSS you are working with here.
Working code :
var data = [10,12,6,8,15];
var svg = d3.select('body')
//.append('svg')
.attr('width', 500).attr('height', 500);
var width = 0.15 * window.innerWidth,
height = 0.95 * window.innerHeight;
svg.selectAll('div')
.data(data)
.enter().append('div')
.attr('class','divs')
.attr('x', function(d,i) { d.clicked=false; return 100+100*i; })
.attr('y', function(d,i) { return 0; })
.style('width', width + 'px')
.style('height',height +'px')
.attr('background-color', function() { return 'red'; })
.text(function(d){ return d;})
.divs {
background: blue;
display:inline-block;
float:left;
margin:5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
Try:
d3.select("#search")
.style("height", height)
.style("width", width);
You can simply do the following when you want to set multiple style attributes instead of repeating style(" ", " ") n times:
d3.select("#search").style({
'height': height+'px',
'width': width+'px'
});
Hope that helps.
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");})