I am trying to recreate a map using the following code in d3.js. It is an interactive map. When I hover over the circles, it should show a tooltip. But the problem is that my tooltip is not moving with the mouse. It is stuck at the bottom of the canvas. I have tried changing the style. I don't know what is wrong with it. Can someone please help me resolve this issue? I am using d3 version 4.
<!DOCTYPE html>
<meta charset="utf-8">
<!-- Load d3.js and the geo projection plugin -->
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<!-- Create an element where the map will take place -->
<div id="my_dataviz"></div>
<style>
.circle:hover{
stroke: black;
stroke-width: 4px;
}
</style>
<script>
// Size ?
var width = 460
var height = 400
// The svg
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
// Map and projection
var projection = d3.geoMercator()
.center([4, 47]) // GPS of location to zoom on
.scale(1020) // This is like the zoom
.translate([ width/2, height/2 ])
// Create data for circles:
var markers = [
{long: 9.083, lat: 42.149, name: "Corsica"}, // corsica
{long: 7.26, lat: 43.71, name: "Nice"}, // nice
{long: 2.349, lat: 48.864, name: "Paris"}, // Paris
{long: -1.397, lat: 43.664, name: "Hossegor"}, // Hossegor
{long: 3.075, lat: 50.640, name: "Lille"}, // Lille
{long: -3.83, lat: 58, name: "Morlaix"}, // Morlaix
];
// Load external data and boot
d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson", function(data){
// Filter data
data.features = data.features.filter( function(d){return d.properties.name=="France"} )
// Draw the map
svg.append("g")
.selectAll("path")
.data(data.features)
.enter()
.append("path")
.attr("fill", "#b8b8b8")
.attr("d", d3.geoPath()
.projection(projection)
)
.style("stroke", "black")
.style("opacity", .3)
// create a tooltip
var Tooltip = d3.select("#my_dataviz")
.append("div")
.attr("class", "tooltip")
.style("opacity", 1)
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px")
// Three function that change the tooltip when user hover / move / leave a cell
var mouseover = function(d) {
Tooltip.style("opacity", 1)
}
var mousemove = function(d) {
Tooltip
.html(d.name "<br>" "long: " d.long "<br>" "lat: " d.lat)
.style("left", (d3.mouse(this)[0] 10) "px")
.style("top", (d3.mouse(this)[1]) "px")
}
var mouseleave = function(d) {
Tooltip.style("opacity", 0)
}
// Add circles:
svg
.selectAll("myCircles")
.data(markers)
.enter()
.append("circle")
.attr("cx", function(d){ return projection([d.long, d.lat])[0] })
.attr("cy", function(d){ return projection([d.long, d.lat])[1] })
.attr("r", 14)
.attr("class", "circle")
.style("fill", "69b3a2")
.attr("stroke", "#69b3a2")
.attr("stroke-width", 3)
.attr("fill-opacity", .4)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave)
})
</script>
CodePudding user response:
Your x,y coordinates for the tooltip were good, you were just missing position: absolute;
for their styling. I chose to add it to the <style>
element you had in the snippet below, but you could have done it in JS with .style()
too.
I also chose to add position: relative;
to #my_dataviz
to keep the absolute positioning of the tooltips relative to that container.
<!-- Load d3.js and the geo projection plugin -->
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<!-- Create an element where the map will take place -->
<div id="my_dataviz"></div>
<style>
.circle:hover{
stroke: black;
stroke-width: 4px;
}
#my_dataviz {
position: relative;
}
.tooltip {
position: absolute;
}
</style>
<script>
// Size ?
var width = 460
var height = 400
// The svg
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
// Map and projection
var projection = d3.geoMercator()
.center([4, 47]) // GPS of location to zoom on
.scale(1020) // This is like the zoom
.translate([ width/2, height/2 ])
// Create data for circles:
var markers = [
{long: 9.083, lat: 42.149, name: "Corsica"}, // corsica
{long: 7.26, lat: 43.71, name: "Nice"}, // nice
{long: 2.349, lat: 48.864, name: "Paris"}, // Paris
{long: -1.397, lat: 43.664, name: "Hossegor"}, // Hossegor
{long: 3.075, lat: 50.640, name: "Lille"}, // Lille
{long: -3.83, lat: 58, name: "Morlaix"}, // Morlaix
];
// Load external data and boot
d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson", function(data){
// Filter data
data.features = data.features.filter( function(d){return d.properties.name=="France"} )
// Draw the map
svg.append("g")
.selectAll("path")
.data(data.features)
.enter()
.append("path")
.attr("fill", "#b8b8b8")
.attr("d", d3.geoPath()
.projection(projection)
)
.style("stroke", "black")
.style("opacity", .3)
// create a tooltip
var Tooltip = d3.select("#my_dataviz")
.append("div")
.attr("class", "tooltip")
.style("opacity", 1)
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px")
// Three function that change the tooltip when user hover / move / leave a cell
var mouseover = function(d) {
Tooltip.style("opacity", 1)
}
var mousemove = function(d) {
Tooltip
.html(d.name "<br>" "long: " d.long "<br>" "lat: " d.lat)
.style("left", (d3.mouse(this)[0] 10) "px")
.style("top", (d3.mouse(this)[1]) "px")
}
var mouseleave = function(d) {
Tooltip.style("opacity", 0)
}
// Add circles:
svg
.selectAll("myCircles")
.data(markers)
.enter()
.append("circle")
.attr("cx", function(d){ return projection([d.long, d.lat])[0] })
.attr("cy", function(d){ return projection([d.long, d.lat])[1] })
.attr("r", 14)
.attr("class", "circle")
.style("fill", "69b3a2")
.attr("stroke", "#69b3a2")
.attr("stroke-width", 3)
.attr("fill-opacity", .4)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave)
})
</script>
Here is another snippet that hides the tooltip using display: none;
instead of changing opacity
. This fixes a bug where one of the circles (for example, Corsica) may no longer receive mouse events because the tooltip is sitting on top of it despite being invisible.
<!-- Load d3.js and the geo projection plugin -->
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<!-- Create an element where the map will take place -->
<div id="my_dataviz"></div>
<style>
.circle:hover{
stroke: black;
stroke-width: 4px;
}
#my_dataviz {
position: relative;
}
.tooltip {
position: absolute;
display: none;
}
</style>
<script>
// Size ?
var width = 460
var height = 400
// The svg
var svg = d3.select("#my_dataviz")
.append("svg")
.attr("width", width)
.attr("height", height)
// Map and projection
var projection = d3.geoMercator()
.center([4, 47]) // GPS of location to zoom on
.scale(1020) // This is like the zoom
.translate([ width/2, height/2 ])
// Create data for circles:
var markers = [
{long: 9.083, lat: 42.149, name: "Corsica"}, // corsica
{long: 7.26, lat: 43.71, name: "Nice"}, // nice
{long: 2.349, lat: 48.864, name: "Paris"}, // Paris
{long: -1.397, lat: 43.664, name: "Hossegor"}, // Hossegor
{long: 3.075, lat: 50.640, name: "Lille"}, // Lille
{long: -3.83, lat: 58, name: "Morlaix"}, // Morlaix
];
// Load external data and boot
d3.json("https://raw.githubusercontent.com/holtzy/D3-graph-gallery/master/DATA/world.geojson", function(data){
// Filter data
data.features = data.features.filter( function(d){return d.properties.name=="France"} )
// Draw the map
svg.append("g")
.selectAll("path")
.data(data.features)
.enter()
.append("path")
.attr("fill", "#b8b8b8")
.attr("d", d3.geoPath()
.projection(projection)
)
.style("stroke", "black")
.style("opacity", .3)
// create a tooltip
var Tooltip = d3.select("#my_dataviz")
.append("div")
.attr("class", "tooltip")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "2px")
.style("border-radius", "5px")
.style("padding", "5px")
// Three function that change the tooltip when user hover / move / leave a cell
var mouseover = function(d) {
Tooltip.style("display", "block")
}
var mousemove = function(d) {
Tooltip
.html(d.name "<br>" "long: " d.long "<br>" "lat: " d.lat)
.style("left", (d3.mouse(this)[0] 10) "px")
.style("top", (d3.mouse(this)[1]) "px")
}
var mouseleave = function(d) {
Tooltip.style("display", "none")
}
// Add circles:
svg
.selectAll("myCircles")
.data(markers)
.enter()
.append("circle")
.attr("cx", function(d){ return projection([d.long, d.lat])[0] })
.attr("cy", function(d){ return projection([d.long, d.lat])[1] })
.attr("r", 14)
.attr("class", "circle")
.style("fill", "69b3a2")
.attr("stroke", "#69b3a2")
.attr("stroke-width", 3)
.attr("fill-opacity", .4)
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseleave", mouseleave)
})
</script>