Why is there a multi-second delay in this d3 code running? - d3.js

I add a group of <span> elements to a <div> and want a popup when I mouseover them.
My code works but there is a 5 (or more) second delay between the DOM being complete and the mouse events being handled. (d3 v3 on Chrome):
var popup=d3.select("body")
.append("div")
.attr("class","popup")
.style("opacity",0)
.style("overflow","auto");
var bucket=d3.select("body")
.append("div").text("DIV")
.attr("id","bucket");
bucket.append("div").attr("class","xxx").text("List: ");
document.addEventListener("DOMContentLoaded",
function(e) {
var ptypes =["Pigs","Geese","Ducks","Elephants"];
var content=[];
for(i=0; i<ptypes.length; i++) {
content.push({"key":ptypes[i],"data":"xxx"});
}
keys=d3.select("body").selectAll(".xxx");
d3.select("body").select(".xxx")
.selectAll("p")
.data(content)
.enter()
.append("span")
.html(function(d) {return " "+d.key+" "; })
.on("mouseover",function(d) {
popup.text(d.data)
.style("opacity",0.9)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY+20) + "px");
})
.on("mouseout",function(d) {
popup.style("opacity",0)
});
});
div.popup {
position: absolute;
text-align: left;
width: 200px; height: 200px; padding: 3px;
background: lightsteelblue;
border: 0px; border-radius: 4px;
}
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
<style>
div.popup {
position: absolute;
text-align: left;
width: 200px; height: 200px; padding: 3px;
background: lightsteelblue;
border: 0px; border-radius: 4px;
}
</style>
</head>
<body>
<script>
var popup=d3.select("body")
.append("div")
.attr("class","popup")
.style("opacity",0)
.style("overflow","auto");
var bucket=d3.select("body")
.append("div").text("DIV")
.attr("id","bucket");
bucket.append("div").attr("class","xxx").text("List: ");
document.addEventListener("DOMContentLoaded",
function(e) {
var ptypes =["Pigs","Geese","Ducks","Elephants"];
var content=[];
for(i=0; i<ptypes.length; i++) {
content.push({"key":ptypes[i],"data":"xxx"});
}
keys=d3.select("body").selectAll(".xxx");
d3.select("body").select(".xxx")
.selectAll("p")
.data(content)
.enter()
.append("span")
.html(function(d) {return " "+d.key+" "; })
.on("mouseover",function(d) {
popup.text(d.data)
.style("opacity",0.9)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY+20) + "px");
})
.on("mouseout",function(d) {
popup.style("opacity",0)
});
});
</script>
</body>
</html>
The code works, but there is a big delay (5 or more seconds) before it gets going and recognises the mouse events.

There's no timing issue, the pointer events appear to be working right away. The issue is that your div covers the elements you want to interact with:
I've set the opacity to 0.4 rather than 0, and we can see the div smothering your interactive elements. If you move the mouse over the "ts" of elephants, I get interaction right away. After the mouse over on the "ts", the div moves out of the way enabling interaction with the rest of the elements:
var popup=d3.select("body")
.append("div")
.attr("class","popup")
.style("opacity",0.4)
.style("overflow","auto");
var bucket=d3.select("body")
.append("div").text("DIV")
.attr("id","bucket");
bucket.append("div").attr("class","xxx").text("List: ");
document.addEventListener("DOMContentLoaded",
function(e) {
var ptypes =["Pigs","Geese","Ducks","Elephants"];
var content=[];
for(i=0; i<ptypes.length; i++) {
content.push({"key":ptypes[i],"data":"xxx"});
}
keys=d3.select("body").selectAll(".xxx");
d3.select("body").select(".xxx")
.selectAll("p")
.data(content)
.enter()
.append("span")
.html(function(d) {return " "+d.key+" "; })
.on("mouseover",function(d) {
popup.text(d.data)
.style("opacity",0.9)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY+20) + "px");
})
.on("mouseout",function(d) {
popup.style("opacity",0.4)
});
});
div.popup {
position: absolute;
text-align: left;
width: 200px; height: 200px; padding: 3px;
background: lightsteelblue;
border: 0px; border-radius: 4px;
}
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>
The solution would then be to set the pointer-events style of the div to none:
var popup=d3.select("body")
.append("div")
.attr("class","popup")
.style("opacity",0.4)
.style("overflow","auto");
var bucket=d3.select("body")
.append("div").text("DIV")
.attr("id","bucket");
bucket.append("div").attr("class","xxx").text("List: ");
document.addEventListener("DOMContentLoaded",
function(e) {
var ptypes =["Pigs","Geese","Ducks","Elephants"];
var content=[];
for(i=0; i<ptypes.length; i++) {
content.push({"key":ptypes[i],"data":"xxx"});
}
keys=d3.select("body").selectAll(".xxx");
d3.select("body").select(".xxx")
.selectAll("p")
.data(content)
.enter()
.append("span")
.html(function(d) {return " "+d.key+" "; })
.on("mouseover",function(d) {
popup.text(d.data)
.style("opacity",0.9)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY+20) + "px");
})
.on("mouseout",function(d) {
popup.style("opacity",0.4)
});
});
div.popup {
position: absolute;
text-align: left;
width: 200px; height: 200px; padding: 3px;
background: lightsteelblue;
border: 0px; border-radius: 4px;
pointer-events:none;
}
<script type="text/javascript" src="https://d3js.org/d3.v5.min.js"></script>

Related

Pie Chart not rendering in dashboard area using D3 v5

I have been trying to render a simple two value pie chart using D3.JS v5 in the lower right corner to no avail. Can someone please assist me with this - the code can be found here:
Codepen
<body>
<div id = "orgChart"></div>
<div id = "mapChart"></div>
<div id = "pieChart"></div>
<script>
/******************************************************************************
Pie Chart
******************************************************************************/
function makePie() {
var widthPie = (window.innerWidth * 0.3) ,
heightPie = (window.innerHeight * 0.3);
var data = [
{name: "Males", count: 43, percent: 61 }
, {name: "Females", count: 27, percent: 39}
];
var pie = d3.pie().value(d=>d.count).padAngle(0.025)(data);
var arcMkr = d3.arc().innerRadius(20).outerRadius(35)
.cornerRadius(4);
var scC = d3.scaleOrdinal(d3.schemePastel1)
.domain(pie.map(d=>d.index));
var g = d3.select("#pieChart")
.append("g").attr("transform", "translate(widthPie/2, heightPie/2)");
g.selectAll("path.x").data(pie).enter().append("path")
.attr("d", arcMkr)
.attr("fill", d=> scC(d.index)).attr("stroke", "grey");
g.selectAll("text.x" ).data( pie ).enter().append( "text")
.text(d => d.data.name +": " + d.data.percent + "%")
.attr("x", d=>arcMkr.innerRadius(20).centroid(d)[0])
.attr("y", d=>arcMkr.innerRadius(20).centroid(d)[1])
.attr("font-family", "sans-serif").attr( "font-size", 8)
.attr("text-anchor", "middle")
;
}
makePie();
</script>
As #Tom Shanley has indicated in the comments, the reason of why your pie chart is not rendered as expected is because you need to create a SVG first.
Notice that I've also changed some CSS properties of #pieChart for improving the snippet visibility, although it is not necessary for your purposes.
<!DOCTYPE html>
<html lang = 'en'>
<head>
<meta charset = 'utf-8'>
<meta name = 'viewport' content = 'width = device-width, initial-scale = 1.0'>
<meta http-equiv = 'X-UA-Compatible' content = 'ie=edge'>
<meta name = 'author' content = "Tom Bellmer">
<meta name = 'date.created' content="03/05/2020">
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v5.min.js"></script>
<title>Org Chart</title>
<style>
body{
background-color: #faf2e4;
font-family: sans-serif;
font-size: 1.2em;
}
text{font-size: .6em}
.node circle {
fill: #fff;
stroke: steelblue;
stroke-width: 1px;
}
.node text {
font: 9px sans-serif;
font-weight: normal;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 1px;
}
#orgChart{
position:absolute;
top: 10px;
left: 10px;
width: 65%;
height: 85%;
}
#mapChart{
position:absolute;
top: 10px;
left: 66%;
width: 34%;
height: 50%;
}
#pieChart{
position:absolute;
top: 51%;
left: 66%;
width: 34%;
height: 55%;
background-color: crimson;
}
circle {
/* fill: #FF8533; */
fill: steelblue;
fill-opacity: .8;
stroke: #fff;
}
circle:hover { fill: red;}
div.tooltip {
position: absolute;
text-align: center;
width: 130px;
height: 14px;
padding: 2px;
font: 11px sans-serif;
background: dodgerblue;
color: white;
border: 0px;
pointer-events: none;
}
svg g{
fill: white;
stroke: black;
}
svg text{fill: black;}
</style>
</head>
<body>
<div id = "orgChart"></div>
<div id = "mapChart"></div>
<div id = "pieChart"></div>
<script>
/******************************************************************************
Pie Chart
******************************************************************************/
function makePie() {
var widthPie = (window.innerWidth * 0.5) ,
heightPie = (window.innerHeight * 0.5);
var data = [
{name: "Males", count: 43, percent: 61 },
{name: "Females", count: 27, percent: 39}
];
var pie = d3.pie().value(d=>d.count).padAngle(0.025)(data);
var arcMkr = d3.arc().innerRadius(20).outerRadius(35)
.cornerRadius(4);
var scC = d3.scaleOrdinal(d3.schemePastel1)
.domain(pie.map(d=>d.index));
// Modified ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
var svg = d3.select("#pieChart")
.append("svg")
.attr("width", widthPie)
.attr("height", heightPie);
var g = svg.append("g").attr("transform", `translate(${widthPie/2}, ${heightPie/2})`);
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
g.selectAll("path.x").data(pie).enter().append("path")
.attr("d", arcMkr)
.attr("fill", d=> scC(d.index)).attr("stroke", "grey");
g.selectAll("text.x" ).data( pie ).enter().append( "text")
.text(d => d.data.name +": " + d.data.percent + "%")
.attr("x", d=>arcMkr.innerRadius(20).centroid(d)[0])
.attr("y", d=>arcMkr.innerRadius(20).centroid(d)[1])
.attr("font-family", "sans-serif").attr( "font-size", 8)
.attr("text-anchor", "middle");
}
makePie();
</script>
</body>

d3.geo.albersUsa() not function

<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>Store Location</title>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="http://d3js.org/queue.v1.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
.boundary {
fill: none;
stroke: #dfdfdf;
stroke-linejoin: round;
}
#map {
text-align: center;
}
circle {
fill: blue;
opacity:.9;
}
text{
font-family: 'PT Sans', sans-serif;
font-weight: 300;
font-size: 12px;
z-index: 900;
}
#tooltip {
position: absolute;
width: 200px;
height: auto;
padding: 10px;
background-color: #ff9436;
-webkit-border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
-webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
-moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
box-shadow: 4px 4px 10px rgba(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" >
<p><strong>Store name and address:</strong></p>
<p><span id="value"></span></p>
<svg id="spendChart"></svg>
</div>
<div id="map"></div>
<script>
var height = 600;
var width = 900, centered;
var projection = d3.geo.albersUsa()
.scale(1200)
.translate([width/2, height/2]);
console.log("working..!!!")
var path = d3.geo.path().projection(projection);
var svg = d3.select("#map")
.append("svg")
.attr("width", width)
.attr("height", height);
var map = svg.append("g")
.attr("class", "boundary");
var usa, usData, spendData, locationData;
var monthParser = d3.time.format("%b");
d3.select("#tooltip").classed("hidden", true);
queue()
.defer(d3.json, 'us.json')
.defer(d3.json, 'newstorelocations.json')
.defer(d3.json, 'newstorespend.json')
.await(ready);
function onHover(location) {
var pos = projection([location.lon, location.lat]),
html = location.name +
"<br />located at: <br />" +
location.AddressLine1 +
",<br />" +
location.City +
"<br />" +
location.StateCode;
d3.select("#value").html(html);
d3.select("#tooltip").attr("left", pos[0] + "px").attr("top", pos[1] + "px").classed("hidden", false);
d3.select(this).style("fill", "red");
var locationSpendData = spendData.filter(function(d) { return d.StoreDescription == location.StoreDescription; });
locationSpendData.forEach(function(d) {
d.month = monthParser.parse(d.MonthName);
});
var chartHeight = 150, chartWidth = 200,
x = d3.time.scale().domain(d3.extent(locationSpendData, function(d) { return d.month; }))
.range([0, chartWidth]),
y = d3.scale.linear().domain([0, d3.max(locationSpendData, function(d) { return d.TotalSpend; })])
.range([chartHeight, 0]);
chart = d3.select("svg#spendChart").attr("height", chartHeight)
.attr("width", chartWidth);
chart.selectAll("rect.bar")
.data(locationSpendData)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.month); })
.attr("y", function(d) { return y(d.TotalSpend); })
.attr("width", chartWidth / locationSpendData.length)
.attr("height", function(d) { return height - y(d.TotalSpend); });
}
function offHover() {
d3.select(this).style("fill", "blue");
d3.select("#spendChart").html("");
d3.select("#tooltip").classed("hidden", true);
}
function ready(error, us, locations, spend) {
usData = us;
locationData = locations;
spendData = spend;
usa = map.selectAll('path')
.data(topojson.feature(usData, usData.objects.states).features);
usa.enter()
.append('path')
.attr('d', path)
.attr('fill', 'gray');
svg.selectAll('circle')
.data(locationData)
.enter()
.append('circle')
.attr('cx', function(d) {return projection([d.lon, d.lat])[0]})
.attr('cy', function(d) {return projection([d.lon, d.lat])[1]})
.attr('r', 4)
.on("mouseover", onHover)
.on("mouseout", offHover);
};
</script>
</body>
</html>
i am creating USA map and location of store on it...
but i getting an error as shown in the image that
i added to us.json and to another .json file for location ...
but no output is getting shown in browser.
i am getting error like this.. .
Uncaught TypeError: Cannot read property 'objects' of undefined

Nested selection with Update

I've built what I think is the most current solution for a nested selection with an update pattern.
However, each time update is clicked, I always get the outer selection, but not always the inner (nested) selection. The log to console show a correctly formed array of arrays.
Is this the correct setup for a nested selection in v5?
Here's a codepen
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>#</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://d3js.org/d3.v5.min.js"></script>
<style type="text/css">
.outer {
border: 1px solid black;
background-color: grey;
width: 100%;
height: 100px;
position: relative;
display: flex;
}
.inner {
display: flex;
justify-content: flex-start;
border: 1px solid blue;
background-color: cyan;
width: 50px;
height: 50px;
position: relative;
}
</style>
</head>
<body>
<button id='update'>update</button>
<div id="anchor"></div>
<script>
const updateButton = document.getElementById('update');
const anchor = d3.select('#anchor');
updateButton.addEventListener('click', function() {
update(
Array.from({
length: Math.ceil(Math.random() * 5)
}, function() {
return Array.from({
length: Math.ceil(Math.random() * 5)
}, function() {
return Math.ceil(Math.random() * 5);
});
})
);
});
function update(data) {
const outer = anchor.selectAll('.outer').data(data);
outer.exit().remove();
outer.enter()
.append('div')
.merge(outer)
.attr("class", "outer");
const inner = outer.selectAll('.inner').data(function(d) {
return d;
});
inner.exit().remove();
inner.enter()
.append('div')
.merge(inner)
.attr("class", "inner")
.text(function(d) {
return d;
});
}
</script>
</body>
</html>
In a case like this one you have to reassign the update selection, so its reference is merged with the enter selection (here I'm changing const for let in order to reassign the selection):
//here is the update selection:
let outer = anchor.selectAll('.outer').data(data);
//here is the enter selection:
const outerEnter = outer.enter()
.append('div')
.attr("class", "outer");
//reassigning here:
outer = outerEnter.merge(outer);
Otherwise other will be only the update selection, even if you have a merge method chained in the enter selection. You can clearly see this if you console outer.size().
Here is your code with that change:
const updateButton = document.getElementById('update');
const anchor = d3.select('#anchor');
updateButton.addEventListener('click', function() {
update(
Array.from({
length: Math.ceil(Math.random() * 5)
}, function() {
return Array.from({
length: Math.ceil(Math.random() * 5)
}, function() {
return Math.ceil(Math.random() * 5);
});
})
);
});
function update(data) {
let outer = anchor.selectAll('.outer').data(data);
outer.exit().remove();
const outerEnter = outer.enter()
.append('div')
.attr("class", "outer");
outer = outerEnter.merge(outer);
const inner = outer.selectAll('.inner').data(function(d) {
return d;
});
inner.exit().remove();
inner.enter()
.append('div')
.attr("class", "inner")
.merge(inner)
.text(function(d) {
return d;
});
}
.outer {
border: 1px solid black;
background-color: grey;
width: 100%;
height: 100px;
position: relative;
display: flex;
}
.inner {
display: flex;
justify-content: flex-start;
border: 1px solid blue;
background-color: cyan;
width: 50px;
height: 50px;
position: relative;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<button id='update'>update</button>
<div id="anchor"></div>

How to display each state name in d3 india map using GeoJSON

In my project, I am trying to display India map using d3 and GeoJSON. It works properly, but I am finding difficulties to display each state name on top of the respective state. How to find the centroid of each state.
Please help me to find out, Thanks in advance...,
In the below image, it is displaying at top left corner.
Index.html
<!DOCTYPE html>
<html>
<meta charset="utf-8">
<style>
.state {
fill: none;
stroke: #a9a9a9;
stroke-width: 1;
}
.state:hover {
fill-opacity: 0.5;
}
#tooltip {
position: absolute;
text-align: center;
padding: 20px;
margin: 10px;
font: 12px sans-serif;
background: lightsteelblue;
border: 1px;
border-radius: 2px;
pointer-events: none;
}
#tooltip h4 {
margin: 0;
font-size: 14px;
}
#tooltip {
background: rgba(0, 0, 0, 0.9);
border: 1px solid grey;
border-radius: 5px;
font-size: 12px;
width: auto;
padding: 4px;
color: white;
opacity: 0;
}
#tooltip table {
table-layout: fixed;
}
#tooltip tr td {
padding: 0;
margin: 0;
}
#tooltip tr td:nth-child(1) {
width: 50px;
}
#tooltip tr td:nth-child(2) {
text-align: center;
}
</style>
<body>
<div id="tooltip"></div>
<!-- div to hold tooltip. -->
<div style="height: 600px;" id="statesvg"></div>
<!-- svg to hold the map. -->
<!-- <script src="indiaState.js"></script> -->
<!-- creates india State. -->
<script src="d3.v3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
function tooltipHtml(n, id, d) { /* function to create html content string in tooltip div. */
return "<h4>" + id + "</h4>" +
"<h4>" + n + "</h4>";
}
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
var sampleData = {}; /* Sample random data. */
["AP", "AR", "AS", "BR", "CT", "DL", "GA", "GJ", "HR", "HP", "JK", "JH", "KA", "KL", "MP", "MH", "MN", "ML", "MZ", "NL", "OR", "PB", "RJ", "SK", "TN", "TR", "UP", "UT", "WB"]
.forEach(function(d) {
var low = Math.round(100 * Math.random());
sampleData[d] = { color: getRandomColor()};
});
/* draw states on id #statesvg */
//iStates.draw("#statesvg", sampleData, tooltipHtml);
d3.select(self.frameElement).style("height", "600px");
d3.json("county.json", function(json) {
console.log(json)
var projection = d3.geo.mercator()
.scale(1)
.translate([0, 0]);
var path = d3.geo.path()
.projection(projection);
function mouseOver(d) {
d3.select("#tooltip").transition().duration(200).style("opacity", .9);
d3.select("#tooltip").html(tooltipHtml(d.n, d.id, sampleData[d.id]))
.style("left", (d3.event.layerX) + "px")
.style("top", (d3.event.layerY) + "px");
}
function mouseOut() {
d3.select("#tooltip").transition().duration(500).style("opacity", 0);
}
function Click(d) {
delete d.d
console.log(d)
}
var svg = d3.select("#statesvg")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.append("g");
svg.selectAll(".state")
.data(json)
.enter()
.append("path")
.attr("class", "state")
.attr("d", function(d) {
return d.d;
})
.style("fill", function(d) {
return sampleData[d.id].color;
})
.on("mousemove", mouseOver).on("mouseout", mouseOut).on("click", Click);
svg.selectAll("text")
.data(json)
.enter()
.append("text")
.attr("fill", "black")
.attr("x", function(d) {
return path.centroid(d.d)[0];
})
.attr("y", function(d) {
return path.centroid(d.d)[1];
})
.attr("text-anchor", "middle")
.attr("dy", ".35em")
.text(function(d) {
return d.id;
});
});
</script>
</body>
</html>
I tried using below code, but its giving both cordinates as NaN, how to solve this...
var projection = d3.geo.mercator()
.scale(1)
.translate([0, 0]);
var path = d3.geo.path()
.projection(projection);
Since your json is returning the actual path d element and not topojson, I'd just use getBBox on the path directly. I also simplified your selections to group the path and the text:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<script data-require="d3#3.5.17" data-semver="3.5.17" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>
<style>
.state {
fill: none;
stroke: #a9a9a9;
stroke-width: 1;
}
.state:hover {
fill-opacity: 0.5;
}
#tooltip {
position: absolute;
text-align: center;
padding: 20px;
margin: 10px;
font: 12px sans-serif;
background: lightsteelblue;
border: 1px;
border-radius: 2px;
pointer-events: none;
}
#tooltip h4 {
margin: 0;
font-size: 14px;
}
#tooltip {
background: rgba(0, 0, 0, 0.9);
border: 1px solid grey;
border-radius: 5px;
font-size: 12px;
width: auto;
padding: 4px;
color: white;
opacity: 0;
}
#tooltip table {
table-layout: fixed;
}
#tooltip tr td {
padding: 0;
margin: 0;
}
#tooltip tr td:nth-child(1) {
width: 50px;
}
#tooltip tr td:nth-child(2) {
text-align: center;
}
</style>
</head>
<body>
<div id="tooltip"></div>
<!-- div to hold tooltip. -->
<div style="height: 600px;" id="statesvg"></div>
<!-- svg to hold the map. -->
<!-- <script src="indiaState.js"></script> -->
<!-- creates india State. -->
<!--<script src="d3.v3.min.js"></script>-->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script>
function tooltipHtml(n, id, d) { /* function to create html content string in tooltip div. */
return "<h4>" + id + "</h4>" +
"<h4>" + n + "</h4>";
}
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
var sampleData = {}; /* Sample random data. */
["AP", "AR", "AS", "BR", "CT", "DL", "GA", "GJ", "HR", "HP", "JK", "JH", "KA", "KL", "MP", "MH", "MN", "ML", "MZ", "NL", "OR", "PB", "RJ", "SK", "TN", "TR", "UP", "UT", "WB"]
.forEach(function(d) {
var low = Math.round(100 * Math.random());
sampleData[d] = { color: getRandomColor()};
});
/* draw states on id #statesvg */
//iStates.draw("#statesvg", sampleData, tooltipHtml);
d3.select(self.frameElement).style("height", "600px");
d3.json("https://api.myjson.com/bins/l36bq", function(json) {
//console.log(json)
var projection = d3.geo.mercator()
.scale(1)
.translate([0, 0]);
var path = d3.geo.path()
.projection(projection);
function mouseOver(d) {
d3.select("#tooltip").transition().duration(200).style("opacity", .9);
d3.select("#tooltip").html(tooltipHtml(d.n, d.id, sampleData[d.id]))
.style("left", (d3.event.layerX) + "px")
.style("top", (d3.event.layerY) + "px");
}
function mouseOut() {
d3.select("#tooltip").transition().duration(500).style("opacity", 0);
}
function Click(d) {
delete d.d
console.log(d)
}
var svg = d3.select("#statesvg")
.append("svg")
.attr("width", "100%")
.attr("height", "100%")
.append("g");
var eS = svg.selectAll(".state")
.data(json)
.enter()
.append("g");
eS.append("path")
.attr("class", "state")
.attr("d", function(d) {
return d.d;
})
.style("fill", function(d) {
return sampleData[d.id].color;
})
.on("mousemove", mouseOver).on("mouseout", mouseOut).on("click", Click)
eS.append("text")
.attr("fill", "black")
.attr("transform", function(d) {
var bbox = this.previousSibling.getBBox();
return "translate(" + (bbox.x + bbox.width/2) + "," + (bbox.y + bbox.height/2) + ")";
})
.attr("text-anchor", "middle")
.attr("dy", ".35em")
.text(function(d) {
return d.id;
});
});
</script>
</body>
</html>

Fix tooltips on horizontal bar chart d3.js

I want to make a similar tooltip like this example but in my chart the tooltips do not show at the end of each bar. I was trying to fix it by adjusting offset([-10, 350]). I can see the tooltips moved but not all of them appear at the end. Anyone can tell me how to fix it? Thanks a lot!
<!DOCTYPE html>
<html>
<style>
.axis {
font-family: Helvetica;
font-size: 1em;
font-weight: bold;
color: #444444;
}
.axis path,
.axis line {
fill: none;
stroke: white;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.bar:hover {
opacity: 0.7;
}
.d3-tip {
line-height: 1;
font-weight: bold;
padding: 12px;
background: #aaaaaa;
color: #aa123f;
border-radius: 6px;
font-family: Helvetica;
}
/* Creates a small triangle extender for the tooltip */
.d3-tip:after {
box-sizing: border-box;
display: inline;
font-size: 10px;
width: 100%;
line-height: 1;
color: #aaaaaa;
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>
<!-- D3.js -->
<script src='http://d3js.org/d3.v3.min.js'></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
</head>
<body>
<div id="barChart"></div>
<script>
var data = [
{y:"Group1", x: 3.5},
{y:"Group2", x: 4.5},
{y:"Group3", x: 3.8}
];
var margin = {top: 40, right: 20, bottom: 30, left: 80},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.
linear().
range([0, width]);
var y = d3.scale.
ordinal().
rangeRoundBands([0, height], 0.3);
var xAxis = d3.
svg.
axis().
scale(x).
orient("bottom");
var yAxis = d3.svg.axis().
scale(y).
orient("left");
x.domain([1, 5]);
y.domain(data.map(function(d) { return d.y; }));
var tipBars = d3.tip().
attr('class', 'd3-tip').
offset([-10, 350]).
html(function(d) {
return '<strong style="color:grey">Result:</strong> <span style="color:grey">' + d.x +
'</span>';
});
var svg = d3.select("#barChart").
append("svg").
attr("width", width + margin.left + margin.right).
attr("height", height + margin.top + margin.bottom).
append("g").
attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var plot = svg.
append("g").
attr('transform', 'translate(' + 0 + ',' + 0 + ')');
var plotBars = plot.selectAll("g").
data(data).
enter().
append("g").
attr('class', 'bars');
svg.call(tipBars);
var bars = plotBars.
append("rect").
attr("x", function(d) { return 0; }).
attr("width",function(d) { return x(d.x);}).
attr("y", function(d) { return y(d.y);}).
attr("height", y.rangeBand()).
attr("class", "bar").
attr("fill","grey").
on('mouseover', tipBars.show).
on('mouseout', tipBars.hide);
svg.append("g").
attr("class", "x axis").
style('font-family', ' Helvetica').
call(xAxis);
svg.append("g").
attr("class", "y axis").
style('font-family', ' Helvetica').
call(yAxis);
</script>
</body>
</html>
Use d3.tip.direction() to set the positioning. Docs
var tipBars = d3.tip()
.attr('class', 'd3-tip')
.direction('e')
.html(function(d) {
return '<strong style="color:grey">Result:</strong> <span style="color:grey">' + d.x +
'</span>';
});

Resources