I have created a stacked bar chart which depicts scanned and unscanned items as bars and also added the tooltip to represent the values of each stack when mouse is moved over. However when i move on a stack i would like to show tooltip like "UnScanned - 57 items" and when i move the mouse over the lower bar it should display "Scanned - 50" . I just need to differentiate the bar whether it is scanned or unscanned along with the data values displayed.
My code goes like:
<!DOCTYPE html>
<html>
<head>
<title>Scanned vs Non Scanned Data</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="../../js/Core_functions.js"></script>
<script type="text/javascript" src="../../js/graphic_functions.js"></script>
<style type="text/css">
svg {
width: 960px;
height: 500px;
border: solid 1px #ccc;
font: 10px sans-serif;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="container">
<h1>Mockup of Component Scanned Mapping DV</h1>
</div>
<script type="text/javascript">
var w = 960,
h = 500,
p = [20, 50, 30, 20],
x = d3.scale.ordinal().rangeRoundBands([0, w - p[1] - p[3]]),
y = d3.scale.linear().range([0, h - p[0] - p[2]]),
z = d3.scale.ordinal().range(["#819FF7", "#CB491A"]),
parse = d3.time.format("%m/%Y").parse,
format = d3.time.format("%b-%y");
/*var yAxis = d3.svg.axis()
.scale(y)
.ticks(12)
.orient("left");*/
var svg = d3.select("#container").append("svg:svg")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + p[3] + "," + (h - p[2]) + ")");
d3.csv("scandata.csv", function(scan) {
// Transpose the data into layers by cause.
var scantypes = d3.layout.stack()(["scanned", "unscanned"].map(function(scans) {
return scan.map(function(d) {
return {x: parse(d.date), y: +d[scans]};
});
}));
// Compute the x-domain (by date) and y-domain (by top).
x.domain(scantypes [0].map(function(d) { return d.x; }));
y.domain([0, d3.max(scantypes[scantypes .length - 1], function(d) { return d.y0 + d.y; })]);
// Add a group for each column.
var cause = svg.selectAll("g.scan")
.data(scantypes)
.enter().append("svg:g")
.attr("class", "scan")
.style("fill", function(d, i) { return z(i); })
.style("stroke", function(d, i) { return d3.rgb(z(i)).darker(); });
// Add a rect for each date.
var rect = cause.selectAll("rect")
.data(Object)
.enter().append("svg:rect")
.attr("x", function(d,i) {
if (i ==0)
{
return x(d.x)+ 10 ;
}
else
{
return x(d.x);
}} )
.attr("y", function(d) { return -y(d.y0) - y(d.y); })
.attr("height", function(d) { return y(d.y); })
.attr("width", x.rangeBand()/2)
.on("mouseover", function(d){
return tooltip.style("visibility", "visible")
.text((d.y))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 20) + "px"); ;})
.on("mousemove", function(d){
return tooltip.style("visibility", "visible")
.text((d.y))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 20) + "px"); ;})
.on("mouseout", function(d){return tooltip.style("visibility", "hidden");})
.on("click", function(d){console.log(d);});
var tooltip = d3.select("#container")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "visible")
.text("Scanned vs UnScanned")
.style("font", "Arial")
.style("font-size", "14px");
// Add a label per date.
var label = svg.selectAll("text")
.data(x.domain())
.enter().append("svg:text")
.attr("x", function(d) { return x(d) + x.rangeBand() / 3; })
.attr("y", 6)
.attr("text-anchor", "middle")
.attr("dy", ".71em")
.text(format);
// Add y-axis rules.
var rule = svg.selectAll("g.rule")
.data(y.ticks(5))
.enter().append("svg:g")
.attr("class", "rule")
.attr("transform", function(d) { return "translate(0," + -y(d) + ")"; });
rule.append("svg:line")
.attr("x2", w - p[1] - p[3])
.style("stroke", function(d) { return d ? "#fff" : "#000"; })
.style("stroke-opacity", function(d) { return d ? .7 : null; });
rule.append("svg:text")
.attr("x", -15)
.style("font-family","Arial 12px")
.attr("dy", ".25em")
.text(d3.format(",d"));
});
</script>
</body>
</html> </script>
</body>
</html>
My csv data :
date,scanned,unscanned
01/2014,10,90
02/2014,55,40
03/2014,45,23
04/2014,65,35
05/2014,100,20
06/2014,50,30
07/2014,10,90
08/2014,22,48
09/2014,0,100
10/2014,3,97
11/2014,22,60
12/2014,57,37
You could make this part of the data that you make for the chart:
var scantypes = d3.layout.stack()(["scanned", "unscanned"].map(function(scans) {
return scan.map(function(d) {
return {x: parse(d.date), y: +d[scans], type: scans};
});
}));
// more code...
.on("mouseover", function(d){
return tooltip.text(d.type + " - " + d.y);
});
Related
This question already has answers here:
D3 Appending Text to a SVG Rectangle
(2 answers)
SVG: text inside rect
(5 answers)
Closed 1 year ago.
After study this excelent d3.js tutorial, I like to add the bar value on top of each bar! below example code the text not show up!
XYZ.csv
year,value
2011,45
2012,47
2013,52
2014,70
2015,75
2016,78
html file:
<!doctype html>
<html>
<head>
<style>
.bar {
fill: steelblue;
}
</style>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<svg width="600" height="500"></svg>
<script>
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin
var xScale = d3.scaleBand().range([0, width]).padding(0.4),
yScale = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
d3.csv("XYZ.csv", function(error, data) {
if (error) {
throw error;
}
xScale.domain(data.map(function(d) { return d.year; }));
yScale.domain([0, d3.max(data, function(d) { return d.value; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(function(d){
return "$" + d;
}).ticks(10));
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); })
.append("text")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("text", function(d) { return d.value; })
});
</script>
</body>
</html>
Below code is added for text on top of each bar:
.append("text")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("text", function(d) { return d.value; })
It's impossible to append a <text> to a <rect>. Just add the texts separately:
g.selectAll(".bar-title")
.data(data)
.enter()
.append("text")
.classed('bar-title', true)
.attr('text-anchor', 'middle')
.attr("x", d => xScale(d.year) + xScale.bandwidth()/2)
.attr("y", d => yScale(d.value))
.text(d => `$${d.value}`);
... or, you can append a <g> element on enter() and then append <text> and <rect> under <g>.
const data = [
{year: 2011, value: 45},
{year: 2012, value: 47},
{year: 2013, value: 52},
{year: 2014, value: 70},
{year: 2015, value: 75},
{year: 2016, value: 78}
];
var svg = d3.select("svg"),
margin = 200,
width = svg.attr("width") - margin,
height = svg.attr("height") - margin
var xScale = d3.scaleBand().range([0, width]).padding(0.4),
yScale = d3.scaleLinear().range([height, 0]);
var g = svg.append("g")
.attr("transform", "translate(" + 100 + "," + 100 + ")");
xScale.domain(data.map(function(d) { return d.year; }));
yScale.domain([0, d3.max(data, function(d) { return d.value; })]);
g.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(xScale));
g.append("g")
.call(d3.axisLeft(yScale).tickFormat(function(d){
return "$" + d;
}).ticks(10));
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return xScale(d.year); })
.attr("y", function(d) { return yScale(d.value); })
.attr("width", xScale.bandwidth())
.attr("height", function(d) { return height - yScale(d.value); })
g.selectAll(".bar-title")
.data(data)
.enter()
.append("text")
.classed('bar-title', true)
.attr('text-anchor', 'middle')
.attr("x", d => xScale(d.year) + xScale.bandwidth()/2)
.attr("y", d => yScale(d.value) - 5)
.text(d => `$${d.value}`);
.bar {
fill: steelblue;
}
.bar-title {
font-family: 'Ubuntu';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg width="600" height="500"></svg>
In my scatterplot, I am hovering on the points, to generate a tooltip. But, my X-cordinate values are not right, corresponding to the graph axis. Can a clue be provided for the same??
One thing to note- I have 2 dataset arrays- 1 corresponds to Y value, the other corresponds to Y-Error values.
var width = 700;
var height = 700;
var padding = 90;
var myData = [12, 18, 20, 9, 17, 25, 30];
var errData = [6, 9, 10, 4.5, 8.5, 12.5, 15];
var svg = d3.select("body").
append("svg")
.attr("width", width)
.attr("height", height);
var Mydiv = d3.select("body")
.append("div")
//.append("span")
//.text('X')
.data(myData)
.attr("class", "toolDiv")
//.attr("span", "close")
.style("opacity", "0");
var MyLine = d3.select("body")
.append("div")
.data(myData)
.attr("class", "error-line");
//.style("opacity", 1);
//var m1 = d3.max(myData + errData)
var yScale = d3.scaleLinear().domain([0, d3.max(myData)]).range([height / 2, 50]);
var xScale = d3.scaleLinear().domain([0, d3.max(myData)]).range([0, width - 100]);
var y_ErScale = d3.scaleLinear().domain([0, d3.max(errData)]).range([height / 2, 50]);
var x_ErScale = d3.scaleLinear().domain([0, d3.max(errData)]).range([0, width - 100]);
var valueline = d3.selectAll("scatter-dots")
.append("line")
.attr("x", function(d, i) {
return xScale(myData[i]);
})
.attr("y", function(d) {
return yScale(d);
});
//.curve();
svg.append("g")
.selectAll("scatter-dots")
.data(myData)
.enter()
.append("line")
.data(myData)
.attr("class", "line")
.attr("d", valueline);
// .style("opacity", 0);
var eBar = d3.select("body").append("svg");
//var x_min =
var x_axis = d3.axisBottom()
.scale(xScale);
var y_axis = d3.axisLeft()
.scale(yScale);
svg.append("g")
.attr("transform", "translate(50, 10)")
.call(y_axis);
var xAxisTranslate = height / 2 + 10;
svg.append("g")
.attr("transform", "translate(50, " + xAxisTranslate + ")")
.call(x_axis);
svg.append("text")
.attr("transform",
"translate(" + (width / 1.2) + " ," + (height - 280) + ")")
.style("text-anchor", "middle")
.style("left", "70px")
.text("Points");
svg.append("text")
.attr("transform",
"translate(" + (width / 2) + " ," + (height - 280) + ")")
.style("text-anchor", "middle")
.style("left", "70px")
.text("X-Axis");
svg.append("text")
.attr("transform",
"translate(" + (height / 40) + " ," + (width - 500) + ")rotate(-90)")
.style("text-anchor", "middle")
.style("left", "70px")
.text("Y-Axis");
// svg.append("text")
// .attr("transform",
// "translate(" + (height/10) + " ," + (width - 690) + ")rotate(-90)")
// .style("text-anchor", "middle")
// .style("left", "70px")
// .text("Y");
svg.append("g")
.selectAll("scatter-dots")
.data(myData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return xScale(myData[i]);
})
.attr("cy", function(d) {
return yScale(d);
})
.attr("r", 3)
.style("opacity", 0.8)
.style("cursor", "help")
// .on("click", function(d, i){
// var active = Mydiv.active ? false : true ,
// newOpacity = active ? 0 : 0.9;
// Mydiv.transition()
// .duration(200)
// .style("opacity", newOpacity);
// Mydiv.html("X" + "-" + errData[i] + "<br/>" + "Y" + "-" + myData[i] )
// .style("left", (d3.event.pageX + 10) + "px")
// .style("top", (d3.event.pageY - 18) + "px");
// Mydiv.active = active;
// });
.on("mouseover", function(d, i) {
//console.log(this);
//console.log(d3.select(this));
//d3.select(this);
//console.log(d3.select(this));
Mydiv.transition()
.duration(200)
.style("opacity", 0.9);
Mydiv.html("X" + "-" + errData[i] + "<br/>" + "Y" + "-" + myData[i])
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
//div.html(yScale(d));
})
.on("mouseout", function(d) {
Mydiv.transition()
.duration(500)
.style("opacity", 0);
});
// var errorBar = eBar.append("path")
// .attr("d", yScale(errData))
// .attr("stroke", "red")
// .attr("stroke-width", 1.5);
// svg.append("g")
// .selectAll("error-bars")
// .data(errData)
// .enter().append("svg:path")
// .attr("cx", function (d,i) { return x_ErScale(errData[i]); } )
// .attr("cy", function (d) { return y_ErScale(d); } )
// .attr("stroke", "red")
// .attr("stroke-width", 1.5);
svg.append("g")
.selectAll("line")
.data(errData)
.enter()
.append("line")
.attr("class", "error-line")
.attr("x1", function(d) {
return x_ErScale(d);
})
.attr("y1", function(d) {
return y_ErScale(d) + 30;
})
.attr("x2", function(d) {
return x_ErScale(d);
})
.attr("y2", function(d) {
return y_ErScale(d) + 2;
})
.on("mouseover", function(d) {
MyLine.transition()
//.duration(200)
.style("opacity", "1");
//.style("fill-opacity", 0);
})
.on("mouseout", function(d) {
MyLine.transition()
//.duration(200)
.style("opacity", 0.0);
//.style("fill-opacity", 1);
});
svg.append("g").selectAll("line")
.data(errData).enter()
.append("line")
.attr("class", "error-cap")
.attr("x1", function(d) {
return x_ErScale(d) - 8;
})
.attr("y1", function(d) {
return y_ErScale(d) - 30;
})
.attr("x2", function(d) {
return x_ErScale(d) + 8;
})
.attr("y2", function(d) {
return y_ErScale(d) - 30;
});
svg.append("g")
.selectAll("line")
.data(errData)
.enter()
.append("line")
.attr("class", "error-line")
.attr("x1", function(d) {
return x_ErScale(d);
})
.attr("y1", function(d) {
return y_ErScale(d) - 30;
})
.attr("x2", function(d) {
return x_ErScale(d);
})
.attr("y2", function(d) {
return y_ErScale(d) - 2;
})
.on("mouseover", function(d) {
MyLine.transition()
//.duration(200)
.style("opacity", "1");
//.style("fill-opacity", 0);
})
.on("mouseout", function(d) {
MyLine.transition()
//.duration(200)
.style("opacity", "0.6");
//.style("fill-opacity", 1);
});
// .on("mouseover", function(d){
// MyLine.transition()
// .duration(200)
// .style("opacity", 0);
// })
// .on("mouseout", function(d){
// MyLine.transition()
// .duration(200)
// .style("opacity", 1);
// });
// Add Error Bottom Cap
svg.append("g").selectAll("line")
.data(errData).enter()
.append("line")
.attr("class", "error-cap")
.attr("x1", function(d) {
return x_ErScale(d) - 8;
})
.attr("y1", function(d) {
return y_ErScale(d) + 30;
})
.attr("x2", function(d) {
return x_ErScale(d) + 8;
})
.attr("y2", function(d) {
return y_ErScale(d) + 30;
});
//svg.selectAll("scatter-dots")
// .data(myData)
// .enter()
// .on("mouseover", function()
// {
// //console.log(this);
// //console.log(d3.select(this));
// //d3.select(this);
// //console.log(d3.select(this));
// //div.transition()
// //.duration(200)
// //.style("opacity", 0.8);
// //div.html(myData);
// });
//// .on("mouseout", function(d)
//// {
//// div.transition()
//// .duration(500)
//// .style("opacity", 0);
//// });
/*body {
font: 10px sans-serif;
}
*/
#d1 {
position: relative;
top: 100px;
}
#svg1 {
position: relative;
bottom: 80px;
}
.axis_path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis_line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.dot {
stroke: #000;
cursor: pointer;
}
.error-line {
stroke: #b30059;
stroke-width: 2px;
opacity: 0.6;
stroke-dasharray: 2;
/*fill-opacity: 1;*/
/*opacity: 1;*/
}
.error-cap {
stroke: #b30059;
stroke-width: 2px;
stroke-type: solid;
}
.toolDiv {
position: absolute;
text-align: center;
width: 120px;
height: 28px;
padding: 2px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 8px;
pointer-events: none;
}
/*.toolDiv.image
{
position: static;
content: url(http://wfarm1.dataknet.com/static/resources/icons/set28/7f8535d7.png);
}*/
/*.close {
width: 10px;
height: 10px;
background: #fff;
position: absolute;
top: 0;
right: 0;
background: url('http://i.imgur.com/Idy9R0n.png') no-repeat 0 0;
cursor: pointer;
}
.close:hover {
background-position: -13px 0;
}*/
.line {
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
<head>
<title>Scatter Plot Example</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<!-- <script src="http://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script> -->
<link rel="stylesheet" type="text/css" href="scatter.css">
</head>
<body>
<script src="scatterplot.js" type="text/javascript"></script>
<div id="d1">
<svg id="svg1">
<div id="d2"></div>
</svg>
</div>
</body>
I've included a fiddle for reference.
I am doing research for a professor at my school and I am coding in d3. I am creating a graph that has tool tips and you can toggle the graph by clicking on the legend. However, when I toggle the lines disappears but I can't get the tool tips associated to that line. I know I need to somehow add an id to these circles but I have no idea how. I have included my code below to show what I have so far. Any help would be appreciated!
<!DOCTYPE html>
<meta charset="utf-8">
<style> /* set the CSS */
body { font: 12px Arial;}
path {
stroke: white;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
div.tooltip {
position: absolute;
text-align: center;
width: 170px;
height: 500px;
padding: 1px;
font: 12px sans-serif;
background: lightsteelblue;
border: 0px;
border-radius: 5px;
pointer-events: none;
}
.legend {
font-size: 16px;
font-weight: bold;
text-anchor: middle;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {top: 20, right: 150, bottom: 60, left: 80},
width = 1160 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Parse the date / time
// Set the ranges
var x = d3.scaleTime().range([0, width-100]);
var formatxAxis=d3.format('.0f');
var y = d3.scaleLinear().range([height, 0]);
// Define the axe
var xAxis = d3.axisBottom().scale(x)
.tickFormat(formatxAxis).ticks(20);
var yAxis = d3.axisLeft().scale(y)
.ticks(5);
// Define the line
var valueline = d3.line()
.curve(d3.curveMonotoneX)
.x(function(d) { return x(d.year); })
.y(function(d) { return y(d.count); });
// Define the div for the tooltip
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// 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 + ")");
// Get the data
d3.json("satisfaction.json", function(error, data) {
data.forEach(function(d) {
d.year = +d.year;
d.count = +d.count;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.year; }));
y.domain([0, d3.max(data, function(d) { return d.count; })]);
//nest the entries by symbol
var dataNest = d3.nest()
.key(function(d) {
return d.word;
})
.entries(data);
//define colors for the lines
var color = d3.scaleOrdinal(d3.schemeCategory10);
// spacing for the legend
legendSpace = width/dataNest.length;
var circleid = svg.selectAll("circle")
.data(dataNest)
.enter()
.append("g")
.attr("id", function(d){
return "circle" + d.key.replace(/\s+/g, '');
});
// Loop through each symbol / key
dataNest.forEach(function(d,i) {
svg.append("path")
.attr("class", "line")
.style("stroke", function() { // Add the colours dynamically
return d.color = color(d.key); })
.attr("id", 'tag'+d.key.replace(/\s+/g, '')) // assign ID
.attr("d", valueline(d.values));
// Add the Legend
svg.append("text")
.attr("x", width - margin.left + 50)
.attr("y", legendSpace/4 + i*(legendSpace/6))
.attr("class", "legend") // style the legend
.style("fill", function() { // Add the colours dynamically
return d.color = color(d.key); })
.on("click", function(){
// Determine if current line is visible
var active = d.active ? false : true,
newOpacity = active ? 0 : 1;
// Hide or show the elements based on the ID
d3.select("#tag"+d.key.replace(/\s+/g, ''))
.transition().duration(100)
.style("opacity", newOpacity);
// Update whether or not the elements are active
d.active = active;
// Hide or show the elements based on the ID
d3.select("circle" + d.key.replace(/\s+/g, ''))
.transition().duration(100)
.style("opacity", newOpacity);
})
.text(d.key);
});
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("r", 5)
.attr("cx", function(d) { return x(d.year); })
.attr("cy", function(d) { return y(d.count); })
.on("click", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html(d.word + "<br/>" + d.count + "<br/>" + d.songs)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 25) + "px");
})
.on("dblclick", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
;
// 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);
});
</script>
</body>
Right now, you are trying to set your id before you have added the circles to the graph. Instead, you can set the id when you're appending the circles to your graph.
// Add the scatterplot
svg.selectAll("dot")
.data(data)
.enter().append("circle")
.attr("id", function(d){ return "circle" + d.key.replace(/\s+/g, ''); })
// ^---- add the id here when you're appending the circles
.attr("r", 5)
.attr("cx", function(d) { return x(d.year); })
.attr("cy", function(d) { return y(d.count); })
.on("click", function(d) {
div.transition()
.duration(200)
.style("opacity", .9);
div .html(d.word + "<br/>" + d.count + "<br/>" + d.songs)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 25) + "px");
})
.on("dblclick", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
})
;
I made a two-line chart with a legend. I want to highlight the stroke-width of each of the lines to 4px based on the color of the legend circle when the user hovers over it. So, user hovers over the blue legend circle, the blue line stroke-width changes to 4px. Same for the red one if he hovers over the red circle on the legend. Is this possible to do based on my code? Here is the whole code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script type="text/javascript" src="../d3/d3.js"></script>
<script type="text/javascript" src="../d3/d3-tip.js"></script>
<style type="text/css">
body{
font: 16px Calibri;
}
.line1{
fill: none;
stroke: steelblue;
stroke-width: 2px;
}
.line1:hover{
stroke-width: 3.5px;
}
.line2{
fill: none;
stroke: red;
stroke-width: 2px;
}
.line2:hover{
stroke-width: 3.5px;
}
.axis path,
.axis line{
fill:none;
stroke: black;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.axis text{
font-family: sans-serif;
font-size: 14px;
stroke: black;
stroke-width: 0.5px;
}
.legend .series {
cursor: pointer;
}
.legend circle {
stroke-width: 2px;
}
.legend .disabled circle {
fill-opacity: 0;
}
</style>
<!--...this code will be used on an external html file and instered-->
<html>
<body>
<div id="dropdown">
<select id = "opts">
<option value = "ds1">Atlas</option>
<option value = "ds2">BioSQL</option>
<option value = "ds3">Coppermine</option>
<option value = "ds4">Ensembl</option>
<option value = "ds5">Mediawiki</option>
<option value = "ds6">Opencart</option>
<option value = "ds7">PhpBB</option>
<option value = "ds8">Typo3</option>
</select>
</div>
<script type="text/javascript">
console.log("worked");
var ds1="../CSV/atlas/results/metrics.csv";
var ds2="../CSV/biosql/results/metrics.csv";
var ds3="../CSV/coppermine/results/metrics.csv";
var ds4="../CSV/ensembl/results/metrics.csv";
var ds5="../CSV/mediawiki/results/metrics.csv";
var ds6="../CSV/opencart/results/metrics.csv";
var ds7="../CSV/phpbb/results/metrics.csv";
var ds8="../CSV/typo3/results/metrics.csv";
</script>
</body>
</html> <!--...............................................................-->
<div id="area1"></div>
<div id="area2"></div>
</head>
<body>
<script type="text/javascript">
var margin = {top: 60, right: 20, bottom: 40, left: 40},
margin2 = {top: 430, right: 20, bottom: 0, left: 50},
width = 800 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
height2 = 870 - margin2.top - margin2.bottom;
var parseDate = d3.time.format("%Y").parse;
var color = d3.scale.category20()
.range(["#1f77b4", "#d62728", "#98df8a"]);
var x = d3.scale.linear()
.range([0, width]);
var x1 = d3.time.scale()
.nice(d3.time.year)
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5);
var x1Axis = d3.svg.axis()
.scale(x1)
.orient("bottom")
.ticks(5);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var svg1 = d3.select("#area1").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height2 + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var line1 = d3.svg.line()
.x(function(d) { return x(d.trID);})
.y(function (d) {return y(d.newT);})
.interpolate("basis");
var line2 = d3.svg.line()
.x(function(d) { return x1(d.time);})
.y(function (d) {return y(d.newT);})
.interpolate("basis");
var dsv = d3.dsv(";", "text/plain"); //setting the delimiter
var dataset = [] //defining the data array
var datapath="../CSV/atlas/results/metrics.csv";
dsv(datapath, function(data){ //------------select the file to load the csv------------
var label = document.getElementById('opts')[document.getElementById('opts').selectedIndex].innerHTML;//takes the name of the f
console.log(label);
dataset= data.map(function(d){ //parse
return { //insert parsed data in the array
trID: +d["trID"],
newT: +d["#newT"],
time: +d["time"]
};
});
dataset.forEach(function(d){
d.time = new Date(d.time*1000);
});
console.log(dataset);
x.domain(d3.extent(dataset, function(d) { return d.trID; }));
x1.domain(d3.extent(dataset, function(d) { return d.time; }));
// y.domain([0, d3.max(dataset.map( function(d) {return d.newT}))]);
y.domain(d3.extent(dataset, function(d) { return d.newT; }));
//------------------creating the lines---------------------
svg1.append("path")
.datum(dataset)
.attr("class", "line1")
.attr("d", line1);
svg1.append("path")
.datum(dataset)
.attr("class", "line2")
// .style("stroke-dasharray",("5,5"))
.attr("d", line2);
//----------------appending Legend--------------------------
var legend = svg1.selectAll(".legend")
.data((["Duration/Time","Duration/ID"]).slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("circle")
.attr("r", 7)
.attr("cx", 45)
.attr("cy", 10)
.style("fill", color);
legend.append("text")
.attr("x", 54)
.attr("y", 9)
.attr("dy", ".35em")
.style("text-anchor", "begin")
.style("font-family", "Calibri")
.text(function(d) { return d; });
//-----------------------------------------------------------
svg1.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("trID");
svg1.append("g")
.attr("class", "x1 axis")
.attr("transform", "translate(0," + height2 + ")")
.call(x1Axis)
.append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("time");
svg1.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("num of tables");
svg1.append("text")
.attr("class","simpletext")
.attr("x", (width/2))
.attr("y", 0 - (margin.top/2))
.attr("text-anchor", "middle")
.style("font-size", "20px")
.style("text-decoration", "underline")
.text(label);
});
d3.select('#opts')
.on('change', function(){
var dataset=[]
var datapath = eval(d3.select(this).property('value'));
label = document.getElementById('opts')[document.getElementById('opts').selectedIndex].innerHTML;
dsv(datapath, function(data){ //------------select the file to load the csv------------
dataset= data.map(function(d){ //parse
return { //insert parsed data in the array
trID: +d["trID"],
newT: +d["#newT"],
time: +d["time"]
};
});
dataset.forEach(function(d){
d.time = new Date(d.time*1000);
});
x.domain(d3.extent(dataset, function(d) { return d.trID; }));
x1.domain(d3.extent(dataset, function(d) { return d.time; }));
// y.domain([0, d3.max(dataset.map( function(d) {return d.newT}))]);
y.domain(d3.extent(dataset, function(d) { return d.newT; }));
d3.selectAll(".line1")
.transition()
.duration(1000)
.attr("d", line1(dataset));
d3.selectAll(".line2")
.transition()
.duration(1000)
.attr("d", line2(dataset));
//Update Axis
//Update X axis
svg1.select(".x.axis")
.transition()
.duration(1000)
.call(xAxis);
svg1.select(".x1.axis")
.transition()
.duration(1000)
.call(x1Axis);
//Update Y axis
svg1.select(".y.axis")
.transition()
.duration(1000)
.call(yAxis);
svg1.selectAll("path")
.data(dataset)
.exit()
.remove();
console.log(label);
svg1.selectAll(".simpletext")
.transition()
.text(label);
/* .attr("x", (width/2))
.attr("y", 0 - (margin.top/2))
.attr("text-anchor", "middle")
.style("font-size", "20px")
.style("text-decoration", "underline")
.text(label);*/
});
});
</script>
</body>
</html>
I define my color as this:
var color = d3.scale.category20()
.range(["#1f77b4", "#d62728", "#98df8a"]);
And each of my lines as this:
var line1 = d3.svg.line()
.x(function(d) { return x(d.trID);})
.y(function (d) {return y(d.newT);})
.interpolate("basis");
var line2 = d3.svg.line()
.x(function(d) { return x1(d.time);})
.y(function (d) {return y(d.newT);})
.interpolate("basis");
Of course it's possible.
So first it has nothing to do with line1 and line2 - they are about data of the line, not actual visual element. You need to change style of the path elements .line1 and .line2.
You can add event listeners when creating the legend.
//----------------appending Legend--------------------------
var legend = svg1.selectAll(".legend")
.data((["Duration/Time","Duration/ID"]).slice().reverse())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; })
.on("mouseover", function(d) {
if (d === "Duration/ID") {
// line1's legend is hovered
svg1.selectAll(".line1").style("stroke-width", "4px");
}
else if (d === "Duration/Time") {
// line2's legend is hovered
svg1.selectAll(".line2").style("stroke-width", "4px");
}
})
.on("mouseout", function() {
// revert the styles
svg1.selectAll(".line1").style("stroke-width", "2px");
svg1.selectAll(".line2").style("stroke-width", "2px");
});
This is just a demonstration. Of course it would be more consistent to define the styles in css and change class on events, and you may not want to hardcode legend names when you have more/dynamic series.
How would I need to modify the following code, which uses d3, to allow dragging of the graph along the x-axis:
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<style type="text/css">
.axis path, .axis line {
fill: none;
stroke: #ddd;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.axis path
{
stroke: #999;
stroke-width: 2px;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="http://d3js.org/d3.v2.min.js?2.9.4"></script>
</head>
<body>
<div id="chart" class="background">
</div>
</body>
<script type="text/javascript">
var data = [{
Id: "1",
Year: 1950,
Relevance: 55,
Category: "Cat1",
SpecFreq: 5,
GenFreq: 10
}, {
Id: "2",
Year: 1975,
Relevance: 25,
Category: "Cat1",
SpecFreq: 2,
GenFreq: 31
}, {
Id: "1",
Year: 1990,
Relevance: 75,
Category: "Cat1",
SpecFreq: 8,
GenFreq: 23
}];
$(function () {
DrawFocus(data);
});
function DrawFocus(data) {
//dimensions
var margin = {
top: 5.5,
right: 19.5,
bottom: 39.5,
left: 39.5
},
width = 800 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//data domain extents
var extentX = d3.extent(data, function (d) {
return d.Year;
});
var extentY = d3.extent(data, function (d) {
return d.Relevance;
});
//pad extents to provide some extra "blank" areas around edge of graph
extentX[0] = extentX[0] - 5;
extentX[1] = extentX[1] + 5;
extentY[0] = extentY[0] - 5;
extentY[1] = extentY[1] + 5;
//scales
var x = d3.scale.linear().domain(extentX).range([0, width]);
var y = d3.scale.linear().domain(extentY).range([height, 0]);
var radiusMax = .025 * width;
var radius = d3.scale.sqrt().domain([0, 100]).range([0, radiusMax]);
var color = d3.scale.ordinal().domain(["Cat1", "Cat2", "Cat3"]).range(["#b7b8a0", "#898a72", "#878772"]);
//axes
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.format("d")).tickSize(-height);
var yAxis = d3.svg.axis().scale(y).orient("left").tickSize(-width);
//create and size svg element, add zoom behavior
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 + ")");
// Add the x-axis.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
// Add the y-axis.
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// Add the x-axis label.
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width / 2)
.attr("y", height + 35)
.text(" Year");
// Add the y-axis label.
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("x", -1 * height / 2)
.attr("y", -40)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("Relevance");
//plot genFreq
var gGenerally = svg.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return color(d.Category);
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.GenFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
//plot specFreq
var gWithin = svg.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return "#d6d487";
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.SpecFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
return svg;
}
</script>
</html>
I've found several examples online and realize that I need to use the zoom behavior, but haven't been able to figure out precisely how to do it. There is a fiddle of the above code available.
Applying the zoom function as shown below, together with some additional code to restrict the zooming domain, was all I needed to do to get it to work. I do seem some odd "stickiness" going on once the graph has been scrolled to the far edge in any direction. It sometimes hesitates for a moment before scrolling back. I'm not sure what causes that. Also, this example is a bit unrealistic because I'm trimming my domain to match the min and max values in my data and then adding a +- 5 buffer. So, you can really only scroll that buffered area. However, I think it would behave just the same if you had a larger domain applied.
<html>
<head>
<style type="text/css">
.axis path, .axis line {
fill: none;
stroke: #ddd;
stroke-width: 1px;
shape-rendering: crispEdges;
}
.axis path
{
stroke: #999;
stroke-width: 2px;
}
</style>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="http://d3js.org/d3.v2.min.js?2.9.4"></script>
</head>
<body>
<div id="chart" class="background">
</div>
</body>
<script type="text/javascript">
var data = [{
Id: "1",
Year: 1950,
Relevance: 55,
Category: "Cat1",
SpecFreq: 5,
GenFreq: 10
}, {
Id: "2",
Year: 1975,
Relevance: 25,
Category: "Cat1",
SpecFreq: 2,
GenFreq: 31
}, {
Id: "1",
Year: 1990,
Relevance: 75,
Category: "Cat1",
SpecFreq: 8,
GenFreq: 23
}];
$(function () {
DrawFocus(data);
});
function DrawFocus(data) {
//dimensions
var margin = {
top: 5.5,
right: 19.5,
bottom: 39.5,
left: 39.5
},
width = 800 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//data domain extents
var extentX = d3.extent(data, function (d) {
return d.Year;
});
var extentY = d3.extent(data, function (d) {
return d.Relevance;
});
//pad extents to provide some extra "blank" areas around edge of graph
extentX[0] = extentX[0] - 5;
extentX[1] = extentX[1] + 5;
extentY[0] = extentY[0] - 5;
extentY[1] = extentY[1] + 5;
//scales
var x = d3.scale.linear().domain(extentX).range([0, width]);
var y = d3.scale.linear().domain(extentY).range([height, 0]);
var radiusMax = .025 * width;
var radius = d3.scale.sqrt().domain([0, 100]).range([0, radiusMax]);
var color = d3.scale.ordinal().domain(["Cat1", "Cat2", "Cat3"]).range(["#b7b8a0", "#898a72", "#878772"]);
//axes
var xAxis = d3.svg.axis().scale(x).orient("bottom").tickFormat(d3.format("d")).tickSize(-height).ticks(10);
var yAxis = d3.svg.axis().scale(y).orient("left").tickSize(-width).ticks(10);
//create and size svg element, add zoom behavior
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.call(d3.behavior.zoom().x(x).y(y).scaleExtent([1, 8]).on("zoom", zoom))
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Add the x-axis.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
// Add the y-axis.
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
// Add the x-axis label.
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.attr("x", width / 2)
.attr("y", height + 35)
.text(" Year");
// Add the y-axis label.
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("x", -1 * height / 2)
.attr("y", -40)
.attr("dy", ".75em")
.attr("transform", "rotate(-90)")
.text("Relevance");
//plot genFreq
var gGenerally = svg.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return color(d.Category);
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.GenFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
//plot specFreq
var gWithin = svg.append("g").selectAll("circle")
.data(data)
.enter().append("circle")
.style("fill", function (d) {
return "#d6d487";
})
.attr("cx", function (d) {
return x(d.Year);
})
.attr("cy", function (d) {
return y(d.Relevance);
})
.attr("r", function (d) {
return radius(d.SpecFreq);
})
.append("title")
.text(function (d) {
return "(" + d.Year + ", " + d.Relevance + ", " + d.GenFreq + ", " + d.SpecFreq + ")";
});
function zoom() {
if (x.domain()[0] >= extentX[0] -5 && x.domain()[1] <= extentX[1] +5 && y.domain()[0] >= extentY[0] -5 && y.domain()[1] <= extentY[1] +5) {
svg.select(".x.axis").call(xAxis).selectAll("text")
.style("font-size", "10px")
.attr("dy", "1.5em");
svg.select(".y.axis").call(yAxis).selectAll("text")
.style("font-size", "10px")
.attr("dx", "-1em");
svg.selectAll("circle").attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}
}
return svg;
}
</script>
</html>