How get close icon(X) on hover on mouseover in d3.js - javascript

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);
});

Related

How add text in D3 mouseover?

I'm working on a heatmap chart in D3 and I can't figure out how to add the text on mouseover. I am not sure how to proceed. If you could give me some clues, I would appreciate it. In the following snippet you can find the code. both the working and the non-working codeblocks. Thanks!
console.log(d3)
let screenWidth = 800
let screenHeight = 400
//load data
d3.csv('./datos/locations.csv').then(function(data){
let filtered = []
for(let item of data) {
if(item.location === "location one") {
filtered.push(item)
}
}
build(filtered)
})
//Create canvas
function createSVG() {
let container = d3.select('#container')
svg = container.append('svg')
.attr('id', 'canvas')
.attr('width', screenWidth)
.attr('height', screenHeight)
}
//Create chart
function build(data) {
let rectWidth = screenWidth / 24
let rectHeight = screenHeight / 7
let rects = svg.selectAll('rect')
.data(data)
.enter()
.append('rect')
.attr('x', function(d,i) {
return (parseInt(d.hour) - 1) * rectWidth})
.attr('y', function(d,i){
return (parseInt(d.day) - 1) * rectHeight})
.attr('height', rectHeight)
.attr('width', rectWidth)
.style('fill', 'black')
.style('stroke', 'white')
.on('mouseover', function(d,i) {
let rects = d3.select(this)
.append('text')
.attr('x')
.attr('y')
.style('font-weight', 500)
.style('font-family', 'Arial')
.style('fill', 'red')
.text(function (d,i) {return d.value})})
}
function main() {
createSVG()
build()
}
main()
```
You can append a <div> with position: absolute to body and position it on mousemove event. Change the opacity to update its display or hidden.
var div = d3.select('body').append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
...
.on('mouseover', function(d) {
div.transition()
.duration(200)
.style('opacity', .9);
div.html('<h3>' + d.status + '</h3>' + '<p>' + timeFormat(new Date(d.date)) + ' at ' + monthDayFormat(new Date(d.date)) + '</p>')
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
})
https://jsfiddle.net/z9ucLqu2/
<text> nodes cannot be children of <rect>s, only just as <line>s or <circle>s can't. They are figure nodes and are not meant to have children. Append the tooltip to the SVG or a <g> instead.
This means that you cannot access d.value through the function (d,i) {return d.value}) anymore, but you can get it because you have access to d from .on('mouseover', function(d,i) {, just remove everything but d.value.
If you use x and y from the <rect>, what is going to happen is that the <text> element covers the <rect>, catches the mouse event and triggers a mouseout immediately on the <rect>. Since you'll probably want to remove the tooltip on mouseout, you'll get the text node flickering on and off. Either move the text to the right by at least rectWidth or use d3.event to get the mouse coordinates of the event and position it a little down and to the right, using something like .attr('x', d3.event.clientX + 10) to move it right.

D3.js tool tips on hover

I,m following this tutorial for tool tips for my graph :
http://bl.ocks.org/d3noob/c37cb8e630aaef7df30d
and its working like a charm !
However there is one issue...
The current graph showing in the tutorial has black dots on the line...
I want the tool tips and black dots to appear only when i hover on them and not always like it is currently showing.
Is there a way to do that ?
Updated answer with mouseout:
https://plnkr.co/edit/9Ej1MYpGqxBdeWO2FUNO?p=preview
.on("mouseover", function(d) {
// show circle selected
d3.select(this)
.transition()
.duration(200)
.style("opacity", 0.9);
.on('mouseout', function(d) {
// hide the circle
d3.select(this)
.transition()
.duration(100)
.style("opacity", 0);
// hide the tooltip
d3.selectAll(".tooltip")
.transition()
.duration(100)
.style("opacity",0);
To use mouseout, you need to move the tooltip slightly up, and move the whole svg a bit downwards.
div.html(
'<a href= "http://google.com">' + // The first <a> tag
formatTime(d.date) +
"</a>" + // closing </a> tag
"<br/>" + d.close)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 42) + "px"); // up a bit
var margin = {top: 50, right: 20, bottom: 30, left: 50}, // down a bit
Since mouseout is very sensitive, the circle will disappear immediately after you move away your mouse, so it is better to increase the radius a bit:
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 8) // slightly bigger for human reaction
Still, I think without mouseout is a better and more intuitive approach:
Old working example (takes a few seconds to load): https://plnkr.co/edit/IitMgKW0jDYlWifokcZB?p=preview
The changes you need to make is in .on("mouseover", function(d), add the following code:
.on("mouseover", function(d) {
// hide other circles
d3.selectAll('circle')
.style("opacity", 0);
// show circle selected
d3.select(this)
.transition()
.duration(200)
.style("opacity", 0.9);
.on("mouseout", function(d) would not work for this case because the circles overlap with the tooltip.
This is a simple d3 tooltip you can look the code! (It's very little )
https://github.com/cbertelegni/tooltip_d3js/
function tooltipd3(tltp_name){
"use strict";
var s = {};
s.name = tltp_name ? tltp_name : "tooltipd3";
s.w = 0; // width tooltip
s.h = 0; // height tooltip
s.t = d3.select("body").append("div") // tooltip html node
.attr("class", s.name)
.style("opacity", 1e-6)
.style("position", "absolute");
s.mouseover = function(html) {
/** #param {string} html - Is the content for tooltip */
s.t.html(html)
.transition()
.duration(300)
.style("opacity", 1);
/** After innerhtml on tooltip get w & h */
s.get_t_size();
};
s.mousemove = function(){
s.t.style("left", (d3.event.pageX - s.w/2) + "px")
.style("top", (d3.event.pageY - s.h - 5) + "px")
.style("opacity", 1);
};
s.mouseout = function() {
s.t.transition()
.duration(300)
.style("opacity", 1e-6)
.each("end", function(){
s.t.html("");
});
};
/** Get width and height of tooltip and set w & h of Tooltip class */
s.get_t_size = function(){
var size = s.t.node().getBoundingClientRect();
s.w = size.width;
s.h= size.height;
};
return s;
}
As Eric said in the comments, this approach is not exactly user-friendly. But, if you want it, here it is:
First, set the opacity of the circles to 0:
.attr("opacity", 0)
Then, on mousemove:
.on("mouseover", function(d) {
d3.select(this).attr("opacity", 1);
div.transition()//the rest of the code
Don't forget to create a mouse out to make them transparent again:
.on("mouseout", function(d) {
d3.select(this).attr("opacity", 0);

Calculate coordinates of div elements using d3 and d3.tip

I want my tooltip using d3-tip to dynamically be placed next to the text I'm writing out with this code:
in the Javascript
$(".intro h2").html("Nextbus prediction of " + "<font size=5>" + cutoff + "</font>" + "minutes really means:")
.on('mouseover', tip2.show)
.on('mouseout', tip2.hide);
in the HTML...
<div class="intro">
<span class="underline"><h2></h2></span>
</div>
I can make the tooltip appear in an absolute place when I define it like this with the style("left", 300+"px")
var tip2 = d3.tip()
.attr('class', 'd3-tip')
.direction("s")
.html(function(d) {
return "this my text for the hover over of the sentence!"
})
.style("left", 300+"px")
.style("top", 150+"px")
But when I take out the style("left", 300+"px"), the text is placed in the bottom left corner of the graph, no matter how much I try to hack it offset.
I want to replace 300 with something that retrieves to coordinates of the div that I'm hovering over with the mouse.
Here's my fiddle: http://jsfiddle.net/0yfbhtcv/1/ (Nevermind the plot that doesn't appear... that's just lost in translation from my code to jsfiddle and shouldn't be necessary for this problem)
This doesn't use d3.tip, but I think it does what you want with plain ol d3, it makes a div that you can put HTML or text into that pops up where your mouse is:
var div = d3.select("body").append("div") // put the tooltip in a separate div
.attr("class", "tooltip");
then
d3.selectAll(".your_class")
.on("mouseover", function (d) {
div.transition()
.duration(200)
.style("opacity", 0.9);
div.html( d.whatever + <somehtml> + "etc")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px"); })
.on("mouseout", function (d) {
div.transition()
.duration(300)
.style("opacity", 0)
});

Display all stacked area data in popup on mouseover in d3.js

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/

D3.js: Position tooltips using element position, not mouse position?

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");})

Categories