I have been experimenting with tooltips in d3.js but am struggling to get it to behave accurately.
This is my div.tooltip:
div.tooltip {
position: absolute;
text-align: center;
width: 60px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
And here is my function, where I am trying to plot some data points. x-axis is date and y-axis is price. The data are colored by symbol. When hovering over each point, I am trying to get the data point to enlarge its color and fill with its color. This is working fine. I also want a little textbox to float to the top-right of it with information about the date/price. For some reason, this is appearing at the bottom left of my chart underneath the x-axis for every data point. As shown below:
// Add some datapoints
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 1.5)
.style("fill", "white")
.style("opacity", .8)
.style("stroke-width", 1)
.attr("cx", function(d) { return x(d.date); })
.attr("cy", function(d) { return y(d.price); })
.style("stroke", function(d) { return color(d.symbol); })
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", function(d) { return color(d.symbol); });
div.transition()
.duration(200)
.style("opacity", .9);
div.html(formatTime(d.date) + "<br/>" + d.price)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY-28) + "px");
})
e.g. When I hover over this green point, it enlarges and fills, but the textbox appears in the bottom left with no text?
Related
I am new to d3 and javascript in general. I build this fiddle here, which is a basic d3 force diagram, with an .onmousover event which shows a div tooltip when the user hovers over a node
https://jsfiddle.net/1qe1gp06/20/
the content from the tooltip comes from the node data
var nodes = [
{ x: width/3, y: height/2, "content":"small" },
{ x: 2*width/3, y: height/2, "content":"biggerbiggerbiggerbiggerbiggerbigger"}
];
d3 node javascript
var node = svg.selectAll('.node')
.data(nodes)
.enter().append('circle')
.attr('class', 'node')
.on("mouseover", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d.content + "<br/>" + d.content + "<br/>" + d.content + "<br/>" + d.content + "<br/>" )
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
;
append div tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
css for the div tooltip
div.tooltip {
position: absolute;
overflow: hidden;
text-align: center;
width: 200px;
height: 50px;
padding: 2px;
font: 15px "Courier New";
color: #F2F3F4;
background: #5D6D7E;
border: 1px;
border-radius: 5px;
border-style: solid;
border-color: #BDC3C7;
pointer-events: none;
}
However, I don't know how to scale the tooltip so that it expands or shrinks based upon the length of the text or number of line breaks in the content.
enter image description here
It is probably easiest seen in the fiddle
can anyone help please?
thanks
I have forked and updated your fiddle I had to make a few corrections so it would run (probably just some typos and not relevant to the question). The thing that needed changing was:
div.tooltip {
width: 200px;
}
to
div.tooltip {
width: max-content;
}
Hard coding a width will enforce that width regardless of the elements contents. max-content will basically make the div as big as it needs to be to fit what is inside. MDN Docs
I'm trying to write an example using D3.js where images lay on a static background. I can move the background and zoom and I can also move separate images (something like this but with images). Also, I need to display a tooltip when the mouse is over an image.
Here what I have tried with d3js v4:
Problems:
When zooming the tooltip is displayed at an incorrect location (The problem is obviously in handleMouseOver where d.x,d.y is just the initial position of the image, but I don't know how to get translate and scale parameters of the current view)
Width of tip is not resized automatically to the text width (should I change div.tooltip somehow?).
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript" src="data.json"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
div.tooltip {
position: absolute;
text-align: center;
width: 120px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
</style>
</head>
<body>
<script>
IS_DRAGGING = false
var svg = d3.select("body")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.style("background-color", "#E59400") // orange
.call(d3.zoom().on("zoom", function () {
svg.attr("transform", d3.event.transform)
}))
.append("g");
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
function handleMouseOver(d, i) {
if(!IS_DRAGGING) {
div.transition()
.duration(100)
.style("opacity", .9);
div.html(d.url)
.style("left", (d.x+32) + "px")
.style("top", (d.y-28) + "px");
}
}
function handleMouseOut(d, i) {
div.transition()
.duration(100)
.style("opacity", 0);
}
function drag_strart(d) {
d3.select(this).raise().classed("active", true);
div.transition()
.duration(100)
.style("opacity", 0);
}
function dragging(d) {
d3.select(this).select("image")
.attr("x", d.x = d3.event.x)
.attr("y", d.y = d3.event.y);
IS_DRAGGING = true
}
function drag_end(d) {
d3.select(this).classed("active", false);
IS_DRAGGING = false
}
var arr = JSON.parse(data);
arr.forEach(function(d) {
d.x = getRandom(0, document.body.clientWidth);
d.y = getRandom(0, document.body.clientHeight);
})
var group = svg.selectAll('g')
.data(arr)
.enter().append("g").call(d3.drag()
.on("start", drag_strart)
.on("drag", dragging)
.on("end", drag_end))
.on("mouseover", handleMouseOver)
.on("mouseout", handleMouseOut)
group.append("image")
.attr("xlink:href", function(d) { return d.img_path; })
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", 32)
.attr("height", 32)
</script>
</body>
Also, I have tried d3js v3 and the d3-tip library:
They work, but have some side effects:
For some reason, I can't move images separately.
When I zoom I want the tooltip to disappear (now it just stays at the same location and is even not redrawn at a new position and at the scale of the image)
Sometimes something like 'blinking' of the tooltip is happening (looks like it turns on and switch off many times when the cursor is near the edge of the image).
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="d3.tip.js"></script>
<script type="text/javascript" src="data.json"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
div.tooltip {
position: absolute;
text-align: center;
width: 120px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
.d3-tip {
line-height: 1.5;
font-weight: normal;
font-family: Helvetica, Arial, sans-serif;
font-size:13px;
padding: 10px;
background: rgba(0, 0, 0, 0.8);
color: #fff;
border-radius: 3px;
position:relative;
z-index:101;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 20px;
width: 100%;
line-height: .5;
color: rgba(0, 0, 0, 0.8);
content: "\25BC";
position: absolute;
text-align: center;
}
/* Style northward tooltips differently */
.d3-tip.n:after {
margin: -1px 0 0 0;
top: 100%;
left: 0;
}
</style>
</head>
<body>
<script>
var svg = d3.select("body")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.style("background-color", "#E59400") // orange
.call(d3.behavior.zoom().on("zoom", function () {
svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")")
}))
.append("g");
function getRandom(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
function dragging_start(d) {
d3.select(this).raise().classed("active", true);
}
function dragging(d) {
d3.select(this).select("image")
.attr("x", d.x = d3.event.x)
.attr("y", d.y = d3.event.y);
}
function dragging_end(d) {
d3.select(this).classed("active", false);
}
var arr = JSON.parse(data);
arr.forEach(function(d) {
d.x = getRandom(0, document.body.clientWidth);
d.y = getRandom(0, document.body.clientHeight);
})
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) { return d.url; });
var drag = d3.behavior.drag()
.on("dragstart", dragging_start)
.on("drag", dragging)
.on("dragend", dragging_end);
var group = svg.selectAll('g')
.data(arr)
.enter().append("g").call(drag)
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
group.append("image")
.attr("xlink:href", function(d) { return d.img_path; })
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", 32)
.attr("height", 32)
.call(tip);
</script>
</body>
Update:
I figured out that adding d3.event.sourceEvent.stopPropagation(); were crucial, now I can move images separately. But the problem that looks like mouse cursor is moving faster than the image itself and also I subtracted half of image w,h (in my case 32/2) to drag the image at the center (by default it was the upper left corner).
function dragging_start(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).raise().classed("active", true);
}
function dragging(d) {
d3.select(this).select("image")
.attr("x", d.x = d3.event.x-16)
.attr("y", d.y = d3.event.y-16);
}
i am unable to resize my bar chart within a script tag. Currently the bar chat is showing up massive on the page and I would like to resize the chart into a smaller size on the page.
Is there a way to resize this using CSS or JavaScript or put it into a Div? I tried to give the script tag a 'id' or 'value' but no luck.
Could someone help me resize my chart please.
this is me d3.js:
<script>
data = [
{label:"Jan Sales", value:35},
{label:"XMAS", value:5},
];
var div = d3.select("body").append("div").attr("class", "toolTip");
var axisMargin = 20,
margin = 40,
valueMargin = 4,
width = parseInt(d3.select('body').style('width'), 10),
height = parseInt(d3.select('body').style('height'), 10),
barHeight = (height-axisMargin-margin*2)* 0.2/data.length,
barPadding = (height-axisMargin-margin*2)*0.6/data.length,
data, bar, svg, scale, xAxis, labelWidth = 0;
max = d3.max(data, function(d) { return d.value; });
svg = d3.select('body')
.append("svg")
.attr("width", width)
.attr("height", height);
bar = svg.selectAll("g")
.data(data)
.enter()
.append("g");
bar.attr("class", "bar")
.attr("cx",0)
.attr("transform", function(d, i) {
return "translate(" + margin + "," + (i * (barHeight + barPadding) + barPadding) + ")";
});
bar.append("text")
.attr("class", "label")
.attr("y", barHeight / 2)
.attr("dy", ".35em") //vertical align middle
.text(function(d){
return d.label;
}).each(function() {
labelWidth = Math.ceil(Math.max(labelWidth, this.getBBox().width));
});
scale = d3.scale.linear()
.domain([0, max])
.range([0, width - margin*2 - labelWidth]);
xAxis = d3.svg.axis()
.scale(scale)
.tickSize(-height + 2*margin + axisMargin)
.orient("bottom");
bar.append("rect")
.attr("transform", "translate("+labelWidth+", 0)")
.attr("height", barHeight)
.attr("width", function(d){
return scale(d.value);
});
bar.append("text")
.attr("class", "value")
.attr("y", barHeight / 2)
.attr("dx", -valueMargin + labelWidth) //margin right
.attr("dy", ".35em") //vertical align middle
.attr("text-anchor", "end")
.text(function(d){
return (d.value+"%");
})
.attr("x", function(d){
var width = this.getBBox().width;
return Math.max(width + valueMargin, scale(d.value));
});
bar
.on("mousemove", function(d){
div.style("left", d3.event.pageX+10+"px");
div.style("top", d3.event.pageY-25+"px");
div.style("display", "inline-block");
div.html((d.label)+"<br>"+(d.value)+"%");
});
bar
.on("mouseout", function(d){
div.style("display", "none");
});
svg.insert("g",":first-child")
.attr("class", "axisHorizontal")
.attr("transform", "translate(" + (margin + labelWidth) + ","+ (height - axisMargin - margin)+")")
.call(xAxis);
</script>
This is my CSS:
svg {
width: 100%;
height: 100%;
position: center;
}
.toolTip {
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
position: absolute;
display: none;
width: auto;
height: auto;
background: none repeat scroll 0 0 white;
border: 0 none;
border-radius: 8px 8px 8px 8px;
box-shadow: -3px 3px 15px #888888;
color: black;
font: 12px sans-serif;
padding: 5px;
text-align: center;
}
text {
font: 15px sans-serif;
color: white;
}
text.value {
font-size: 100%;
fill: white;
}
.axisHorizontal path{
fill: none;
}
.axisHorizontal .tick line {
stroke-width: 1;
stroke: rgba(0, 0, 0, 0.2);
}
.bar {
fill: steelblue;
fill-opacity: .9;
font-size: 120%;
}
#search {
position:absolute;
top: -2%;
}
.tablebad thead tr {
background-color: #eee;
}
.tablegood thead tr th {
background-color: #eee;
}
Add a viewBox attribute to the svg element. This will allow you to lock the svg to the exact dimensions of your graph using the min-x, min-y, width and height parameters.
Then you can resize the svg element and you're graph will stretch to grow bigger or smaller.
https://www.w3.org/TR/SVG11/coords.html#ViewBoxAttribute
Edit:
Here is your js fiddle updated to use viewBox. I've commented out the width and height attributes so that it resizes as you make the page wider or narrower.
https://jsfiddle.net/cexLbfnk/1/
Is this the sort of thing you're looking for?
i'm learning to use javascript and d3.js. I made a simple linechart with tool tip that works perfectly (see here).
I decided to make it responsive, so using a template a make a new one (see here).
responsiveness is ok but i can't add the tooltip div.
any help?
here the piece of code that it' problematic:
chart2.selectAll("dot")
.data(data)
.enter().append("rect")
.attr("width", 1)
.attr("height", 120)
.style("opacity", 0) // set the element opacity
.style("stroke", "#6f6f6f") // set the line colour
.attr("xScale", function(d) { return xScale(d.date); })
.attr("yScale", function(d) { return yScale(d.close)-60; })
.on("mouseover", function(d)
{
d3.select(this).attr("width", 1).style("opacity", .8) ; //il punto cambia al mousover (bellissmo)
div.transition()
.duration(70)
.style("opacity", .8)
.style("border", "1px");
div .html(formatTime(d.date) + "<br/>" + d.close)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 64) + "px");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.1).style("opacity", .0);
div.transition()
.duration(200)
.style("opacity", 0);
});
the div is definied here:
var div = d3.select("#chart2").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
and in the css is:
div.tooltip {
position: absolute;
text-align: center;
width: 95px;
height: 40px;
padding: 2px;
font: 15px arial;
font-weight: bold;
color: black;
background: #f0f0f0;
pointer-events: none;
}
define div as following and select "body" and append div there
var div = d3.select('body').append("div")
.attr("id", "chart")
.attr("class", "tooltip")
.style("opacity", 0);
I am embarking on a journey to learn to visualize data using d3.js, and so far I am finding the "Interactive Data Visualization" by Scott Murray very helpful. I was following through some of the example codes in book chapter 11, and was wondering how I would add the tooltip to the pie chart (the book already describes this procedure using the bar chart). Anyways, just been tinkering around with the codes for past couple of hours and would like to see if anyone can lend me a hand on this:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3: Pie layout</title>
<script type="text/javascript" src="d3/d3.v3.js"></script>
<style type="text/css">
text {
font-family: sans-serif;
font-size: 12px;
fill: white;
}
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: white;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-mox-box-shadow: 4px 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rbga(0, 0, 0, 0.4)
pointer-events: none;
}
#tooltip.hidden {
display: none;
}
#tooltip p {
margin: 0;
font-family: sans-serif;
font-size: 16px;
line-height: 20px;
}
</style>
</head>
<body>
<div id="tooltip" class="hidden">
<p><strong>Important Label Heading</strong></p>
<p><span id="value">100</span>%</p>
</div>
<script type="text/javascript">
//Width and height
var w = 300;
var h = 300;
var dataset = [ 5, 10, 20, 45, 6, 25 ];
var outerRadius = w / 2;
var innerRadius = 0;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie();
// Easy colors accessible via a 10-step ordinal scale
var color = d3.scale.category10();
// Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Set up groups
var arcs = svg.selectAll("g.arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")")
.on("mouseover", function(d){
d3.select("#tooltip")
.select("#value")
.text(d);
d3.select("tooltip").classed("hidden",false);
})
.on("mouseout", function() {
// Hide the tooltip
d3.select("#tooltip").classed("hidden", true);
});
// Draw arc paths
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
// Labels
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d) {
return d.value;
});
</script>
</body>
</html>
I know this is bit to digest, but what I want to know more specifically is how to set the x and y value for the tool-tip. Thank you in advance.
I prefer to use the opacity to show/hide the tooltip. Here is the FIDDLE. This should get you going.
d3.select("#tooltip")
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY + "px")
.style("opacity", 1)
.select("#value")
.text(d.value);
I'm adding mouse move event on FernOfTheAndes's answer, This will makes it more pretty usecase. Hope this will be helpful
.on("mouseover", function(d) {
d3.select("#tooltip").style('opacity', 1)
.select("#value").text(d.value);
})
.on("mousemove", function(d) {
d3.select("#tooltip").style("top", (d3.event.pageY - 10) + "px")
.style("left", (d3.event.pageX + 10) + "px");
})
.on("mouseout", function() {
d3.select("#tooltip").style('opacity', 0);
});