lineFunction corresponds in this example with d3.svg.line(). Later, however, lineFunction is filled as a function with a parameter lineData, namely a list of points with x and y coordinates. How can I bypass lineFunction and include the dataset directly in d3.svg.line()?
My approach would be to call directly on d3.svg.line(lineData):
//The line SVG Path we draw
var lineGraph = svg.append("path")
.attr("d", d3.svg.line(lineData)
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate('linear'))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");
But this does not make any sense, as long as this is no function, that accepts parameters. I've also looked into the D3 code base and found that the line function does accept an input:
export default function() {
var x = pointX,
// ...
function line(data) {
// ...
}
// ...
return line;
}
Here is a running example by Dimitar Danailov:
var width = 400;
var height = 400;
var svg = d3.select('body').append('svg');
svg.attr('width', width);
svg.attr('height', height);
//This is the accessor function we talked about above
var lineFunction = d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate('linear');
//The data for our line
var lineData = [
{ "x": 1, "y": 5},
{ "x": 20, "y": 20},
{ "x": 40, "y": 10},
{ "x": 60, "y": 40},
{ "x": 80, "y": 5},
{ "x": 100, "y": 60}
];
//The line SVG Path we draw
var lineGraph = svg.append("path")
.attr("d", lineFunction(lineData))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");
svg {
font-family: "Helvetica Neue", Helvetica;
}
.line {
fill: none;
stroke: #000;
stroke-width: 2px;
}
<script src="//d3js.org/d3.v3.min.js"></script>
Source: https://bl.ocks.org/dimitardanailov/6f0a451d4457b9fa7bf6e0dddcd0f468
Further examples: https://www.dashingd3js.com/svg-paths-and-d3js
What you can do is call d3.svg.line() after you've configured it, like:
var lineGraph = svg.append("path")
.attr("d", d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate('linear')(lineData))
So the revised snippet looks like this:
var width = 400;
var height = 400;
var svg = d3.select('body').append('svg');
svg.attr('width', width);
svg.attr('height', height);
//The data for our line
var lineData = [
{ "x": 1, "y": 5},
{ "x": 20, "y": 20},
{ "x": 40, "y": 10},
{ "x": 60, "y": 40},
{ "x": 80, "y": 5},
{ "x": 100, "y": 60}
];
//The line SVG Path we draw
var lineGraph = svg.append("path")
.attr("d", d3.svg.line()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.interpolate('linear')(lineData))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none");
svg {
font-family: "Helvetica Neue", Helvetica;
}
.line {
fill: none;
stroke: #000;
stroke-width: 2px;
}
<script src="//d3js.org/d3.v3.min.js"></script>
Edit: Note that the question and answer uses d3.svg.line() which is from d3 v3. For higher versions you can use d3.line(), and omit the interpolate, like that mentioned below by #uzay95.
Related
I'm trying to create points between any 2 points of my triangle. How do i create mid-points between 2 points? For example, how would i create a point
between the coordinates (150, 0) and (0, 200)?
var points = [
{"x": 150, "y": 0},
{"x": 0, "y": 200},
{"x": 300, "y": 200},
{"x": 150, "y": 0}
];
//CREATE THE SVG CONTAINER
var svg = d3.select("body").append("svg")
.attr("width", 500)
.attr("height", 500)
.style('fill', '#113F8C')
.style('padding-left', '20px')
.style('padding-top', '5px');
// DRAW THE PATH
var path = svg.append("path")
.data([lineData])
.attr("stroke", "black")
.attr("stroke-width", 3)
.attr("d", d3.line());
//SET THE POINTS
svg.selectAll("circle")
.data(points)
.enter().append("circle")
.attr("cx", function(d) {
console.log(d.x+ " hello");
for (var i = 0; i < points.length; i++) {
//console.log(points[i]);
x[i] = d.x;
console.log(d.x);
};
return d.x;
})
.attr("cy", function(d) {console.log(d.y+ " hello"); return d.y; })
.attr("r", 5);
// THE X TEST
x[0];
Since you have a points data with a redundant fourth point in the triangle (which is the same of the first), we can simply remove this fourth object of the data:
.data(points.slice(0, points.length-1))
And don't even mind about the range error of the last data:
.attr("cx", (d,i)=>(d.x + points[i+1].x)/2)
.attr("cy", (d,i)=>(d.y + points[i+1].y)/2);
Here is a demo. The blue points are the points of your data array, the red points are the mid points.
var svg = d3.select("body")
.append("svg")
.attr("width", 400)
.attr("height", 300);
var points = [
{"x": 150, "y": 10},
{"x": 10, "y": 200},
{"x": 300, "y": 200},
{"x": 150, "y": 10}
];
var circles = svg.selectAll(".circles")
.data(points)
.enter()
.append("circle")
.attr("r", 5)
.attr("fill", "blue")
.attr("cx", d=>d.x)
.attr("cy", d=>d.y);
var midPoints = svg.selectAll(".midPoints")
.data(points.slice(0, points.length-1))
.enter()
.append("circle")
.attr("r", 5)
.attr("fill", "brown")
.attr("cx", (d,i)=>(d.x + points[i+1].x)/2)
.attr("cy", (d,i)=>(d.y + points[i+1].y)/2);
<script src="https://d3js.org/d3.v4.min.js"></script>
The same snippet, with lines for each triangle:
var svg = d3.select("body")
.append("svg")
.attr("width", 400)
.attr("height", 300);
var points = [
{"x": 150, "y": 10},
{"x": 10, "y": 200},
{"x": 300, "y": 200},
{"x": 150, "y": 10}
];
var line = d3.line()
.x(d=>d.x)
.y(d=>d.y);
var lineMid = d3.line()
.x((d,i)=>(d.x + points[i+1].x)/2)
.y((d,i)=>(d.y + points[i+1].y)/2)
.curve(d3.curveLinearClosed);
var lineCircles = svg.append("path")
.attr("d", line(points))
.attr("fill", "none")
.attr("stroke", "blue")
.attr("opacity", 0.4);
var lineMidPoints = svg.append("path")
.attr("d", lineMid(points.slice(0, points.length-1)))
.attr("fill", "none")
.attr("stroke", "brown")
.attr("opacity", 0.4);
var circles = svg.selectAll(".circles")
.data(points)
.enter()
.append("circle")
.attr("r", 5)
.attr("fill", "blue")
.attr("cx", d=>d.x)
.attr("cy", d=>d.y);
var midPoints = svg.selectAll(".midPoints")
.data(points.slice(0, points.length-1))
.enter()
.append("circle")
.attr("r", 5)
.attr("fill", "brown")
.attr("cx", (d,i)=>(d.x + points[i+1].x)/2)
.attr("cy", (d,i)=>(d.y + points[i+1].y)/2);
<script src="https://d3js.org/d3.v4.min.js"></script>
A simple program for multiple ellipse I have written. The program shows no error but also no ellipse is being seen. Though i tried add multiple random colors for it. I think there is some slight mistake somewhere. Can someone help out?
SNIPPET:
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script>
<script>
$(document).ready(function(){
//our basic data
var customData = [
{ "x": 30, "y": 30, "width": 20, "height" : 10 },
{ "x": 70, "y": 70, "width": 20, "height" : 20},
{ "x": 110, "y": 100, "width": 20, "height" : 30}
];
//Make an SVG Container
var mySVG = d3.select("svg");
//create ellipses skeleton by data
var ellipses = mySVG.selectAll("ellipse")
.data(customData)
.enter()
.append("ellipse");
//Draw the Rectangle
ellipses.append("ellipse")
.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("rx", function (d) { return d.width; })
.attr("ry", function(d) { return d.height; })
.attr("fill",function() { return "hsl(" + Math.random() * 360 + ",100%,50%)"; });
});
</script>
</head>
<body>
<svg width="500px" height="500px"></svg>
</body>
</html>
You are appending ellipse elements twice. Here is the working snippet.
var customData = [{
"x": 30,
"y": 30,
"width": 20,
"height": 10
}, {
"x": 70,
"y": 70,
"width": 20,
"height": 20
}, {
"x": 110,
"y": 100,
"width": 20,
"height": 30
}];
//Make an SVG Container
var mySVG = d3.select("svg");
//create ellipses skeleton by data
var ellipses = mySVG.selectAll("ellipse")
.data(customData)
.enter()
.append("ellipse");
// Removed second append from here
//Draw the Rectangle
ellipses.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("rx", function(d) {
return d.width;
})
.attr("ry", function(d) {
return d.height;
})
.attr("fill", function() {
return "hsl(" + Math.random() * 360 + ",100%,50%)";
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="500px" height="500px"></svg>
I am drawing line chart but unable to figure out the scale for x-axies I am using ordinal scale because i am showing categories on x-axis here is the code for scaling. please let me know what i am doing wrong here.
var xScale = d3.scale.ordinal()
.domain(xExtents)
.range([padding, w - padding * 2]);
here is the sample code sorry for putting all javascript and HTML at one place.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
<style type="text/css">
.axis path,
.axis line {
fill: none;
stroke: black;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
.y1 {
fill: white;
stroke: orange;
stroke-width: 1.5px;
}
.y2 {
fill: white;
stroke: red;
stroke-width: 1.5px;
}
.y3 {
fill: white;
stroke: steelblue;
stroke-width: 1.5px;
}
.line {
fill: none;
stroke-width: 1.5px;
}
div.tooltip {
position: absolute;
text-align: center;
width: 50px;
height: 10px;
padding: 5px;
font: 10px sans-serif;
background: whiteSmoke;
border: solid 1px #aaa;
pointer-events: none;
box-shadow: 2px 2px 1px #888;
}
.legend {
padding: 5px;
font: 10px sans-serif;
background: yellow;
box-shadow: 2px 2px 1px #888;
}
.title {
font: 13px sans-serif;
}
</style>
</head>
<body>
<script type="text/javascript">
//Width and height
var w = 500;
var h = 300;
var padding = 50;
var dataset = [
[
{x: "USA", y: 0},
{x: "UK", y: 0},
{x: "BRAZIL", y: 2},
{x: "KORIA", y: 0},
{x: "JAPAN", y: 0},
{x: "CHINA", y: 0}
],
[
{x: "USA", y: 3},
{x: "UK", y: 4},
{x: "BRAZIL", y: 1},
{x: "KORIA", y: 3},
{x: "JAPAN", y: 1},
{x: "CHINA", y: 2}
],
[
{x: "USA", y: 2},
{x: "UK", y: 0},
{x: "BRAZIL", y: 2},
{x: "KORIA", y: 3},
{x: "JAPAN", y: 4},
{x: "CHINA", y: 1}
]
];
var color_hash = { 0 : ["apple", "green"],
1 : ["mango", "orange"],
2 : ["cherry", "red"]
}
// Define axis ranges & scales
var yExtents = d3.extent(d3.merge(dataset), function (d) { return d.y; });
var xExtents = d3.extent(d3.merge(dataset), function (d) { return d.x; });
var xScale = d3.scale.ordinal()
.domain(xExtents)
.range([padding, w - padding * 2]);
var yScale = d3.scale.linear()
.domain([0, yExtents[1]])
.range([h - padding, padding]);
// Create SVG element
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
// Define lines
var line = d3.svg.line()
.x(function(d) { return x(d.x); })
.y(function(d) { return y(d.y1, d.y2, d.y3); });
var pathContainers = svg.selectAll('g.line')
.data(dataset);
pathContainers.enter().append('g')
.attr('class', 'line')
.attr("style", function(d) {
return "stroke: " + color_hash[dataset.indexOf(d)][1];
});
pathContainers.selectAll('path')
.data(function (d) { return [d]; }) // continues the data from the pathContainer
.enter().append('path')
.attr('d', d3.svg.line()
.x(function (d) { return xScale(d.x); })
.y(function (d) { return yScale(d.y); })
);
// add circles
pathContainers.selectAll('circle')
.data(function (d) { return d; })
.enter().append('circle')
.attr('cx', function (d) { return xScale(d.x); })
.attr('cy', function (d) { return yScale(d.y); })
.attr('r', 3);
//Define X axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.ticks(5);
//Define Y axis
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left")
.ticks(5);
//Add X axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
//Add Y axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
// Add title
svg.append("svg:text")
.attr("class", "title")
.attr("x", 20)
.attr("y", 20)
.text("Fruit Sold Per Hour");
// add legend
var legend = svg.append("g")
.attr("class", "legend")
.attr("x", w - 65)
.attr("y", 25)
.attr("height", 100)
.attr("width", 100);
legend.selectAll('g').data(dataset)
.enter()
.append('g')
.each(function(d, i) {
var g = d3.select(this);
g.append("rect")
.attr("x", w - 65)
.attr("y", i*25)
.attr("width", 10)
.attr("height", 10)
.style("fill", color_hash[String(i)][1]);
g.append("text")
.attr("x", w - 50)
.attr("y", i * 25 + 8)
.attr("height",30)
.attr("width",100)
.style("fill", color_hash[String(i)][1])
.text(color_hash[String(i)][0]);
});
</script>
</body>
</html>
If you want to show different categories on x axis (in your case, there are five countries with corresponding data), the x scale setting should be using rangeBands or rangeRoundBands for ordinal data. (see ordinal.rangeBands and ordinal.rangeRoundBands in official api documents).
var xScale = d3.scale.ordinal()
.domain(xExtents)
.rangeRoundBands([padding, w - padding * 2], 0.1);
range is often used with linear type of data. Hope this helps.
First Problem:
Use this https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js
Instead of this http://mbostock.github.com/d3/d3.js
Second Problem:
d3.extent(d3.merge(dataset), function (d) { return d.x; }
will return ["BRAZIL", "USA"]
You need all the unique countries.
var countries = d3.merge(dataset).map(function(d){return d.x});
countries = d3.set(countries).values();//unique countries
var xScale = d3.scale.ordinal()
.domain(countries)
.rangeBands([padding, w - padding * 2], 1);
this will return ["USA", "UK", "BRAZIL", "KORIA", "JAPAN", "CHINA"]
Last Problem:
var xScale = d3.scale.ordinal()
.domain(countries)
.rangeBands([padding, w - padding * 2], 1);//with ordinals we give range bands
Working code here
Hope this helps!
I want to draw an xy multiseries linechart with d3.js. Thats ok.
But afterwards I want to scale the x-axes ordinal.
so thats the code of my xy linechart:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
div.tooltip {
position: absolute;
text-align: center;
padding: 5px;
font: 14px sans-serif;
background: black;
color: white;
border: 0px;
border-radius: 8px;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1px;
}
</style>
<body>
<script src="d3.js"></script>
<script src="jquery-2.1.4.js" charset="utf-8"></script>
<script>
var margin = {
top: 20,
right: 20,
bottom: 20,
left: 50
},
width = 1180 - margin.left - margin.right,
height = 580 - margin.top - margin.bottom;
var x = d3.scale.linear().rangeRound([0, width]);
var y = d3.scale.linear().rangeRound([height, 0]);
var linearScale = d3.scale.linear();
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("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 line = d3.svg.line()
.x(function(d) { console.log('x'+x(d.arbeitsgang));return x(d.arbeitsgang); })
.y(function(d) { console.log('y'+y(d.koordinaten));return y(d.koordinaten); });
*/
var line = d3.svg.line()
.x(function (d) {
return x(d.x);
})
.y(function (d) {
return y(d.y);
});
// Define 'div' for tooltips
var div = d3.select("body")
.append("div") // declare the tooltip div
.attr("class", "tooltip") // apply the 'tooltip' class
.style("opacity", 0); // set the opacity to nil
//d3.json("Arbeitsgang.json", function(error, data) {
var data = [
{
"key": "Paket 1",
"values": [
{
"x": 0,
"y": 40,
"arbeitsgang": "A1"
},
{
"x": 6,
"y": 30,
"arbeitsgang": "A2"
},
{
"x": 17,
"y": 20,
"arbeitsgang": "A3"
}
]
},
{
"key": "Paket 3",
"values": [
{
"x": 0,
"y": 85,
"arbeitsgang": "A1"
},
{
"x": 8,
"y": 50,
"arbeitsgang": "A2"
},
{
"x": 17,
"y": 89,
"arbeitsgang": "A3"
}
]
},
{
"key": "Paket 2",
"values": [
{
"x": 0,
"y": 45,
"arbeitsgang": "A1"
},
{
"x": 6,
"y": 145,
"arbeitsgang": "A1"
},
{
"x": 17,
"y": 53,
"arbeitsgang": "A1"
}
]
}
];
linearScale.domain(d3.keys(data[0]).filter(function (key) {
return key;
}));
x.domain([
d3.min(data, function (c) {
return d3.min(c.values, function (v) {
return v.x;
});
}),
d3.max(data, function (c) {
return d3.max(c.values, function (v) {
return v.x;
});
})
]);
y.domain([
d3.min(data, function (c) {
return d3.min(c.values, function (v) {
return v.y;
});
}),
d3.max(data, function (c) {
return d3.max(c.values, function (v) {
return v.y;
});
})
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
var graphen = svg.selectAll(".graphen")
.data(data)
.enter().append("g")
.attr("class", "graphen");
var graph = graphen.append("path")
.attr("class", "line")
.attr("d", function (d) {
return line(d.values);
});
graph.on("mouseover", function (d) {
d3.select(this).style("stroke-width", 7);
div.transition()
.duration(200)
.style("opacity", .9);
div.html(d.key)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
div.style("visibility", "visible");
var selectthegraphs = $('.line').not(this); //select all the rest of the lines, except the one you are hovering on and drop their opacity
d3.selectAll(selectthegraphs)
.style("opacity", 0.2);
var selectcircles = $('.circle');
d3.selectAll(selectcircles)
.style("opacity", 0.2);
})
.on("mouseout", function () {
d3.select(this).style("stroke-width", 1);
div.transition()
.duration(500)
.style("opacity", 0.01);
div.style("visibility", "hidden");
var selectthegraphs = $('.line'); //select all the rest of the lines, except the one you are hovering on and drop their opacity
d3.selectAll(selectthegraphs)
.style("opacity",1);
var selectcircles = $('.circle');
d3.selectAll(selectcircles)
.style("opacity", 1);
});
graphen.each(function (p, j) {
d3.select(this).selectAll("circle")
.data(p.values)
.enter().append("circle")
.style("stroke", "black")
.style("fill", "white")
.attr("class","circle")
.attr("r", 5)
.attr("cx", function (d) {
return x(d.x);
})
.attr("cy", function (d) {
return y(d.y);
})
.on("mouseover", function (d) {
d3.select(this).transition().duration(500)
.attr("r", 10);
div.transition()
.duration(500)
.style("opacity", 0.9);
div.style("visibility", "visible");
div.html("X: " + d.x + "<br/>" + "Y: " + d.y + "<br/>" + "Arbeitsgang: " + d.arbeitsgang)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function (d) {
d3.select(this).transition().duration(500)
.attr("r", 5);
div.transition()
.duration(500)
.style("opacity", 0);
div.style("visibility", "hidden");
})
});
ok. but now I want the same graph with an ordinal scale.
The ticks shall be "data.values.arbeitsgang"
"arbeitsgang": "A1" for example.
please help me
Ordinal scales are not so tough.
var x = d3.scale.ordinal()
.domain(["A1","A2","A3"])
.rangeRoundPoints([0, width]);
The scale should now divide the range going from 0 to your width into 3 equally large parts. I choose for rangeRoundPoints because the values of the ticks are rounded to integers then and I like integers.
I believe you can customize the text of the x axis (of course using your ordinal scale to scale the axis), showing "Arbeitsgang:A1" for example, but I wouldn't know how to do that right on the top of my head.
For more information about ordinal scales, check out this link. Unless I totally misunderstood the question, I think this is the only thing you need.
I realize that I have hardcoded the values of the domain, but an array containing the values will do as well.
I have a graph for which I need a reference line everywhere the mouse-cursor is inside this graph. And this reference line will follow the mouse movements inside the graph.
But this doesn't seems to work fine. It works only on the axis and the ticks (.axis lines) of the axis. On debugging, I found that mouse event works fine when applied over SVG but not on the group, why so ?
Here is my code :
test.html
<html>
<head>
<script src="jquery.js">
</script>
<script src="d3.v2.js">
</script>
<script src="retest.js">
</script>
<style type="text/css">
.g_main {
cursor:pointer;
}
.axis path, .axis line {
stroke: #DBDBDB;
/*shape-rendering: crispEdges;
*/
}
.y g:first-child text {
display:none;
}
.y g:first-child line {
stroke: #989898 ;
stroke-width: 2.5px;
}
/*.x g:first-child line {
stroke: black ;
stroke-width: 2.5px;
}
*/
.y path {
stroke: #989898 ;
stroke-width: 2.5px;
}
</style>
</head>
<body>
<center>
<button id="reload" onclick="loadViz();">
load Graph
</button>
<div id="viz" class="viz">
</div>
</center>
<script>
loadViz();
</script>
</body>
</html>
retest.js
var series,
classifications,
minVal,
maxVal,
svgW = 600,
svgH = 600,
//w = 1200,
//h = 1200,
vizPadding = {
top: 120,
right: 30,
bottom: 120,
left: 50
},
yAxMin_PA = 0,
yAxMax_PA = 50,
xAxMin_PA = 2002,
xAxMax_PA = 2008,
areaStrokeColors = ['#FF6600', '#3366FF', '#B8860B', '#458B00', 'white'];
var loadViz = function () {
color = d3.scale.category10();
data = {
"lines": [{
"line": [{
"X": 2002,
"Y": 42
}, {
"X": 2003,
"Y": 45
},
{
"X": 2005,
"Y": 47
},
{
"X": 2007,
"Y": 41
}
]
}, {
"line": [{
"X": 2003,
"Y": 33
}, {
"X": 2005,
"Y": 38
}, {
"Y": 36,
"X": 2008
}
]
}, {
"line": [{
"X": 2004,
"Y": 13
}, {
"X": 2005,
"Y": 19
}, {
"X": 2008,
"Y": 21
}
]
}, {
"line": [{
"X": 2003,
"Y": 20
}, {
"X": 2005,
"Y": 27
}, {
"X": 2008,
"Y": 29
}
]
}
]
};
$("#viz").html("");
buildBase();
//setScales();
};
var buildBase = function () {
margin = {
top: 80,
right: 120,
bottom: 40,
left: 40
},
width = 960 - margin.left - margin.right,
height = 550 - margin.top - margin.bottom;
t2 = height + margin.top + margin.bottom;
x = d3.scale.linear()
.domain([xAxMin_PA, xAxMax_PA])
.range([0, width]);
y = d3.scale.linear()
.domain([yAxMin_PA, yAxMax_PA])
.range([height, 0]);
tickSizeToApplyX = 5;
tickSizeToApplyY = 10;
// Function to draw X-axis
xAxis = d3.svg.axis()
.scale(x)
.ticks(tickSizeToApplyX)
.tickSize(-height, 0, 0)
//.tickSize(10)
.orient("bottom")
.tickPadding(5);
// Function to draw Y-axis
yAxis = d3.svg.axis()
.scale(y)
.ticks(tickSizeToApplyY)
.tickSize(-width, 0, 0)
//.tickSize(0)
.orient("left")
.tickPadding(5);
// Define the line
var valueline = d3.svg.line()
.x(function (d) { /*console.log(d.X);*/
return x(d.X);
})
.y(function (d) { /*console.log(d.Y);*/
return y(d.Y);
});
// Define the line
var referline = d3.svg.line()
.x(function (dx) { /*console.log(d.X);*/
return dx;
})
.y(function (dy) { /*console.log(d.Y);*/
return dy;
});
// Append SVG into the html
var viz = d3.select("#viz")
.append("svg")
.attr("width", width + margin.left + margin.right + 10)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("class", "g_main")
.attr("transform", "translate(" + margin.left + "," + ((margin.top) - 30) + ")");
viz.on("mousemove", function () {
cx = d3.mouse(this)[0];
cy = d3.mouse(this)[1];
console.log("xx=>" + cx + "yy=>" + cy);
redrawline(cx, cy);
})
.on("mouseover", function () {
d3.selectAll('.line_over').style("display", "block");
})
.on("mouseout", function () {
d3.selectAll('.line_over').style("display", "none");
});
//console.log(this);
viz.append("line")
//d3.select("svg").append("line")
.attr("class", 'line_over')
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", x(xAxMax_PA))
.attr("y2", 0)
.style("stroke", "gray")
.attr("stroke-dasharray", ("5,5"))
.style("stroke-width", "1.5")
.style("display", "none");
// Draw X-axis
viz.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Draw Y-axis
viz.append("g")
.attr("class", function (d, i) {
return "y axis"
})
.call(yAxis);
function redrawline(cx, cy) {
d3.selectAll('.line_over')
.attr("x1", 0)
.attr("y1", cy)
.attr("x2", x(xAxMax_PA))
.attr("y2", cy)
.style("display", "block");
}
};
The g element is just an empty container which cannot capture click events (see documentation for pointer-events property for details).
However, mouse events do bubble up to it. Hence, the effect you desire can be achieved by first making sure that the g receives all pointer events:
.g_main {
// ..
pointer-events: all;
}
And then appending an invisible rectangle to it as a place to hover over:
viz.on("mousemove", function () {
cx = d3.mouse(this)[0];
cy = d3.mouse(this)[1];
redrawline(cx, cy);
})
.on("mouseover", function () {
d3.selectAll('.line_over').style("display", "block");
})
.on("mouseout", function () {
d3.selectAll('.line_over').style("display", "none");
})
.append('rect')
.attr('class', 'click-capture')
.style('visibility', 'hidden')
.attr('x', 0)
.attr('y', 0)
.attr('width', width)
.attr('height', height);
Working example: http://jsfiddle.net/H3W3k/
As for why they work when applied to the svg element (from the docs):
Note that the ‘svg’ element is not a graphics element, and in a Conforming SVG Stand-Alone File a rootmost ‘svg’ element will never be the target of pointer events, though events can bubble to this element. If a pointer event does not result in a positive hit-test on a graphics element, then it should evoke any user-agent-specific window behavior, such as a presenting a context menu or controls to allow zooming and panning of an SVG document fragment.