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");
}
})
Related
I have a search box that highlights all circles when they have the same name and fades out the circles that don't match. All working as expected.
<input class="highlight" name="searchbox" id="searchbox" type="text" list="initname-datalist" placeholder="Search Project/Initiative.." onInput="initiativeSearch(this.value)">
<datalist id="initname-datalist"></datalist>
function initiativeSearch(initSelection) {
circles.transition()
.delay(0)
.duration(500)
.style("opacity", function(d) {
return d.data.initiative_name !== initSelection ? 0.5 : 1;
})
.style("stroke", function(d) {
return d.data.initiative_name === initSelection ? "black" : "grey";
});
}
initiative_name is a column name in my CSV. I want to do a similar thing but using mouseover, so when a user mouseover a circle all other circles with the same name will be highlighted.
I have a current mouseover in place that adds a yellow stroke to the circle being moused over and also a tool tip. I don't necessarily need to retain the yellow stroke.
.on("mouseover", function(d) {
d3.select(this) // highlight the circle that the tooltip relates to
.transition()
.delay(0)
.duration(100)
.style("stroke", "yellow")
.style("stroke-width", 5);
tooltip.transition()
.duration(200)
.style("opacity", .95);
tooltip.html("<strong>" + d.data.initiative_name + "</strong>)
.style("left", d3.select(this).attr("cx") + "px")
.style("top", d3.select(this).attr("cy") + "px");
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.delay(0)
.duration(500)
.style("stroke", "grey")
.style("stroke-width", 1);
tooltip.transition()
.duration(500)
.style("opacity", 0);
});
Any ideas how I can highlight all circles with the same initiative_name using mouseover?
It's hard to write a solution without actually testing it with the data, but this is a possible one:
.on("mouseover", function(d) {
circles.style("opacity", function(e) {
return d.data.initiative_name !== e.data.initiative_name ? 0.5 : 1;
});
//etc...
Call initiativeSearch() on mouseover like this
.on("mouseover", function(d) {
initiativeSearch(d.data.initiative_name);
}
I have a line graph that adds circles on the line for each data point. On hover, I would like to display the "Date" and "Close" of the data point.
Here is the jsfiddle
Here is what I've tried:
.on("mouseover", function(d) {
svg.transition()
.duration(100)
.style("opacity", 1);
svg .html(d.date + "<br/>" + d.close)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
svg.transition()
.duration(100)
.style("opacity", 1);
})
Something is happening with the code but it is very slow and doesnt show the "Date" and "Close" on mouseover.
How can I make the "Date" and "Close" show/hide for each data point circle when hovered over?
I think the best way to do it will be to include d3.tip for your solution.
I use d3.tip.v0.6.3.js.
what you'll have to do first is to create the tool tip view in a var:
var toolTip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<div style='margin-bottom: -12px; color:"+d.data.color+"'>"+d.name+"</div></br><div style='margin-bottom: -5px'>"+ d.data.label+"</div></br>";
});
The 'function(d)' after the '.html' returns html piece for the tooltip, just don't forget the inverted commas.
right after that function you should add:
svg.call(toolTip);
that way your svg will know this tooltip var you created, you can create this var right after you svg.
last part will be to connect this tooltip to 2 events on the d3 elements: mouseover&mouseout:
.on("mouseover", toolTip.show )
.on("mouseout", toolTip.hide);
Hope it will help
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 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)")
I am trying to create a scatter plot and want to show tooltips by clicking on each point. The tooltip will disappear only when the point is deselected (clicked again). Currently, selected points will have a black border with r=8. Deselected points have no visible black border with r=4.5.
With the code below, when I deselect the points, the tooltip won't go away. How can I link the tooltip to each point? Thanks!
.on("click", function (d) {
var clickTooltip = d3.select("#data_visualization").append("div").attr("class", "click_tooltip");
if (d3.select(this).attr("r") < 8) {
d3.select(this)
.style("stroke", "black")
.style("stroke-width", "2px")
.style("stroke-opacity", 1)
.attr("r", 8);
clickTooltip.style("opacity", 0.62);
var clickTooltipText = "display";
clickTooltip.html(clickTooltipText)
.style("left", (d3.event.pageX + 20) + "px")
.style("top", (d3.event.pageY - 40) + "px");
} else {
d3.select(this)
.attr("r", 4.5)
.style("stroke-opacity", 0);
clickTooltip.style("opacity", 0);
}
}
You are appending a new element every time the click handler is called. Instead, create the element once and then select it:
var clickTooltip = d3.select("#data_visualization").append("div").attr("class", "click_tooltip");
.on("click", function (d) {
if (d3.select(this).attr("r") < 8) {
// etc
I figured it out. I am posting my answer here in case anyone is interested. The idea is to add an ID to each tooltip div. Later we can use JQuery to remove by ID.
.on("click", function (d) {
var pointID = "point_" + d3.select(this).attr("cx").replace(".", "_") + "_" + d3.select(this).attr("cy").replace(".", "_");
var clickTooltip = d3.select("#data_visualization")
.append("div")
.attr("id", pointID)
.attr("class", "click_tooltip");
if (d3.select(this).attr("r") < 8) {
d3.select(this)
.style("stroke", "black")
.style("stroke-width", "2px")
.style("stroke-opacity", 1)
.attr("r", 8);
clickTooltip.style("opacity", 0.62);
var clickTooltipText = "display";
clickTooltip.html(clickTooltipText)
.style("left", (d3.event.pageX + 20) + "px")
.style("top", (d3.event.pageY - 40) + "px");
} else {
d3.select(this)
.attr("r", 4.5)
.style("stroke-opacity", 0);
d3.select("#" + pointID).remove();
}
}