d3 mouse move cross hair boundary - javascript

[Update]
Click jsfiddle here.
[Original Post]
How can I limit the coordinates of cross hair when moving the mouse? Notice when I move my mouse to the left of x-axis or bottom of y-axis, the cross hair and text still shows. I want the cross hair and text to stop showing when either mouse is moved to the left of x-axis or bottom of y-axis.
I have tried to add if else to limit the cross hair, but it didn't work. For instance, I tried something like .style("display", (xCoord>=minX & xCoord<=maxX & yCoord>=minY & yCoord<=maxY) ? "block" : "none") in the addCrossHair() function.
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style>
.axis path,
.axis line
{
fill:none;
rendering:crispEdges;
stroke:black;
width:2.5;
}
</style>
</head>
<body>
<script src="d3.min.js"></script>
<script>
var width = 800,
height = 600;
var randomX = [],
randomY = [];
for (var i = 0; i <= 500; i++) {
randomX[i] = Math.random() * 400;
randomY[i] = Math.random() * 400;
}
var minX = d3.min(randomX),
maxX = d3.max(randomX),
minY = d3.min(randomY),
maxY = d3.max(randomY);
var xScale = d3.scale.linear().domain([minX, maxX]).range([0, width]);
var xAxis = d3.svg.axis().scale(xScale).orient("bottom");
var yScale = d3.scale.linear().domain([minY, maxY]).range([height, 0]);
var yAxis = d3.svg.axis().scale(yScale).orient("left");
var svgContainer = d3.select("body").append("div").append("svg").attr("width", width).attr("height", height);
var svg = svgContainer.append("g").attr("transform", "translate(50, 50)");
svg.append("g").attr("class", "axis").attr("transform", "translate(0,530)").call(xAxis);
svg.append("g").attr("class", "axis").call(yAxis);
var crossHair = svg.append("g").attr("class", "crosshair");
crossHair.append("line").attr("id", "h_crosshair") // horizontal cross hair
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", 0)
.style("stroke", "gray")
.style("stroke-width", "1px")
.style("stroke-dasharray", "5,5")
.style("display", "none");
crossHair.append("line").attr("id", "v_crosshair") // vertical cross hair
.attr("x1", 0)
.attr("y1", 0)
.attr("x2", 0)
.attr("y2", 0)
.style("stroke", "gray")
.style("stroke-width", "1px")
.style("stroke-dasharray", "5,5")
.style("display", "none");
crossHair.append("text").attr("id", "crosshair_text") // text label for cross hair
.style("font-size", "10px")
.style("stroke", "gray")
.style("stroke-width", "0.5px");
svgContainer.on("mousemove", function () {
var xCoord = d3.mouse(this)[0] - 50,
yCoord = d3.mouse(this)[1] - 50;
addCrossHair(xCoord, yCoord);
})
.on("mouseover", function () {d3.selectAll(".crosshair").style("display", "block");})
.on("mouseout", function () {d3.selectAll(".crosshair").style("display", "none");});
function addCrossHair(xCoord, yCoord) {
// Update horizontal cross hair
d3.select("#h_crosshair")
.attr("x1", xScale(minX))
.attr("y1", yCoord)
.attr("x2", xScale(maxX))
.attr("y2", yCoord)
.style("display", "block");
// Update vertical cross hair
d3.select("#v_crosshair")
.attr("x1", xCoord)
.attr("y1", yScale(minY))
.attr("x2", xCoord)
.attr("y2", yScale(maxY))
.style("display", "block");
// Update text label
d3.select("#crosshair_text")
.attr("transform", "translate(" + (xCoord + 5) + "," + (yCoord - 5) + ")")
.text("(" + xScale.invert(xCoord) + " , " + yScale.invert(yCoord) + ")");
}
svg.selectAll("scatter-dots")
.data(randomY)
.enter().append("svg:circle")
.attr("cy", function (d) {
return yScale(d);
})
.attr("cx", function (d, i) {
return xScale(randomX[i]);
})
.style("fill", "brown")
.attr("r", 3)
</script>
</body>

The way to do this is to make the actual canvas (i.e. where you're drawing the dots) a separate g element from the ones that the axes are rendered into. Then the canvas can be translated such that it sits to the right and above the axes. The crosshair handler would be attached to this canvas g element (which is not quite straightforward, see this question) and the crosshairs or dots won't appear outside of the canvas.
Complete demo here.

Related

Tooltip is not showing at appended 'rect' on top of point/circle in d3 chart

I have a d3 line chart with a tooltip, I am facing a problem with a tooltip.
I have functionality, on click of points/circle I am appending rect to g, which is adding on top of the existing rect which has the tooltip functionality.
My tooltip is not coming at selected(rect) Graph Point.
g.append("rect")
.attr("class", "overlay")
.attr("id", "firstLayer")
.attr("width", width)
.attr("height", height)
.on("mouseover", function(d) {
focus.style("display", null);
div
.transition()
.duration(200)
.style("opacity", 0.9);
})
.on("click", function(d, index) {
let newXScale = newX ? newX : xScale;
if (rect) rect.remove();
rect = g
.append("rect")
.attr("x", newXScale(d.startTime) - 12.5)
.attr("y", 0)
.attr("width", 24)
.attr("height", height + 5)
.attr("data", d.startTime)
.style("fill", "steelblue")
.style("opacity", 0.5);
if (clickLine) clickLine.remove();
clickLine = g
.append("line")
.attr("x1", newXScale(d.startTime))
.attr("y1", yScale(yDomain[0]))
.attr("x2", newXScale(d.startTime))
.attr("y2", yScale(yDomain[1]))
.attr("class", "focusLine")
.style("opacity", 0.5);
})
rect element is coming on top of the gm on hover of that tooltip is not coming, any suggestions on how to fix it ?
On mouse hover -
At selected Graph Point -
CodeSandbox link below -
https://codesandbox.io/s/damp-dawn-82hxc
Please guide me what can be changed.
on click of the circle you are appending another rectangle to g, which is adding on top of the existing rect which has the tool tip functionality
Note: d3 js adds layer/shape on top of another which basically overrides the existing layer/shape functionality with the new layer/shape if they are in the same position
To avoid that we have to draw the layers depends on their intended purpose and position.
Solution for the above problem
append background rects for circle you want to create with opacity: 0
g.selectAll(".faaa")
.data(data)
.enter()
.append("rect")
.attr("class", "faaa")
.attr("id", d => "rect_" + d.id)
.attr("x", d => xScale(d.startTime) - 12.5)
.attr("y", 0)
.attr("width", 24)
.attr("height", height + 5)
.attr("data", d => d)
.style("fill", "steelblue")
.style("opacity", 0);
append firstLayer rect which has the tooltip functionality so the background rect won't break the tooltip functionality
g.append("rect")
.attr("class", "overlay")
.attr("id", "firstLayer")
.attr("width", width)
.attr("height", height)
.on("mouseover", function(d) {
focus.style("display", null);
div
.transition()
.duration(200)
.style("opacity", 0.9);
})
.on("mouseout", function() {
focus.style("display", "none");
div
.transition()
.duration(300)
.style("opacity", 0);
})
.on("mousemove", function() {
var mouse = d3.mouse(this);
var mouseDate = xScale.invert(mouse[0]);
var i = bisectDate(data, mouseDate); // returns the index to the current data item
var d0 = data[i - 1];
var d1 = data[i];
let d;
// work out which date value is closest to the mouse
if (typeof d1 !== "undefined") {
d = mouseDate - d0.startTime > d1.startTime - mouseDate ? d1 : d0;
} else {
d = d0;
}
div
.html(
`<span>${parseDate(d.startTime)}</span>
<span> Changes : ${d.magnitude} % </span>`
)
.style("left", d3.event.pageX + "px")
.style("top", d3.event.pageY - 28 + "px");
var x = xScale(d.startTime);
var y = yScale(d.magnitude);
focus
.select("#focusCircle")
.attr("cx", x)
.attr("cy", y);
focus
.select("#focusLineX")
.attr("x1", x)
.attr("y1", yScale(yDomain[0]))
.attr("x2", x)
.attr("y2", yScale(yDomain[1]));
focus
.select("#focusLineY")
.attr("x1", xScale(xDomain[0]))
.attr("y1", y)
.attr("x2", xScale(xDomain[1]))
.attr("y2", y);
});
append circle and add click functionality then change the opacity to highlight the background rect
g.selectAll(".foo")
.data(data)
.enter()
.append("circle")
.attr("id", d => d.id)
.attr("class", "foo")
.attr("data", d => d)
.attr("cx", function(d) {
return xScale(d.startTime);
})
.attr("cy", function(d) {
return yScale(d.magnitude);
})
.attr("r", function(d) {
return 6;
})
.on("click", function(d) {
// change the opacity here
d3.select("#rect_" + d.id).style("opacity", 0.5);
})
.attr("class", "circle");
Hope this solves the above problem...

D3.js Combining Candlestick Chart with Line Graph

I am trying to plot a moving average on top of a candlestick chart but the "path" is not appearing completely on the svg canvas that I created.
I have tried looking at several post on how to put a line on top of a bar graph (because I figured it would be similar) but it has not worked.
A couple of the examples and post I have looked at are below:
https://bl.ocks.org/nanu146/f48ffc5ec10270f55c9e1fb3da8b38f0
d3.js How to add lines to a bar chart
D3.js combining bar and line chart
I have all the data in a array.
I am using the same x "scale" for both the candle stick graph and the moving average (line). I have tried using the same y "scale" for both the line and the candlestick but it did not work. Therefore i tried creating 2 scales for y, one for the moving average and one for the candlestick chart. That is what Im doing in my code below.
<script src="https://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript">
var twoHundredDayCandleStickChart = [];
//pulling from 2 properties so must do this way
#for (int i = 0; i != 100; ++i)
{
#:twoHundredDayCandleStickChart.push({date: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Key', high: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Value.high', low: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Value.low', open: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Value.open', close: '#Model.DailyTimeSeriesData.Data.ElementAt(i).Value.close', sma: '#Model.TwoHundredDaySma.Data.ElementAt(i).Value.Sma'})
}
console.log(twoHundredDayCandleStickChart);
var width = 900;
var height = 500;
var margin = 50;
function min(a, b) { return a < b ? a : b; }
function max(a, b) { return a > b ? a : b; }
//y for the candlestick
var y = d3.scaleLinear().range([height - margin, margin]);
var x = d3.scaleTime().range([margin, width - margin]);
//y for the line
var y1 = d3.scaleLinear().range([height - margin, margin]);
//line for the sma
var line1 = d3.line()
.x(function (d) { return x(d["date"]); })
.y(function (d) { return y(d["sma"]); });
function buildChart(data) {
data.forEach(function (d) {
d.date = new Date(d.date);
d.high = +d.high;
d.low = +d.low;
d.open = +d.open;
d.close = +d.close;
d.sma = +d.sma;
});
var chart = d3.select("#twoHundredDaySmaWithCandleStickChart")
.append("svg")
.attr("class", "chart")
.attr("width", width)
.attr("height", height);
//map is going to create an array with all the lows and then d3.min will take the min out of all of them
y.domain([d3.min(data.map(function (x) { return x["low"]; })), d3.max(data.map(function (x) { return x["high"]; }))])
x.domain(d3.extent(data, function (d) { return d["date"]; }))
y1.domain(d3.extent(68, d3.max(data, function (d) { return d["sma"]; })))
//grid for the chart; x and y axis
chart.selectAll("line.x")
.data(x.ticks(10))
.enter().append("line")
.attr("class", "x")
//.text(String)
.attr("x1", x)
.attr("x2", x)
.attr("y1", margin)
.attr("y2", height - margin)
.attr("stroke", "#ccc");
chart.selectAll("line.y")
.data(y.ticks(10))
.enter().append("line")
.attr("class", "y")
.attr("x1", margin)
.attr("x2", width - margin)
.attr("y1", y)
.attr("y2", y)
.attr("stroke", "#ccc");
//x axis
chart.append("g")
.attr("transform", "translate(0," + 450 + ")") //need to change this 450 to a variable- it is how far down the axis will go
.attr("class", "xrule") // give it a class so it can be used to select only xaxis labels or change color
//the x axis
.call(d3.axisBottom(x))
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", function (d) {
return "rotate(-65)"
});
//the y axis
chart.selectAll("text.yrule")
.data(y.ticks(10))
.enter()
.append("text")
.attr("class", "yrule")
.attr("x", 0)
.attr("y", y)
.attr("dy", 0)
.attr("dx", 20)
.attr("text-anchor", "middle")
.text(String);
//add rectangles- if open higher then close then red
chart.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function (d) { return x(d["date"]); })
.attr("y", function (d) { return y(max(d["open"], d["close"])); })
.attr("height", function (d) { return y(min(d["open"], d["close"])) - y(max(d["open"], d["close"])); })
.attr("width", function (d) { return 0.5 * (width - 2 * margin) / data.length; })
.attr("fill", function (d) { return d["open"] > d["close"] ? "red" : "green"; });
//add a stem to the rectangle
chart.selectAll("line.stem")
.data(data)
.enter().append("line")
.attr("class", "stem")
.attr("x1", function (d) { return x(d["date"]) + 0.25 * (width - 2 * margin) / data.length; })
.attr("x2", function (d) { return x(d["date"]) + 0.25 * (width - 2 * margin) / data.length; })
.attr("y1", function (d) { return y(d["high"]); })
.attr("y2", function (d) { return y(d["low"]); })
.attr("stroke", function (d) { return d.open > d.close ? "red" : "green"; });
chart.append("path")
.data([data])
.attr("d", line1)
.attr("class", "line")
.style("stroke", "white")
.attr("fill", "none")
.attr("stroke-width", 2);
}
buildChart(twoHundredDayCandleStickChart);
</script>
The above code is giving me the image below:
The problem in the chart above was my scales! I was taking the domain for the candle stick data but the line data was a lot lower of a min. Therefore the whole graph was not showing up on the scale because the min of the domain had to be adjusted. MANY hours wasted but hopefully this can save someone else time!
d3.select("#twoHundredDaySmaWithCandleStickChart")
Try to change the above code like below
d3.select("svg") or give the div Id

D3js dynamically attach circles into a line with drag and drop

I am very new to d3js v3 and I was trying out a new program where there are lines and the according to the data, circles get embedded into them.
This is what I have so far.
var width = 500,
height = 500;
var animals = ['dog', 'cat', 'bat'];
var fruits = ['apple', 'banana'];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var line1 = svg.append("line")
.attr("x1", 350)
.attr("y1", 5)
.attr("x2", 350)
.attr("y2", 350)
.attr("stroke-width", 2)
.attr("stroke", "black");
var line2 = svg.append("line")
.attr("x1", 80)
.attr("y1", 5)
.attr("x2", 100)
.attr("y2", 350)
.attr("stroke-width", 2)
.attr("stroke", "black");
var animal_scale = d3.scale.ordinal()
.domain(animals)
.rangePoints([5, 350],.2);
var fruit_scale = d3.scale.ordinal()
.domain(fruits)
.rangePoints([5, 350],.2);
var animal_circles = svg.selectAll('circle')
.data(animals)
.enter()
.append('circle')
.attr('cx', function(d) {
// is there a way to calc it automatically according to line 1
})
.attr('cy', function(d) {
return animal_scale(d);
})
.attr('id', function(d) {
return d;
})
.attr('r', 20);
var fruits_circles = svg.selectAll('circle')
.data(fruits)
.enter()
.append('circle')
.attr('cx', function(d) {
// is there a way to calc it automatically according to line 2
})
.attr('cy', function(d) {
return fruit_scale(d);
})
.attr('id', function(d) {
return d;
})
.attr('r', 20);
<!DOCTYPE html>
<html>
<meta charset=utf-8>
<head>
<title></title>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
</body>
</html>
I looked at some sources and being new, its kinda hard to understand most of it. I eventually want to be able to move and drag the circles between lines at the end of the project.There are some issues with the current code, as it does not display the second set of circles too.
Could someone please help me understand further how to do this. It would be a great way for me to learn.
You can select objects by class name and set data. Here is my fast solution for drag-n-drop: jsFiddle. You can modify drag function to add limits to cx position

how to change stack order of text label in JavaScript?

I am trying to plot a network graph using networkD3 in R. I wanted to make some changes to the display so that the text labels (which appears when mouseover) can be easily read.
Please refer to the link here for an example. Note: Jump to the d3ForceNetwork plot.
As seen in the example, the labels are hard to read due to its colour and it often gets obstructed by the surrounding nodes. I have been messing around with the JS file and managed to change the text label color to black. However, having no knowledge of JS or CSS (I can't even tell the difference between the 2 actually), I have no idea how I can change the stack order such that the text labels will always be displayed above any other objects.
Can anyone advise me on how I can achieve the desired outcome?
Below is the full JS file:
HTMLWidgets.widget({
name: "forceNetwork",
type: "output",
initialize: function(el, width, height) {
d3.select(el).append("svg")
.attr("width", width)
.attr("height", height);
return d3.layout.force();
},
resize: function(el, width, height, force) {
d3.select(el).select("svg")
.attr("width", width)
.attr("height", height);
force.size([width, height]).resume();
},
renderValue: function(el, x, force) {
// Compute the node radius using the javascript math expression specified
function nodeSize(d) {
if(options.nodesize){
return eval(options.radiusCalculation);
}else{
return 6}
}
// alias options
var options = x.options;
// convert links and nodes data frames to d3 friendly format
var links = HTMLWidgets.dataframeToD3(x.links);
var nodes = HTMLWidgets.dataframeToD3(x.nodes);
// get the width and height
var width = el.offsetWidth;
var height = el.offsetHeight;
var color = eval(options.colourScale);
// set this up even if zoom = F
var zoom = d3.behavior.zoom();
// create d3 force layout
force
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(options.linkDistance)
.charge(options.charge)
.on("tick", tick)
.start();
// thanks http://plnkr.co/edit/cxLlvIlmo1Y6vJyPs6N9?p=preview
// http://stackoverflow.com/questions/22924253/adding-pan-zoom-to-d3js-force-directed
var drag = force.drag()
.on("dragstart", dragstart)
// allow force drag to work with pan/zoom drag
function dragstart(d) {
d3.event.sourceEvent.preventDefault();
d3.event.sourceEvent.stopPropagation();
}
// select the svg element and remove existing children
var svg = d3.select(el).select("svg");
svg.selectAll("*").remove();
// add two g layers; the first will be zoom target if zoom = T
// fine to have two g layers even if zoom = F
svg = svg
.append("g").attr("class","zoom-layer")
.append("g")
// add zooming if requested
if (options.zoom) {
function redraw() {
d3.select(el).select(".zoom-layer").attr("transform",
"translate(" + d3.event.translate + ")"+
" scale(" + d3.event.scale + ")");
}
zoom.on("zoom", redraw)
d3.select(el).select("svg")
.attr("pointer-events", "all")
.call(zoom);
} else {
zoom.on("zoom", null);
}
// draw links
var link = svg.selectAll(".link")
.data(force.links())
.enter().append("line")
.attr("class", "link")
.style("stroke", function(d) { return d.colour ; })
//.style("stroke", options.linkColour)
.style("opacity", options.opacity)
.style("stroke-width", eval("(" + options.linkWidth + ")"))
.on("mouseover", function(d) {
d3.select(this)
.style("opacity", 1);
})
.on("mouseout", function(d) {
d3.select(this)
.style("opacity", options.opacity);
});
// draw nodes
var node = svg.selectAll(".node")
.data(force.nodes())
.enter().append("g")
.attr("class", "node")
.style("fill", function(d) { return color(d.group); })
.style("opacity", options.opacity)
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("click", click)
.call(force.drag);
node.append("circle")
.attr("r", function(d){return nodeSize(d);})
.style("stroke", "#fff")
.style("opacity", options.opacity)
.style("stroke-width", "1.5px");
node.append("svg:text")
.attr("class", "nodetext")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function(d) { return d.name })
.style("font", options.fontSize + "px " + options.fontFamily)
.style("opacity", options.opacityNoHover)
.style("pointer-events", "none");
function tick() {
node.attr("transform", function(d) {
if(options.bounded){ // adds bounding box
d.x = Math.max(nodeSize(d), Math.min(width - nodeSize(d), d.x));
d.y = Math.max(nodeSize(d), Math.min(height - nodeSize(d), d.y));
}
return "translate(" + d.x + "," + d.y + ")"});
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
}
function mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", function(d){return nodeSize(d)+5;});
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 13)
.style("stroke-width", ".5px")
.style("font", options.clickTextSize + "px ")
.style('fill', 'black')
.style('position','relative')
.style("opacity", 1);
}
function mouseout() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", function(d){return nodeSize(d);});
d3.select(this).select("text").transition()
.duration(1250)
.attr("x", 0)
.style("font", options.fontSize + "px ")
.style("opacity", options.opacityNoHover);
}
function click(d) {
return eval(options.clickAction)
}
// add legend option
if(options.legend){
var legendRectSize = 18;
var legendSpacing = 4;
var legend = svg.selectAll('.legend')
.data(color.domain())
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function(d, i) {
var height = legendRectSize + legendSpacing;
var offset = height * color.domain().length / 2;
var horz = legendRectSize;
var vert = i * height+4;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', color)
.style('stroke', color);
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing)
.style('fill', 'darkOrange')
.text(function(d) { return d; });
}
// make font-family consistent across all elements
d3.select(el).selectAll('text').style('font-family', options.fontFamily);
},
});
I suspect I need to make some changes to the code over here:
function mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.attr("r", function(d){return nodeSize(d)+5;});
d3.select(this).select("text").transition()
.duration(750)
.attr("x", 13)
.style("stroke-width", ".5px")
.style("font", options.clickTextSize + "px ")
.style('fill', 'black')
.style("opacity", 1);
}
You need to resort the node groups holding the circles and text so the currently mouseover'ed one is the last in that group, and thus the last one drawn so it appears on top of the others. See the first answer here -->
Updating SVG Element Z-Index With D3
In your case, if your data doesn't have an id field you may have to use 'name' instead as below (adapted to use the mouseover function you've got):
function mouseover(d) {
d3.selectAll("g.node").sort(function (a, b) {
if (a.name != d.name) return -1; // a is not the hovered element, send "a" to the back
else return 1; // a is the hovered element, bring "a" to the front (by making it last)
});
// your code continues
The pain might be that you have to do this edit for every d3 graph generated by this R script, unless you can edit the R code/package itself. (or you could suggest it to the package author as an enhancement.)

D3: adding a line when clicking on a circle in scatter plot

I have a scatter plot. Now if I click on one of the points, how can I generate a line passing through that point?
I am stuck at two places:
With the following code, why is my line now showing?
<!DOCTYPE html>
<meta charset="utf-8">
<body>
<script src="d3.min.js"></script>
<script>
var width = 500, height = 500;
var randomX=[], randomY=[];
for (var i=0; i<=50; i++) {
randomX[i] = Math.random()*400;
randomY[i] = Math.random()*400;}
var data = randomX.concat(randomY);
var x = d3.scale.linear()
.domain([0, d3.max(randomX)])
.range([0, width]);
var y = d3.scale.linear()
.domain([0, d3.max(randomY)])
.range([height, 0]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g");
svg.selectAll("scatter-dots")
.data(randomY)
.enter().append("svg:circle")
.attr("cy", function(d) {return y(d); } )
.attr("cx", function(d,i) {return x(randomX[i]); } )
.style("fill", "brown")
.attr("r", 5)
.on("click", function(d,i) {
d3.select(this)
.append("svg:line")
.attr("x1", 300).attr("y1", 300)
.attr("x2", 50).attr("y2", 50)
.style("stroke", "steelblue")
.style("stroke-width", 3);
});
</script>
Where is the coordinates of my clicked point stored? I tried this.cx and this.cy, but none of them gave me the actual coordinates.
First, you need to append the line element to the top-level SVG or a g element, not a circle element, otherwise it won't be shown. So in your click handler, you would need to do this:
.on("click", function(d,i) {
svg.append("svg:line")
.attr("x1", 300).attr("y1", 300)
.attr("x2", 50).attr("y2", 50)
.style("stroke", "steelblue")
.style("stroke-width", 3);
});
You can get the coordinates of the click either through d3.event or the coordinates of the circle itself, i.e.
.on("click", function(d,i) {
var x = x(randomX[i]),
y = y(d);
});
or even
.on("click", function(d,i) {
var x = d3.select(this).attr("cx"),
y = d3.select(this).attr("cy");
});

Categories