I implement a tooltip over circles placed through d3 on a leafletmap like this:
var tooltip = d3.select("body")
.append("div")
.attr("id", "mytooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
feature.on("mouseover",function(d) {
d3.select(this)
.transition()
.ease("elastic")
.duration(500)
.attr('r', function (d){
return (d.properties.xy * 5)
.style("stroke", "black")
d3.select("#mytooltip")
.style("visibility", "visible")
.text(d.properties.xy1 + " " + d.properties.xy2)
});
feature.on("mousemove", function() {
return tooltip.style("top", (d3.event.pageY-10)+"px")
.style("left",(d3.event.pageX+10)+"px");
});
feature.on("mouseout",function(d) {
d3.select(this)
.transition()
.ease("elastic")
.duration(500)
.attr('r', function (d){
return (d.properties.xy);
})
.style("stroke", "none")
d3.select("#mytooltip")
.style("visibility", "hidden")
});
Where my feature is this:
var feature = g.selectAll("circle")
.data(myData.features)
.enter()
//...
I wonder how I can style the tooltip that shows up? Is there a way to give it a background, write something in bold, italic, different colors etc?
This is what I like to do. First, I set the CSS style for the tooltip, using a div with a class named "tooltip":
div.tooltip {
position: absolute;
etc...
}
Then I set a tooltip var (here, svgId is the ID of the element where you append your SVG, not much different of selecting "body" as you did):
var tooltip = d3.select("#svgId").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
The div has 0 opacity. Then it's just a matter of showing the tooltip on mouseover or mousemove:
selection.on("mousemove", function(d) {
tooltip.html("<strong> Look, I'm bold !</strong> and now I'm not bold<br>
and this is another line!and this is my data: " + d.whatever)
.style('top', d3.event.pageY - 12 + 'px')
.style('left', d3.event.pageX + 25 + 'px')
.style("opacity", 1);
});
You can use HTML tags to style your text inside the tooltip, making it bold, italic etc. And, finally, we make the tooltip disappear on mouseout (as you did):
selection.on("mouseout", function(d) {
tooltip.style("opacity", 0);
});
Since the div with 0 opacity still takes space in the page, a better approach is changing its display property from none to block during the mouseover, and back to none in the mouse out.
You can style the tooltip with CSS. You could do that in a separate .css file, in a <style> tag, or with d3 the same way you give the tooltip visibility. Something like .style("background", "rgba(179, 107, 0, 0.5)")
Related
I have made a map with D3 and using some data from nasa.gov(https://data.nasa.gov/resource/y77d-th95.geojson)
Here is the codepen
http://codepen.io/redixhumayun/full/VPepqM/
I have tried making a tooltip with the following code.
//setting up the tooltip here
var div = svg.append('div')
.attr('class', 'tooltip')
.style('opacity', 0.7);
var meteorites = meteorite.selectAll('circle')
.data(data.features)
.enter()
.append('circle')
.attr('cx', function(d) {
return projection([d.properties.reclong, d.properties.reclat])[0]
})
.attr('cy', function(d) {
return projection([d.properties.reclong, d.properties.reclat])[1]
})
.attr('fill', function(d) {
return color_scale(d.properties.mass)
})
.attr('stroke', 'black')
.attr("stroke-width", 1)
.attr('r', function(d) {
return weight_scale(d.properties.mass);
})
.attr('fill-opacity', function(d) {
if (weight_scale(d.properties.mass) > 7) {
return 0.5
}
return 1;
})
.on('mouseover', function(d) {
div.transition().duration(200)
.style('opacity', 0.9)
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY / 1.5) + 'px')
div.html('<p>Please show up</p>');
}).on('mouseout', function(d){
div.transition().duration(200)
.style('opacity', 0);
})
However, the tooltip does not show up. I even tried changing the z-index of the tooltip to be greater than that of the underlying map so that it wouldn't be hidden by the map, but no luck.
When I inspect the tooltip in the elements inspector, it shows that the style, left and top attributes of the tooltip div are changing, but I can't seem to see it on the screen. Not sure what I'm doing wrong here.
You have three problems here:
First, set the position of the <div> to absolute in the CSS:
position: absolute;
Second, the biggest problem: you cannot append a <div> to an SVG. The good news is that you don't need to (since we just set the tooltip div to an absolute position). So, append the div to the body:
var div = d3.select("body")
.append('div')
.attr('class', 'tooltip')
.style('opacity', 0.7);
Third problem: set the pointer-events to none or move the tooltip a little bit to the right, otherwise it will get in the way of your mouseover event:
.style('left', d3.event.pageX + 10 + 'px')
This is your updated CodePen: http://codepen.io/anon/pen/GrqKBY?editors=0110
http://codepen.io/anon/pen/YNWKpr
var div = svg.append('foreignObject').append('xhtml:div')
.attr('class', 'tooltip')
.style('opacity', 0.7);
You have to wrap non-svg elements in a foreignObject tag, and you have to specify the html namespace when appending html elements.
I am working on D3.js and I have a map with lines and nodes. When I come on them with mouse I want them to change color and get thicker and I want a small pop-up window which shows their IDs.
On HTML for the pop-up there is a function onmouseover.
First question: Is there any function in javascript like onmouseover?
Second question: Is there any way me to change the color and make the lines or nodes thicker when the mouse are on that specific node or line. (I can use JQuery if there is a way in JQuery)
For the tooltip I have this fiddle : http://jsfiddle.net/reko91/7NReF/36/
Firstly, create the container for the toolip :
var tooltip = d3.select("body")
.append("div")
.attr('class','tooltipdiv')
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
Then on mouseover set the text to what you want, mousemove, move the tooltip, and mouseout, hide the tooltip :
circles.on("mouseover", function(d){return tooltip.style("visibility", "visible").text(d);})
.on("mousemove", function(){return tooltip.style("top",
(d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
.on("mouseout", function(){return tooltip.style("visibility", "hidden");});
As for the nodes, you can either style it directly or add a class and do it all in one. So you only need to change the CSS. So with the above being said, I have implemented both in this fiddle : http://jsfiddle.net/reko91/7NReF/37/
Source code :
var w = 500;
var h = 50;
var dataset = [ 5, 10, 15, 20, 25 ];
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var circles = svg.selectAll("circle")
.data(dataset)
.enter()
.append("circle");
circles.attr("cx", function(d, i) {
return (i * 50) + 25;
})
.attr("cy", h/2)
.attr("r", function(d) {
return d;
});
var tooltip = d3.select("body")
.append("div")
.attr('class','tooltipdiv')
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
circles.on("mouseover", function(d){
d3.select(this).classed('hovernode', true)
return tooltip.style("visibility", "visible").text(d);})
.on("mousemove", function(){return tooltip.style("top",
(d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
.on("mouseout", function(){
d3.select(this).classed('hovernode', false)
return tooltip.style("visibility", "hidden");});
.tooltipdiv{
background:white;
}
.hovernode{
fill:red;
stroke:blue;
stroke-width:5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
</body>
I have added the ability to change the border of the nodes
var node = svg.selectAll(".node")
.data(Data.nodes)
.on("mouseover", function(){
d3.select(this).style("stroke","thick"); // sample CSS operation.
})
this is the element a node in your case which you want to work on.
I would suggest against using jquery. I somehow feel d3 is faster than jquery and can implement similar functionality with greater control.
Okay I wrote a piece of code as an answer to change the color and it works:
.on("mouseover", function(){
d3.select(this)
.attr("fill", "orange");
})
.on("mouseout", function(d){
d3.select(this)
.attr("fill", "rgb(0, 0, " +(d*10) + ")");
});
first .on function makes it orange(you can change the color however you want, second .on makes the original color when you're not on the element anymore.
For pop-up window:
.append("title")
.text(function(d){return d;});
Within a Callbackfunction I have this:
/...
feature.on("mouseover",function(d) {
d3.select(this)
.transition()
.ease("cubic")
.duration(10)
.attr('r', function (d){
return (d.x);
})
.tooltip.style("visibility", "visible");
});
My tooltip is defined like so:
var tooltip = d3.select("body")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
However, the tooltip won't pop up. Any ideas why?
How would I append the tooltip to the mouseoverevent? And: How would I change the text in there (assuming I want text from my data which I can access with function(d) {....)
Code extracted from here
First give an ID or class to your tooltip for selection like this:
var tooltip = d3.select("body")
.append("div")
.attr("id", "mytooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden")
.text("a simple tooltip");
Next Instead of doing this:
feature.on("mouseover",function(d) {
d3.select(this)
.transition()
.ease("cubic")
.duration(10)
.attr('r', function (d){
return (d.x);
})
.tooltip.style("visibility", "visible");//this is wrong
});
Do this to show tooltip on mouse over:
feature.on("mouseover",function(d) {
d3.select(this)
.transition()
.ease("cubic")
.duration(10)
.attr('r', function (d){
return (d.x);
});
//show tooltip on hover
d3.select("#mytooltip")
.style("visibility", "visible")//set style to it
.text("new tooltip")//set text to it
});
Furthermore, add this for the tooltip to show up next to the mouse (as shown here)! Otherwise the tooltip probably gets displayed somewhere on your site where you cannot see it!
feature.on("mousemove", function() {
return tooltip.style("top", (d3.event.pageY-10)+"px")
.style("left",(d3.event.pageX+10)+"px");
});
I am making an interactive D3.js chart with filters that display points when the user clicks the selected checkbox. Additionally, on a mouseover event a popup will appear next to the selected point with some information.
Because there is a relatively large number of points on the chart, I opted to make the relevant points transparent when the corresponding checkbox is de-selected, rather than removing the points and re-drawing them (which lags a little on slower machines).
The code I currently have for this works. The code for displaying the tooltips also works. However, they do not work well together.
When the data point is de-selected, the user cannot see it, but because it still exists the browser still displays the tooltip for the de-selected points on mouseover. Therefore I have the issue of "phantom" tooltips appearing when the user moves the mouse over a currently-transparent point.
I have tried to enclose the code that makes the tooltips appear in an if statement as shown below, but this does not work. Unsure if my syntax is wrong or if this behavior is not correct.
This is the original code that mostly works. Tooltips appear, the right datapoints turn transparent, but tooltips appear over transparent points.
svg.selectAll("path")
.data(dataSet)
.enter().append("path")
.attr("class", "dot")
//other stuff goes here
//code to make tooltip appear on mouseover
.on("mouseover", function(d) {
if(d.style("opacity", 0)=false){
div.transition()
.duration(200)
.style("opacity", .8);
div .html(d.datetime.substring(0,10) )
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 24) + "px");
}
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
//code for tooltip itself
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
});
//code to make de-selected points transparent
d3.selectAll("[name=cat1]").on("change", function() {
var selected = this.value;
display = this.checked ? "inline" : "none";
svg.selectAll(".dot")
.filter(function(d) {return selected == d.rainSnowStatus;})
.attr("display", display);
});
This is what I tried to do (placing an if statement inside the mouseover function so it only activates when the datapoint is not transparent), but it does not work (the tooltips fail to appear altogether).
//tooltip code within an if statement; does not work
.on("mouseover", function(d) {
if(svg.dot.style("opacity", 0)==false){ // << IS THIS RIGHT?
div.transition()
.duration(200)
.style("opacity", .8);
div .html(d.datetime.substring(0,10) )
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 24) + "px");
}
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
Any help is appreciated. Thanks!
You can get the current value of the opacity attribute by running d3.select(this).style("opacity"), so to check it in your mouseover handler you would do
.on("mouseover", function(d) {
if(d3.select(this).style("opacity") != 0){
div.transition()
.duration(200)
.style("opacity", .8);
div .html(d.datetime.substring(0,10) )
.style("left", (d3.event.pageX + 5) + "px")
.style("top", (d3.event.pageY - 24) + "px");
}
})
I've been looking for a way to have my legend control my chart animation (similar to NVD3 examples). I've run into a problem though - nested selections.
var legend = svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(70,10)")
;
var legendRect = legend.selectAll('rect').data(colors);
legendRect.enter()
.append("rect")
.attr("x", w - 65)
.attr("width", 10)
.attr("height", 10)
.attr("y", function(d, i) {
return i * 20;
})
.style("stroke", function(d) {
return d[1];
})
.style("fill", function(d) {
return d[1];
});
I'm using a bit of a hack to do my animation. Basically setting style to display: none.
I want to be able to click on the rectangles and call the function. But putting a mouseover or onclick within legendRect doesn't work. The bars to animate are not children of the legend. How can I call the function, or chain my function to my legend?
function updateBars(opts) {
var gbars = svg.selectAll("rect.global");
var lbars = svg.selectAll("rect.local");
if (opts === "global") {
gbars.style("display", "block") ;
lbars.style("display", "none");
gbars.transition()
.duration(500)
.attr("width", xScale.rangeBand());
};
if (opts === "local") {
lbars.style("display", "block")
;
gbars.style("display", "none");
lbars.transition()
.duration(500)
.attr("x", 1 / -xScale.rangeBand())
.attr("width", xScale.rangeBand());
};
}
My other obstacle is changing the fill color on click. I want it to almost imitate a checkbox, so clicking (to deselect) would turn the fill white. I tried something similar as .on("click",(".style" ("fill", "white"))). But that is incorrect.
Here is my fiddle. For some reason, the function isn't updating things on Fiddle. It works on my localhost though. Not sure the problem with that.
I'm not completely sure I understand you correctly, but if your first question is how to change element X when clicking on element Y, you need something along the lines of:
legendRect.on("click", function() {
gbars.transition()
.duration(500)
.style("display", "block")
// etc...
}
As for changing the fill on click, try:
gbars.on("click", function() {
d3.select(this)
.attr("fill", "white");
}