D3 mouseover effect for each chart on one page - javascript

I have multiple d3-charts on one page and would like to add a mouseover effect for each chart.
At the moment only one chart is affected and has a mouseover effect.
I've created an example with multiple charts.
Here is the fiddle: http://jsfiddle.net/zumdpjzx/
for( var i= 1; i < 3; i++){
console.log(i);
var arrData = [
["2014-08-20", 100, 100],
["2014-08-21", 95, 85],
["2014-08-22", 93, 71],
["2014-08-23", 88, 57],
["2014-08-24", 86, 42],
["2014-08-25", 98, 28],
["2014-08-26", 117, 14],
["2014-08-27", 123, 0]
];
arrData = arrData.sort((function(index){
return function(a, b){
return (a[index] === b[index] ? 0 : (a[index] < b[index] ? -1 : 1));
};
})(0));
console.log("array: " + arrData);
var margin = {top: 40, right: 40, bottom: 60, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m-%d").parse;
var x = d3.time.scale().range([0, width])
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(arrData.length)
.tickFormat(d3.time.format("%Y-%m-%d"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.close); });
var line2 = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.open); });
var svg = d3.select("#chart" + i).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 data = arrData.map(function(d) {
return {
//date: d[0],
date: parseDate(d[0]),
close: d[2],
open: d[1]
};
});
var length = arrData.length - 1;
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return Math.max(d.close, d.open); })]);
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Open Issues");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
svg.append("path") // Add the valueline2 path.
.attr("class", "line")
.style("stroke", "red")
.attr("d", line2(data))
.text("line2");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[length].open) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "red")
.text("Open");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(data[length].close) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "steelblue")
.text("Close");
//mouse over
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
focus.append("circle")
.attr("r", 4.5);
var bisectDate = d3.bisector(function(d) { return d.date; }).left;
var formatValue = d3.format(",.2f");
var formatCurrency = function(d) { return + d; };
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemoveOpen);
}
function mousemoveOpen() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.date) + "," + y(d.open) + ")");
focus.select("text").text(formatCurrency(d.open));
}
Edit:
I've found now a new solution. Here is the fiddle: http://jsfiddle.net/4h72u83h/1/
Thank you for your help!

You're pretty close to the mark, but you're not keeping track of which focus element you are updating in the mouseout, mouseover and mousemove handlers.
You could do something like this:
for (var i = 1; i < 3; i++) {
console.log(i);
var arrData = [
["2014-08-20", 100, 100],
["2014-08-21", 95, 85],
["2014-08-22", 93, 71],
["2014-08-23", 88, 57],
["2014-08-24", 86, 42],
["2014-08-25", 98, 28],
["2014-08-26", 117, 14],
["2014-08-27", 123, 0]
];
arrData = arrData.sort((function(index) {
return function(a, b) {
return (a[index] === b[index] ? 0 : (a[index] < b[index] ? -1 : 1));
};
})(0));
console.log("array: " + arrData);
var margin = {
top: 40,
right: 40,
bottom: 60,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y-%m-%d").parse;
var x = d3.time.scale()
.range([0, width])
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom").ticks(arrData.length).tickFormat(d3.time.format("%Y-%m-%d"));
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
var line2 = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.open);
});
var svg = d3.select("#chart" + i).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 data = arrData.map(function(d) {
return {
//date: d[0],
date: parseDate(d[0]),
close: d[2],
open: d[1]
};
});
console.log(data);
console.log(arrData.length);
var length = arrData.length - 1;
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return Math.max(d.close, d.open);
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function(d) {
return "rotate(-65)"
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Open Issues");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
svg.append("path") // Add the valueline2 path.
.attr("class", "line")
.style("stroke", "red")
.attr("d", line2(data))
.text("line2");
svg.append("text")
.attr("transform", "translate(" + (width + 3) + "," + y(data[length].open) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "red")
.text("Open");
svg.append("text")
.attr("transform", "translate(" + (width + 3) + "," + y(data[length].close) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "steelblue")
.text("Close");
//mouse over
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
focus.append("circle")
.attr("r", 4.5);
var bisectDate = d3.bisector(function(d) {
return d.date;
}).left;
var formatValue = d3.format(",.2f");
var formatCurrency = function(d) {
return +d;
};
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() {
var thisFocus = d3.select(d3.select(this)[0][0].parentNode).select(".focus");
thisFocus.style("display", null);
})
.on("mouseout", function() {
var thisFocus = d3.select(d3.select(this)[0][0].parentNode).select(".focus");
thisFocus.style("display", "none");
})
.on("mousemove", mousemoveOpen);
}
function mousemoveOpen() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
var thisFocus = d3.select(d3.select(this)[0][0].parentNode).select(".focus");
thisFocus.attr("transform", "translate(" + x(d.date) + "," + y(d.open) + ")");
thisFocus.select("text").text(formatCurrency(d.open));
}
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: none;
stroke: steelblue;
}
.legend {
padding: 5px;
font: 10px sans-serif;
background: yellow;
box-shadow: 2px 2px 1px #888;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
<div id="chart1"></div>
<div id="chart2"></div>
</body>
Basically, what I've done there is to modify the mouseover, mouseout and mousemove, so that it grabs the right focus element to be updated and then updates it.
The important bit is:
var thisFocus = d3.select(d3.select(this)[0][0].parentNode).select(".focus");
thisFocus.attr("transform", "translate(" + x(d.date) + "," + y(d.open) + ")");
thisFocus.select("text").text(formatCurrency(d.open));
The first line grabs the focus that corresponds to the graph that is receiving the mouse events. You'll see similar lines in the mouseover and mouseout handlers.
I would probably recommend that you just keep track of the focus elements separately in an object to begin with, and then you can just use that reference in your mouse handling functions. Selecting it all the time can have performance impacts, though not so much in this case.

mousemoveOpen is called on both charts in your example. Looking at it, 'focus' and 'data' exist outside of the closure. By the time mousemoveOpen gets called both will be fetched from the global scope and use the last value they were set to. That's why the last chart always gets updated: the focus and data variables point reference the last chart.
I tried playing with your fiddle example, but I couldn't get it working. You could use underscore, or native javascript's 'bind'

Related

I had some simple problems in D3.JS

https://i.stack.imgur.com/90isf.png
When visualizing the data, I got some troubles of X axis of line chart.
As picture shows, line of X axis disappeared, in fact, it only shows the scale of X axis. I wonder why this condition happens. Was it due to the illegal coding or because of the size of SVG was not big enough to contain the whole picture?
var margin = { top: 30, right: 120, bottom: 30, left: 50 },
width = 960 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom,
tooltip = { width: 100, height: 100, x: 10, y: -30 };
var parseDate = d3.timeParse("%m-%d-%Y"),
bisectDate = d3.bisector(function(d) { return d.date; }).left,
formatValue = d3.format(","),
dateFormatter = d3.timeParse("%m-%d-%Y");
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
var yAxis = d3.axisLeft()
.scale(y);
var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.likes); });
var svg = d3.select("body").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 + ")");
d3.csv("output4.csv",
function(d){
return { date : d.framenum, likes : d.nSNR }
},
function(data) {
x.domain([0, d3.max(data, function(d) { return +d.date; })]);
y.domain([-d3.max(data, function(d) { return +d.likes; }), d3.max(data, function(d) { return +d.likes; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.style("font-size","17px")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Number of Likes");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 5);
focus.append("rect")
.attr("class", "tooltip")
.attr("width", 100)
.attr("height", 50)
.attr("x", 10)
.attr("y", -22)
.attr("rx", 4)
.attr("ry", 4);
focus.append("text")
.attr("class", "tooltip-date")
.attr("x", 18)
.attr("y", -2);
focus.append("text")
.attr("x", 18)
.attr("y", 18)
.text("Likes:");
focus.append("text")
.attr("class", "tooltip-likes")
.attr("x", 60)
.attr("y", 18);
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() { focus.style("display", null); })
.on("mouseout", function() { focus.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.date) + "," + y(d.likes) + ")");
focus.select(".tooltip-date").text(dateFormatter(d.date));
focus.select(".tooltip-likes").text(formatValue(d.likes));
}
});

Grouping bar and line charts

I am trying to group bar chart and line chart in d3 js and I followed one Link for that purpose,
Here is what my Ajax is returning in response:
[
{
"date_created": "2017-12-27",
"jobs_fail": 19,
"jobs_resub": 31,
"jobs_success": 50
},
{
"date_created": "2017-12-29",
"jobs_fail": 18,
"jobs_resub": 25,
"jobs_success": 44
},
{
"date_created": "2017-12-28",
"jobs_fail": 8,
"jobs_resub": 24,
"jobs_success": 44
},
{
"date_created": "2018-01-02",
"jobs_fail": 2,
"jobs_resub": 0,
"jobs_success": 0
}
]
And what I am trying to show is displaying the jobs_fail and jobs_resub as a bar and jobs_sucess as line chart in same graphs with respect to date_created,
Here is my code for that purpose.
<script>
function get_data() {
console.log("create post is working!") // sanity check
return $.ajax({
url : "/group/guest/query/", // the endpoint
type : "GET", // http method
});
};
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var colorRange = d3.scale.category20();
var color = d3.scale.ordinal()
.range(colorRange.range());
var divTooltip = d3.select("body").append("div").attr("class", "toolTip");
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10, "");
var svg = d3.select("#chart").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 ajdata = get_data();
var k = [];
ajdata.success(function (data) {
var obj = jQuery.parseJSON(data);
alert(data);
var options = d3.keys(obj[0]).filter(function(key) { if (key != "date_created" & key != "jobs_success" ) { return key }}); // & key != "date_created"){return key} });
var line_option = d3.keys(obj[0]).filter(function(key) { if (key == "jobs_success" & key == "date_created"){return key} });
alert(options);
obj.forEach(function(d) {
d.valores = options.map(function(name) {return { name: name, value: +d[name]}; });});
x0.domain(obj.map(function(d) { return d.date_created; }));
x1.domain(options).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(obj, function(d) { return d3.max(d.valores, function(d) { return d.value; }); })]);
var line = d3.svg.line()
.x(function(d) { return x1(d.date_created); })
.y(function(d) { return y(d.jobs_success); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Number of jobs");
var bar = svg.selectAll(".bar")
.data(obj)
.enter().append("g")
.attr("class", "rect")
.attr("transform", function(d) { return "translate(" + x0(d.date_created) + ",0)"; });
bar.selectAll("rect")
.data(function(d) { return d.valores; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("value", function(d){return d.name;})
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
bar
.on("mousemove", function(d){
divTooltip.style("left", d3.event.pageX+10+"px");
divTooltip.style("top", d3.event.pageY-25+"px");
divTooltip.style("display", "inline-block");
var x = d3.event.pageX, y = d3.event.pageY
var elements = document.querySelectorAll(':hover');
l = elements.length
l = l-1
elementData = elements[l].__data__
divTooltip.html((d.date_created)+"<br>"+elementData.name+"<br>"+elementData.value);
});
bar
.on("mouseout", function(d){
divTooltip.style("display", "none");
});
var legend = svg.selectAll(".legend")
.data(options.slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
svg.append("path")
//.data(obj)
.attr("class", "line")
.attr("d", line(obj));
UPDATE
What problem I am facing is I am able to render bar but not the line chart on bars.
I am trying to debug but not able to do so.
Please let me know what might I am doing wrong here.
You have some minor problems and a big problem.
The minor problems are:
Your y scale should take into account the maximum value in your dataset:
y.domain([0, d3.max(obj, function(d) {
return d.jobs_success
})]);
Your line generator should use x0. Besides that, you'll have to move the line by half rangeBand:
.x(function(d) {
return x0(d.date_created) + x0.rangeBand() / 2;
})
By default, a <path> has a black fill. Change it:
.style("fill", "none")
Those, however, are minor problems. The biggest problem lies here, in the data() method:
svg.append("path")
.data(obj)
.attr("class", "line")
.attr("d", line);
Let's see in detail what's happening here. You're passing the obj array to the data(). However, if you do this, each element of that array will be passed, individually, to the line generator.
So, supposing that this is your array...
["foo", "bar", "baz"]
...what you're passing to the line generator is just:
"foo".
You have some different solutions here. First, you can pass the array to the line generator directly, as you did in your edit. Second, you can wrap the array in an outer array:
svg.append("path")
.data([obj])
.attr("class", "line")
.attr("d", line);
That way, the whole obj array will be passed to the line generator.
Or, third, you can use datum:
svg.append("path")
.datum(obj)
.attr("class", "line")
.attr("d", line);
Here is your code with those changes and using datum to draw the path:
var obj = [{
"date_created": "2017-12-27",
"jobs_fail": 19,
"jobs_resub": 31,
"jobs_success": 50
}, {
"date_created": "2017-12-29",
"jobs_fail": 18,
"jobs_resub": 25,
"jobs_success": 44
}, {
"date_created": "2017-12-28",
"jobs_fail": 8,
"jobs_resub": 24,
"jobs_success": 44
}, {
"date_created": "2018-01-02",
"jobs_fail": 2,
"jobs_resub": 0,
"jobs_success": 0
}];
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var colorRange = d3.scale.category20();
var color = d3.scale.ordinal()
.range(colorRange.range());
var divTooltip = d3.select("body").append("div").attr("class", "toolTip");
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(10, "");
var svg = d3.select("body").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 options = d3.keys(obj[0]).filter(function(key) {
if (key != "date_created" & key != "jobs_success") {
return key
}
}); // & key != "date_created"){return key} });
var line_option = d3.keys(obj[0]).filter(function(key) {
if (key == "jobs_success" & key == "date_created") {
return key
}
});
obj.forEach(function(d) {
d.valores = options.map(function(name) {
return {
name: name,
value: +d[name]
};
});
});
x0.domain(obj.map(function(d) {
return d.date_created;
}));
x1.domain(options).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(obj, function(d) {
return d.jobs_success
})]);
var line = d3.svg.line()
.x(function(d) {
return x0(d.date_created) + x0.rangeBand() / 2;
})
.y(function(d) {
return y(d.jobs_success);
});
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Number of jobs");
var bar = svg.selectAll(".bar")
.data(obj)
.enter().append("g")
.attr("class", "rect")
.attr("transform", function(d) {
return "translate(" + x0(d.date_created) + ",0)";
});
bar.selectAll("rect")
.data(function(d) {
return d.valores;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) {
return x1(d.name);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("value", function(d) {
return d.name;
})
.attr("height", function(d) {
return height - y(d.value);
})
.style("fill", function(d) {
return color(d.name);
});
bar
.on("mousemove", function(d) {
divTooltip.style("left", d3.event.pageX + 10 + "px");
divTooltip.style("top", d3.event.pageY - 25 + "px");
divTooltip.style("display", "inline-block");
var x = d3.event.pageX,
y = d3.event.pageY
var elements = document.querySelectorAll(':hover');
l = elements.length
l = l - 1
elementData = elements[l].__data__
divTooltip.html((d.date_created) + "<br>" + elementData.name + "<br>" + elementData.value);
});
bar
.on("mouseout", function(d) {
divTooltip.style("display", "none");
});
var legend = svg.selectAll(".legend")
.data(options.slice())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
svg.append("path")
.datum(obj)
.attr("class", "line")
.attr("d", line)
.style("fill", "none")
.style("stroke", "red")
.style("stroke-width", 2);
.axis line,
.axis path {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
Ok a couple of things. Your line's x function should be relying on x0 not x1:
var line = d3.svg.line()
.x(function(d) { return x0(d.date_created); })
.y(function(d) { return y(d.jobs_success); });
And your path needs to be called like this:
svg.append("path")
.attr("class", "line")
.attr("d", line(obj));
That should get you most of the way there - you might want to tweak the maximum y-value, and shift the x co-ordinate of the line by x0.rangeBand()/2 as well to make it line up properly with the centre of the bars.

D3.js Mouseover and Focus + Context issue

I am new to D3.js
I've gone through some tutorials and have straight up jumped into my first project. I was hoping to combine the following with slight tweaks according to my needs. Currently I am having two issues
Focus+Context via Brushing
and
X-Value Mouseover
The Mouseover is wrongly displayed. It renders to the left of the chart. Could be a very small issue but I cant seem to find it.
I cant seem to figure out a way to display the "Safe Value" text outside the chart right next to the line. EDIT 2 - I've figured this out
Any help would be much appreciated.
Here is the CSS
body {
font: 10px sans-serif;
}
svg {
font: 10px sans-serif;
}
.line {
fill: none;
stroke: steelBlue;
stroke-width: 1.5px;
/*clip-path: url(#clip);*/
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.brush .extent {
stroke: #fff;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.overlay {
fill: none;
pointer-events: all;
}
.xy circle {
fill: steelblue;
stroke: black;
}
JS
var margin = {top: 10, right: 15, bottom: 100, left: 60},
margin2 = {top: 430, right: 15, bottom: 20, left: 60},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%Y-%m-%dT%H:%M:%SZ").parse,
bisectDate = d3.bisector(function(d) { return d.date; }).left,
formatValue = d3.format(",.2f"),
formatData = function(d) { return formatValue(d) + " %"; };
var x = d3.time.scale().range([0, width]),
x2 = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
y2 = d3.scale.linear().range([height2, 0]);
var xAxis = d3.svg.axis().scale(x).orient("bottom").ticks(d3.time.months, 1).tickFormat(d3.time.format("%m/%y")),
xAxis2 = d3.svg.axis().scale(x2).orient("bottom").ticks(d3.time.months, 1).tickFormat(d3.time.format("%m/%y")),
yAxis = d3.svg.axis().scale(y).orient("left");
var brush = d3.svg.brush()
.x(x2)
.on("brush", brushed);
var line = d3.svg.line()
.interpolate("monotone")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });
var line2 = d3.svg.line()
.interpolate("monotone")
.x(function(d) { return x2(d.date); })
.y(function(d) { return y2(d.value); });
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
d3.csv("data.csv", function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
data.sort(function(a, b) {
return a.date - b.date;
});
x.domain(d3.extent(data.map(function(d) { return d.date; })));
y.domain([0, d3.max(data.map(function(d) { return d.value; }))]);
x2.domain(x.domain());
y2.domain(y.domain());
focus.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
focus.append("line")
.attr("x1",x(data[0].date))
.attr("y1",y(83))
.attr("x2",x(data[data.length - 1].date))
.attr("y2",y(83))
.attr("stroke","orangered");
svg.append("text")
.attr("transform", "translate(" + (width+3) + "," + y(83) + ")")
.attr("dy", ".35em")
.attr("text-anchor", "start")
.style("fill", "orangered")
.text(function(d) { return "Safe Value = 83" });
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("x", 0)
.attr("y", 0)
.style("text-anchor", "middle")
.attr("transform", "translate(-50,"+ height/2 + ") rotate(-90)")
.text("Dissolved Oxygen (%)");
context.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line2);
context.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "x brush")
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", height2 + 7);
var xy = svg.append("g")
.attr("class", "xy")
.style("display", "none");
xy.append("circle")
.attr("r", 4.5);
xy.append("text")
.attr("x", 9)
.attr("dy", ".35em");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.on("mouseover", function() { xy.style("display", null); })
.on("mouseout", function() { xy.style("display", "none"); })
.on("mousemove", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
console.log(x0);
xy.attr("transform", "translate(" + x(d.date) + "," + y(d.value) + ")");
xy.select("text").text(formatData(d.value));
}
});
function brushed() {
x.domain(brush.empty() ? x2.domain() : brush.extent());
focus.select(".line").attr("d", line);
focus.select(".x.axis").call(xAxis);
}
Plunker Code
(Please refer to the code at Plunker, since I have updated a few things over there.) Thanks
Image1
Image2
For your problem #2, the code for the text is placing it out of the visible area. Just adjust your arguments to translate to something like the following:
.attr("transform", "translate(" + (width - 35) + ",30" + ")")
or something else that you prefer - note the minus on the x.

How to add tool-tips in d3 js vertically grouped bar chart

I was creating an animated grouped bar chart. I was able to create a simple vertical grouped bar chart by the code provided below. Now I like to add some tool-tips which will show some specific data about the bar. For example, "Letter:A,Frequency:0.05". Can anyone help me out in this case.Thanks in advance :)
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
//console.log(margin.left);
var svg = d3.select("body").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 data = [
{letter: "A", frequency: .08167,depth:.32},
{letter: "B", frequency: .01492,depth:.69}
];
var groupNames=d3.keys(data[0]).filter(function(key){return key!="letter";})
data.forEach(function(d){
d.groups=groupNames.map(function(name){return {name:name,value:+d[name]};})
});
x0.domain(data.map(function(d){ alert(d.letter);return d.letter;}));
x1.domain(groupNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0,d3.max(data,function(d){
return d3.max(d.groups,function(d){
return d.value;
});
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Letter Fun");
var letter = svg.selectAll(".letter")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.letter) + ",0)"; });
letter.selectAll("rect")
.data(function(d) { return d.groups; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) {alert(d.name); return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
letter.selectAll("text")
.data(function(d) { return d.groups; })
.enter().append("text")
.attr("class","barstext")
.attr("x", function(d) { return x1(d.name); })
.attr("y",function(d) { return y(d.value); })
.text(function(d){return d.value;})
var legend = svg.selectAll(".legend")
.data(groupNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
<body></body>
Step 1: Add letter information to the groups data.
data.forEach(function(d) {
d.groups = groupNames.map(function(name) {
return {
name: name,
letter: d.letter,
value: +d[name]
};
})
});
Step 2: Append title to bars.
.append("title")
.text(function(d,i){
var name = d.name.replace(/^./,d.name[0].toUpperCase());
return "Letter : "+d.letter+" \n"+name+" : "+d.value;
});
var margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
//console.log(margin.left);
var svg = d3.select("body").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 data = [{
letter: "A",
frequency: .08167,
depth: .32
}, {
letter: "B",
frequency: .01492,
depth: .69
}];
var groupNames = d3.keys(data[0]).filter(function(key) {
return key != "letter";
})
data.forEach(function(d) {
d.groups = groupNames.map(function(name) {
return {
name: name,
letter: d.letter,
value: +d[name]
};
})
});
x0.domain(data.map(function(d) {
return d.letter;
}));
x1.domain(groupNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) {
return d3.max(d.groups, function(d) {
return d.value;
});
})]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Letter Fun");
var letter = svg.selectAll(".letter")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) {
return "translate(" + x0(d.letter) + ",0)";
});
letter.selectAll("rect")
.data(function(d) {
return d.groups;
})
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) {
return x1(d.name);
})
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
})
.style("fill", function(d) {
return color(d.name);
})
.append("title")
.text(function(d,i){
var name = d.name.replace(/^./,d.name[0].toUpperCase());
return "Letter : "+d.letter+" \n"+name+" : "+d.value;
});
letter.selectAll("text")
.data(function(d) {
return d.groups;
})
.enter().append("text")
.attr("class", "barstext")
.attr("x", function(d) {
return x1(d.name);
})
.attr("y", function(d) {
return y(d.value);
})
.text(function(d) {
return d.value;
})
var legend = svg.selectAll(".legend")
.data(groupNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) {
return d;
});
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body></body>

D3 chart not showing up

Problem
Having trouble getting my chart in D3 to show up. Getting an Uncaught SyntaxError: Unexpected end of input with my scripts.js
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
<script src="assets/js/scripts.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
</body>
</html>
scripts.js
$(function() {
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("body").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 + ")");
d3.csv("assets/data/data.csv", function(error, data) {
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.ages, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
style.css
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: blue;
}
.x.axis path {
display: none;
}
I found that I got the same error when pasting your code directly into JSFiddle. It went away when I properly closed the main function. For reference, here's your code in live snippet form with the extra closing braces:
var csvData = [
{"State":0, "Age":31, "Names":["Austin"]},
{"State":0, "Age":31, "Names":["Austin"]},
{"State":0, "Age":31, "Names":["Austin"]},
];
$(function() {
$("#placeholder").remove();
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x1 = d3.scale.ordinal();
var y = d3.scale.linear()
.range([height, 0]);
var color = d3.scale.ordinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("body").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 data = csvData;
var ageNames = d3.keys(data[0]).filter(function(key) { return key !== "State"; });
data.forEach(function(d) {
d.ages = ageNames.map(function(name) { return {name: name, value: +d[name]}; });
});
x0.domain(data.map(function(d) { return d.State; }));
x1.domain(ageNames).rangeRoundBands([0, x0.rangeBand()]);
y.domain([0, d3.max(data, function(d) { return d3.max(d.ages, function(d) { return d.value; }); })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Population");
var state = svg.selectAll(".state")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(d.State) + ",0)"; });
state.selectAll("rect")
.data(function(d) { return d.ages; })
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return color(d.name); });
var legend = svg.selectAll(".legend")
.data(ageNames.slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", width - 18)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d) { return d; });
});
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: blue;
}
.x.axis path {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
On the d3.csv("csv...") call - try adding another }); to the end of it.
I tried your code out on JSFiddle, and that seemed to resolve the syntax error.
EDIT: My bad. Left in a hurry. Here's the fiddle:
http://jsfiddle.net/strsjpxa/
I later noticed it made more sense to close the function at the end.

Categories

Resources