d3-tip doesn't work in a multiline chart - javascript

I am just learning how to use d3-tip in a multiline chart. I took this example for practising: http://bl.ocks.org/d3noob/d8be922a10cb0b148cd5
I've added d3-tip to the code but the tooltip doesn't appear. The chart is displayed correctly, but when I check the console: "Uncaught TypeError: Cannot read property 'value' of undefined" pointing to this code line:
.html(function(d) {
return "<b>" + d.value + "</b>";
})
Here I show you the script:
<script src="d3/d3.min.js" charset="utf-8"></script>
<script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%b %Y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([0, 5])
.direction('n')
.html(function(d) {
return "<b>" + d.value + "</b>";
})
// Define the line
var priceline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); });
// Adds the svg canvas
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 + ")");
svg.call(tip);
// Get the data
d3.csv("stocks.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
// 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 d.value; })]);
// Nest the entries by symbol
var dataNest = d3.nest()
.key(function(d) {return d.name;})
.entries(data);
// Loop through each symbol / key
dataNest.forEach(function(d) {
svg.append("path")
.attr("class", "line")
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.attr("d", priceline(d.values))
});
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
function type(d) {
d.value = +d.value;
return d;
}
</script>
I have changed many things, but I don't find the way. Any idea?
Thanks in advance.

You don't have any data bound to the elements you're calling the tooltip on. You don't need a loop, just use D3's data binding:
svg.selectAll("path")
.data(dataNest)
.enter()
.append("path")
.attr("class", "line")
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.attr("d", function(d) { return priceline(d.values); });
You probably also want to redefine your tooltip function:
.html(function(d) {
return "<b>" + d.key + "</b>";
});

Related

Inserting two separate charts on one page D3.js v4

In Bellow code I am trying to use separate charts (i.e. Bar and line chart) and getting Error: attribute d: Expected number, "MNaN,116LNaN,117L…"
Tried with using different SVG elements for both charts. Give some suggestions if I am missing something.
If I commenting Bar chart code then Line chart works properly and same works with the Bar chart but both at a same time doesn't work.
<div>
<div>Simple Bar Chart</div>
<div class="simpleBar">
<svg width="960" height="500"></svg>
</div>
</div>
<div>
<div>Line Chart</div>
<div id="lineGraph"></div>
</div>
<script>
// --------------- Bar chart code starts -----------------------
var svg = d3.select(".simpleBar > svg"),
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var tooltip = d3.select("body").append("div").attr("class", "toolTip");
var x = d3.scaleBand().rangeRound([0, width]).padding(0.1),
y = d3.scaleLinear().rangeRound([height, 0]);
var colours = d3.scaleOrdinal()
.range(["#6F257F", "#CA0D59"]);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.json("dataSample.json", function(error, data) {
if (error) throw error;
x.domain(data.map(function(d) { return d.Timestamp; }));
y.domain([0, d3.max(data, function(d) { return d.DayTotal; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(5).tickFormat(function(d) { return parseInt(d) + "Rs"; }).tickSizeInner([-width]))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 1)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.attr("fill", "#5D6971")
.text("Average Expence(Rs)");
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("x", function(d) { return x(d.Timestamp); })
.attr("y", function(d) { return y(d.DayTotal); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.DayTotal); })
.attr("fill", function(d) { return colours(d.Timestamp); })
.on("mousemove", function(d){
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html((d.Timestamp) + "<br>" + "Rs" + (d.DayTotal));
})
.on("mouseout", function(d){ tooltip.style("display", "none");});
});
// --------------- Line chart code starts -----------------------
// parse the Timestamp / time
var parseTime = d3.timeParse("%d-%b-%y");
// set the ranges
var xLine = d3.scaleTime().range([0, width]);
var yLine = d3.scaleLinear().range([height, 0]);
// define the line
var valueline = d3.line()
.x(function(d) { return x(d.Timestamp); })
.y(function(d) { return y(d.DayTotal); });
var svgLine = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// append the svg obgect to the body of the page
// appends a 'group' element to 'svg'
// moves the 'group' element to the top left margin
var svgLine = d3.select("#lineGraph").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 + ")");
// Get the data
d3.json("dataSample.json", function(error, data) {
if (error) throw error;
// format the data
data.forEach(function(d) {
d.Timestamp = parseTime(d.Timestamp);
d.DayTotal = +d.DayTotal;
});
// Scale the range of the data
xLine.domain(d3.extent(data, function(d) { return d.Timestamp; }));
yLine.domain([0, d3.max(data, function(d) { return d.DayTotal; })]);
// Add the valueline path.
svgLine.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
// Add the X Axis
svgLine.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xLine).ticks(5));
// Add the Y Axis
svgLine.append("g")
.call(d3.axisLeft(yLine));
});
</script>
and bellow is the Json data format:
[
{
"DayTotal": "418",
"Timestamp": "15-Mar-18"
},{...}
]
You are supposed to use xLine, yLine in your d3 line function whereas you used the ones(x, y) that you meant for bar charts.
var valueline = d3.line()
.x(function(d) { return xLine(d.Timestamp); })
.y(function(d) { return yLine(d.DayTotal); });
Solved JSFiddle

Dynamically Update Multiple Charts in D3

As stated by the title, I'm trying to dynamically update multiple line charts using D3. I have combined these two examples (http://bl.ocks.org/d3noob/6bd13f974d6516f3e491 and http://bl.ocks.org/d3noob/5987480). Only the first chart is updating with the second remaining static.
Here is the code:
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 30, right: 20, bottom: 30, left: 50},
width = 400 - margin.left - margin.right,
height = 220 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%H:%M:%S").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.temperature); });
// Adds the svg canvas
var chart1 = 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 + ")");
// Get the data
d3.csv("output.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.temperature = +d.temperature;
});
// 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 d.temperature; })]);
// Add the valueline path.
chart1.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
chart1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
chart1.append("g")
.attr("class", "y axis")
.call(yAxis);
});
// Adds the svg canvas
var chart2 = 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 + ")");
// Get the data
d3.csv("output2.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.humidity = +d.humidity;
});
// 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 d.humidity; })]);
// Add the valueline path.
chart2.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
chart2.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
chart2.append("g")
.attr("class", "y axis")
.call(yAxis);
});
//2 graph sample code ends here
var TemperatureInterval = setInterval(function() {
updateTemperatureData();
}, 5000);
var HumidityInterval = setInterval(function() {
updateHumidityData();
}, 7000);
function updateTemperatureData() {
// Get the data again
d3.csv("output2.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.temperature = +d.temperature;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.temperature; })]);
// Select the section we want to apply our changes to
var chart1 = d3.select("body").transition();
// Make the changes
chart1.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
chart1.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
chart1.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
});
}
function updateHumidityData() {
// Get the data again
d3.csv("output2.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.humidity = +d.humidity;
});
// Scale the range of the data again
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.humidity; })]);
// Select the section we want to apply our changes to
var chart2 = d3.select("body").transition();
// Make the changes
chart2.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
chart2.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
chart2.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
});
}
</script>
</body>
Thanks.
Here is a possible approach you can try:
// Adds the svg canvas
var chart1 = d3.select("body")
.append("svg")
.attr("class", "chart-1") // Be a little more specific and give a class or identifier to the chart
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var chart2 = d3.select("body")
.append("svg")
.attr("class", "chart-2") // Be a little more specific and give a class or identifier to the chart
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
Add placeholders for the path and axis:
// Chart1 placeholders
chart1.append('path').attr('class', 'line');
chart1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
chart1.append("g")
.attr("class", "y axis");
// Chart2 placeholders
chart2.append('path').attr('class', 'line');
chart2.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")");
chart2.append("g")
.attr("class", "y axis");
Since we don't have a data source we must simulate one:
function fetchData(selector) {
console.log('fetching');
return new Promise(function(resolve, reject) {
// Create new array, each time a different sized one with random values
var dataLength = Math.floor(Math.random() * 40) + 1;
var data = [];
for (var i = 0; i < dataLength; i++) {
data.push({
date: randomDate('2016-01-01', '2016-12-01'),
temperature: Math.floor(Math.random() * 40) + 1
});
}
setTimeout(function() { // Adding timeout to simulate latency
// resolve our promise with the newly created data and the selector of the chart we want to update
resolve({
data: data,
selector: selector
});
}, 4000)
})
}
function randomDate() { // helper function
var startDate = new Date(2012, 0, 1).getTime();
var endDate = new Date(2015, 0, 1).getTime();
var spaces = (endDate - startDate);
var timestamp = Math.round(Math.random() * spaces);
timestamp += startDate;
return new Date(timestamp);
}
Then let us create a new function which will update our charts:
function updateChart(resolved) { // receiving the resolved object from our promise
var data = resolved.data;
var selector = resolved.selector;
data.forEach(function(d) {
d.date = d.date;
d.temperature = +d.temperature;
});
// sort dates so we don't have issue of line screwing up
data.sort(function(a, b) {
return a.date - b.date;
});
// 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 d.temperature;
})]);
// Since both charts have the same behaviour we can do this
var selection = d3.select("body").select(selector).transition();
// Add the valueline path.
selection.select(".line") // change the line
.duration(750)
.attr("d", valueline(data));
selection.select(".x.axis") // change the x axis
.duration(750)
.call(xAxis);
selection.select(".y.axis") // change the y axis
.duration(750)
.call(yAxis);
}
And finally set our update functions:
var HumidityInterval = setInterval(function() {
fetchData('.chart-1').then(updateChart);
}, 7000);
var TemperatureInterval = setInterval(function() {
fetchData('.chart-2').then(updateChart);
}, 7000);
Here is a plnkr with the working code if you have any doubts:
http://plnkr.co/edit/XlyME4tYlhoW4OgRW1dx?p=preview

D3 v4 line chart with JSON dates

I've worked on this for a few days now, and I cant really get my line drawn and got some date formating problems I can't crack.
Using this D3 v3 fiddle as inspiration: http://jsfiddle.net/vmvp0zja/ I tried to convert it to D3 v4, but I can't really get my data drawn properly.
I am trying to draw several lines, but I cant even draw one..
Could you take a look and see what I am missing here? Thanks! :)
// JSON data:
var data = [
{"Date":"\/Date(1475272800000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475359200000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475445600000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475532000000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475618400000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475704800000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475791200000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475877600000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475964000000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476050400000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476136800000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476223200000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476309600000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1476396000000)\/","Open":0,"Closed":0},
{"Date":"\/Date(1475445600000)\/","Open":1,"Closed":0},
{"Date":"\/Date(1475532000000)\/","Open":1,"Closed":0},
{"Date":"\/Date(1475618400000)\/","Open":2,"Closed":0},
{"Date":"\/Date(1475791200000)\/","Open":9,"Closed":0},
{"Date":"\/Date(1475964000000)\/","Open":1,"Closed":0},
{"Date":"\/Date(1475445600000)\/","Open":0,"Closed":1},
{"Date":"\/Date(1475532000000)\/","Open":0,"Closed":1},
{"Date":"\/Date(1475618400000)\/","Open":0,"Closed":1},
{"Date":"\/Date(1475964000000)\/","Open":0,"Closed":1}]
This is my D3 mess:
// linechart.js
var formatTime = d3.timeFormat("%Y-%m-%d");
data.forEach(function (d) {
var unixToISO = new Date(d.Date.match(/\d+/)[0]*1);
d.Date = formatTime(unixToISO);
d.Open = +d.Open;
d.Closed = +d.Closed;
console.log(d.Date);
return d;
});
var margin = {top: 30, right: 40, bottom: 30, left: 50 },
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var x = d3.scaleTime()
.range([0, width]);
var y0 = d3.scaleLinear()
.range([height, 0]);
// Scale the range of the data
x.domain(d3.extent(data, function (d) { return d.Date; }));
y0.domain([
d3.min(data, function (d) { return Math.min(d.Open); }),
d3.max(data, function (d) { return Math.max(d.Open); })]);
var valueline1 = d3.line()
.x(function (d) { console.log(x(d.Date)); return x(d.Date); })
.y(function (d) { return y(d.Open); });
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 + ")");
svg.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
svg.append("g")
.attr("class", "y axis")
.style("fill", "steelblue")
.call(d3.axisLeft(y0));
svg.append("path") // Add the valueline path.
.data(data)
.attr("class", "line")
.attr("d", valueline1);
Help much appreciated in advance. I am probably overlooking something obvious.
Thank you!
Changes:
data.forEach(function(d) {
var unixToISO = new Date(d.Date.match(/\d+/)[0] * 1);
d.Date = unixToISO; // here
d.Open = +d.Open;
// (...)
var valueline1 = d3.line()
.x(function(d) {
console.log(x(d.Date));
return x(d.Date);
})
.y(function(d) {
return y0(d.Open); // here
});
// (...)
svg.append("path")
.data([data]) // here
.attr("class", "line")
.attr("d", valueline1);
and added x-axis labels with
svg.append("text")
.attr("transform", "translate(" + (width / 2) + " ," +
(height + margin.top + 20) + ")")
.style("text-anchor", "middle")
.text("Date");
BTW, your data is unsorted.
JSFiddle Demo
Docs reference

D3 line and chart graphs on the same page breaking

I am trying to render 2 graphs on the same page: chart graph and a linear graph
in html i have two divs which bind to the 2 different graphs respectively like so:
<div id="svg-container">
<div id="svg-container-avg">
The first graph that i import in html is this linear 'average' graph that just doesn't display the path or the x axis.
It works fine if i delete the chart graph which is imported straight after. I would automatically assume that there are some sort of dependencies between the two graphs, but i cannot find anything...
my first import, linear graph: scns-avg.js file contains this
// Set the dimensions of the canvas / graph
var margin = {
top: 30,
right: 20,
bottom: 70,
left: 50
},
width = 800 - margin.left - margin.right,
height = 350 - margin.top - margin.bottom;
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
var valueline = d3.svg.line()
.x(function(d) {
return x(d.scnsID);
})
.y(function(d) {
return y(d.average);
});
var svg2 = d3.select("#svg-container-avg")
.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 + ")");
// Get the data
d3.json("scns-avg-data-retrieval.php", function(error, data) {
data.forEach(function(d) {
d.scnsID = d.scnsID;
d.average = +d.average;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.scnsID;
}));
y.domain([0, d3.max(data, function(d) {
return d.average;
})]);
svg2.append("path") // Add the valueline path.
.style("stroke", "rgba(13, 183, 196, 0.9)")
.attr("d", valueline(data));
svg2.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("class", "dot")
.attr("r", 3.5)
.attr("cx", function(d) {
return x(d.scnsID);
})
.attr("cy", function(d) {
return y(d.average);
})
svg2.append("g") // Add the X Axis
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg2.append("g") // Add the Y Axis
.attr("class", "y axis")
.call(yAxis);
});
My Data structor in JSON:
[{"date":"11-Mar-16","average":"3.18","scnsID":"2"},{"date":"12-Mar-16","average":"3.09","scnsID":"3"},{"date":"15-Mar-16","average":"3.16","scnsID":"4"},{"date":"17-Mar-16","average":"3.20","scnsID":"5"}]
When trying to run the html page with both graphs being imported the scns-avg.js throws up in the console Error: Invalid value for attribute d="M43,2.8124999999999902LNaN,15.46875000000002LNaN,5.6249999999999805L471,0"
which points to valueline(data) in this part of the code
svg2.append("path")
.style("stroke", "rgba(13, 183, 196, 0.9)")
.attr("d", valueline(data));
That is dependent on this piece of code, so the problem must be lying here, but for 6 hours now i can't find the solution to this..
var valueline = d3.svg.line()
.x(function(d) { return x(d.scnsID); })
.y(function(d) { return y(d.average); });
My chart graph that gets imported next and if disabled the linear graph renders properly is as follows below:
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Frequency:</strong> <span style='color:rgba(13, 183, 196, 0.9)'>" + d.average + "</span>";
});
var svg = d3.select("#svg-container").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 + ")");
svg.call(tip);
// Get the data
d3.json("scns-data-retrieval.php", function(error, data) {
if (error) throw error;
data.forEach(function(d) {
d.question = d.question;
d.average = +d.average;
});
x.domain(data.map(function(d) { return d.question; }));
y.domain([0, 5]);
legendSpace = width/5;
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", 0 - margin.left)
.attr("x", 0 - (height / 3))
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Level of need for help:");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.question); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.average); })
.attr("height", function(d) { return height - y(d.average); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
});
function type(d) {
d.average = +d.average;
return d;
};
data structure in JSON:
[{"question":1,"average":3.3333333333333},{"question":2,"average":2.5},{"question":3,"average":4},{"question":4,"average":2.75},{"question":5,"average":2.75},{"question":6,"average":2.75},{"question":7,"average":3},{"question":8,"average":3},{"question":9,"average":2.75},{"question":10,"average":3.25},{"question":11,"average":3.25},{"question":12,"average":3.5},{"question":13,"average":3},{"question":14,"average":3.25},{"question":15,"average":3.5},{"question":16,"average":3.5},{"question":17,"average":3.25},{"question":18,"average":3.75},{"question":19,"average":3.5},{"question":20,"average":3},{"question":21,"average":3},{"question":22,"average":3.5},{"question":23,"average":3.25},{"question":24,"average":3.75},{"question":25,"average":3.75},{"question":26,"average":3.75},{"question":27,"average":3.5},{"question":28,"average":2.75},{"question":29,"average":2.25},{"question":30,"average":3.5},{"question":31,"average":3},{"question":32,"average":3}]
I can't do this anymore and it's driving me crazy so i am now turning to the power of stackoverflow!
After some mad research and trial and error i have fixed this problem.
When you are trying to render more than 1 graph, if they are of different type and not lets say - multiple linear graphs, but something like a linear graph with a bar chart the variable scope is getting mixed up and it's not restricted to the javascript file once it's imported into html.
To fix this problem every graph js file has to wrapped in (function(){ All your graph code goes here })();
This restricts the variable scope to just the function.

Line chart in D3 is all over the place

everyone! Currently trying to generate a line chart using D3 with a simple JSON data. I'm using mbostock's line chart code, I just change data.tsv with my own JSON file:
[
{"date":"Aug-14","close":"1"},
{"date":"Feb-14","close":"2"},
{"date":"Jan-14","close":"1"},
{"date":"Jul-14","close":"3"},
{"date":"Jun-14","close":"1"},
{"date":"Mar-14","close":"3"},
{"date":"May-14","close":"1"},
{"date":"Nov-14","close":"1"},
{"date":"Oct-14","close":"1"},
{"date":"Sep-14","close":"1"}
]
However, I get a graph that looks like this crazy line chart
And I have no idea why it's drawing the lines like that. Any input would be amazing!
Edit: Adding the current code I'm using.
<script>
//Setting up...
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var parseDate = d3.time.format("%d-%b-%y").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");
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 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.json("test.php", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
console.log(d.date);
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.close; }));
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("Price ($)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
});
</script>
Hard to tell without looking directly at your code, but you might not be passing a Number into your scale function. In the source code you copied, see:
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
Or, just pass in data where close is a Number.
[
{"date":"Aug-14","close":1},
{"date":"Feb-14","close":2},
{"date":"Jan-14","close":1},
{"date":"Jul-14","close":3},
{"date":"Jun-14","close":1},
{"date":"Mar-14","close":3},
{"date":"May-14","close":1},
{"date":"Nov-14","close":1},
{"date":"Oct-14","close":1},
{"date":"Sep-14","close":1}
]
Edit:
Note also that the dates in the example you linked are sorted, yours are not.
var parseDate = d3.time.format("%b-%y").parse;
data.sort(function(a, b) {
return d3.ascending(parseDate(a.date), parseDate(b.date));
});

Categories