I have a scatterplot that has zoom functioning perfectly. I am trying to add a tooltip such that on 'mouseenter' on a <circle> element, the tooltip fires. I have this working, i.e the 'mouseenter' event is called but I cannot zoom while the mouse stays on this <circle>. Is there a way to get them to both occur at the same time?
Here is a minimal version of the code that reproduces the issue.
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<button id="resetBtn">Reset</button>
<div id="chart"></div>
<script>
var data = [
{
x: 0.5, y: 0.5
},
{
x: 0.6, y: 0.6
},
{
x: 0.45, y: 0.65
},
{
x: 0.76, y: 0.61
},
{
x: 0.51, y: 0.05
},
{
x: 0.16, y: 6.8
}
];
var plot = volcanoPlot()
.xColumn("x")
.yColumn("y");
d3.select('#chart')
.data([data])
.call(plot);
function volcanoPlot() {
var width = 960,
height = 500,
margin = {top: 20, right: 20, bottom: 40, left: 50},
xColumn,
dotRadius = 10,
yColumn,
xScale = d3.scaleLinear(),
yScale = d3.scaleLog();
function chart(selection){
var innerWidth = width - margin.left - margin.right, // set the size of the chart within its container
innerHeight = height - margin.top - margin.bottom;
selection.each(function(data) {
xScale.range([0, innerWidth])
.domain(d3.extent(data, function(d) { return d[xColumn]; }))
.nice();
yScale.range([0, innerHeight])
.domain(d3.extent(data, function(d) { return d[yColumn]; }))
.nice();
var zoom = d3.zoom()
.scaleExtent([1, 20])
.translateExtent([[0, 0], [width, height]])
.on('zoom', zoomFunction);
var svg = d3.select(this).append('svg')
.attr('height', height)
.attr('width', width)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
d3.select('#resetBtn')
.style('top', margin.top * 1.5 + 'px')
.style('left', margin.left * 1.25 + 'px')
.on('click', reset);
svg.append('defs').append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('height', innerHeight)
.attr('width', innerWidth);
// add the axes
var xAxis = d3.axisBottom(xScale);
var yAxis = d3.axisLeft(yScale);
var gX = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + innerHeight + ')')
.call(xAxis);
gX.append('text')
.attr('class', 'label')
.attr('transform', 'translate(' + width / 2 + ',' + (margin.bottom - 6) + ')')
.attr('text-anchor', 'middle')
.text(xColumn);
var gY = svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
gY.append('text')
.attr('class', 'label')
.attr('transform', 'translate(' + (0 - margin.left / 1.5) + ',' + (height / 2) + ') rotate(-90)')
.style('text-anchor', 'middle')
.text(yColumn);
var zoomBox = svg.append('rect')
.attr('class', 'zoom')
.attr('height', innerHeight)
.attr('width', innerWidth)
.attr('fill', 'none')
.attr('pointer-events', 'all')
.call(zoom);
var circles = svg.append('g')
.attr('class', 'circlesContainer')
.attr('clip-path', 'url(#clip)');
circles.selectAll(".dot")
.data(data)
.enter().append('circle')
.attr('r', dotRadius)
.attr('cx', function(d) { return xScale(d[xColumn]); })
.attr('cy', function(d) { return yScale(d[yColumn]); })
.attr('class', 'dot')
.attr('stroke', 'none')
.on('mouseenter', function(){
console.log("hi");
});
function zoomFunction() {
var transform = d3.zoomTransform(this);
d3.selectAll('.dot')
.attr('transform', transform)
.attr('r', dotRadius / Math.sqrt(transform.k));
gX.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));
gY.call(yAxis.scale(d3.event.transform.rescaleY(yScale)));
}
function reset() {
var ease = d3.easePolyIn.exponent(4.0);
d3.select('.zoom')
.transition().duration(750)
.ease(ease)
.call(zoom.transform, d3.zoomIdentity);
}
});
}
chart.width = function(value) {
if (!arguments.length) return width;
width = value;
return chart;
};
chart.height = function(value) {
if (!arguments.length) return height;
height = value;
return chart;
};
chart.margin = function(value) {
if (!arguments.length) return margin;
margin = value;
return chart;
};
chart.xColumn = function(value) {
if (!arguments.length) return xColumn;
xColumn = value;
return chart;
};
chart.yColumn = function(value) {
if (!arguments.length) return yColumn;
yColumn = value;
return chart;
};
return chart;
}
</script>
</body>
</html>
You're calling your zoom function on the rectangle and, because of that, the zoom will not work when you hover over the circles. It has nothing to do with the mouseenter function: you can remove the mouseenter and the zoom still won't work when you hover over the circles.
A simple solution is calling the zoom on your SVG group, not on the rectangle:
var svg = d3.select(this).append('svg')
.attr('height', height)
.attr('width', width)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.call(zoom);
Here is your code with that change only:
<!DOCTYPE html>
<html lang="en">
<head>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<button id="resetBtn">Reset</button>
<div id="chart"></div>
<script>
var data = [
{
x: 0.5, y: 0.5
},
{
x: 0.6, y: 0.6
},
{
x: 0.45, y: 0.65
},
{
x: 0.76, y: 0.61
},
{
x: 0.51, y: 0.05
},
{
x: 0.16, y: 6.8
}
];
var plot = volcanoPlot()
.xColumn("x")
.yColumn("y");
d3.select('#chart')
.data([data])
.call(plot);
function volcanoPlot() {
var width = 960,
height = 500,
margin = {top: 20, right: 20, bottom: 40, left: 50},
xColumn,
dotRadius = 10,
yColumn,
xScale = d3.scaleLinear(),
yScale = d3.scaleLog();
function chart(selection){
var innerWidth = width - margin.left - margin.right, // set the size of the chart within its container
innerHeight = height - margin.top - margin.bottom;
selection.each(function(data) {
xScale.range([0, innerWidth])
.domain(d3.extent(data, function(d) { return d[xColumn]; }))
.nice();
yScale.range([0, innerHeight])
.domain(d3.extent(data, function(d) { return d[yColumn]; }))
.nice();
var zoom = d3.zoom()
.scaleExtent([1, 20])
.translateExtent([[0, 0], [width, height]])
.on('zoom', zoomFunction);
var svg = d3.select(this).append('svg')
.attr('height', height)
.attr('width', width)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.call(zoom);
d3.select('#resetBtn')
.style('top', margin.top * 1.5 + 'px')
.style('left', margin.left * 1.25 + 'px')
.on('click', reset);
svg.append('defs').append('clipPath')
.attr('id', 'clip')
.append('rect')
.attr('height', innerHeight)
.attr('width', innerWidth);
// add the axes
var xAxis = d3.axisBottom(xScale);
var yAxis = d3.axisLeft(yScale);
var gX = svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + innerHeight + ')')
.call(xAxis);
gX.append('text')
.attr('class', 'label')
.attr('transform', 'translate(' + width / 2 + ',' + (margin.bottom - 6) + ')')
.attr('text-anchor', 'middle')
.text(xColumn);
var gY = svg.append('g')
.attr('class', 'y axis')
.call(yAxis);
gY.append('text')
.attr('class', 'label')
.attr('transform', 'translate(' + (0 - margin.left / 1.5) + ',' + (height / 2) + ') rotate(-90)')
.style('text-anchor', 'middle')
.text(yColumn);
var zoomBox = svg.append('rect')
.attr('class', 'zoom')
.attr('height', innerHeight)
.attr('width', innerWidth)
.attr('fill', 'none')
.attr('pointer-events', 'all');
var circles = svg.append('g')
.attr('class', 'circlesContainer')
.attr('clip-path', 'url(#clip)');
circles.selectAll(".dot")
.data(data)
.enter().append('circle')
.attr('r', dotRadius)
.attr('cx', function(d) { return xScale(d[xColumn]); })
.attr('cy', function(d) { return yScale(d[yColumn]); })
.attr('class', 'dot')
.attr('stroke', 'none')
.on('mouseenter', function(){
console.log("hi");
});
function zoomFunction() {
var transform = d3.zoomTransform(this);
d3.selectAll('.dot')
.attr('transform', transform)
.attr('r', dotRadius / Math.sqrt(transform.k));
gX.call(xAxis.scale(d3.event.transform.rescaleX(xScale)));
gY.call(yAxis.scale(d3.event.transform.rescaleY(yScale)));
}
function reset() {
var ease = d3.easePolyIn.exponent(4.0);
d3.select('.zoom')
.transition().duration(750)
.ease(ease)
.call(zoom.transform, d3.zoomIdentity);
}
});
}
chart.width = function(value) {
if (!arguments.length) return width;
width = value;
return chart;
};
chart.height = function(value) {
if (!arguments.length) return height;
height = value;
return chart;
};
chart.margin = function(value) {
if (!arguments.length) return margin;
margin = value;
return chart;
};
chart.xColumn = function(value) {
if (!arguments.length) return xColumn;
xColumn = value;
return chart;
};
chart.yColumn = function(value) {
if (!arguments.length) return yColumn;
yColumn = value;
return chart;
};
return chart;
}
</script>
</body>
</html>
Related
I need to change the range for yAxis ticks in my d3 chart. Currently, it has a range of 2M and looks like this:
The majority of data is spread between 0 and 4M. I would like to have a smaller range for ticks up until 4M and then a bigger range. So I would have ticks for 1M, 2M, 3M, 4M and then 8M, 12M, 16M. Here is the code:
const width = 1000;
const height = 700;
const padding = 120; // between title and svg borders
const marginTop = 120;
// Min and max for y axis for Revenue values
const maxRevenue = d3.max(dataset, (d) => d[1]);
const minRevenue = d3.min(dataset, (d) => d[1]);
const yScale = d3
.scaleLinear()
.domain([minRevenue, maxRevenue])
.range([height - padding, marginTop]);
const yAxis = d3.axisLeft(yScale);
svg
.append("g")
.attr("transform", `translate(${padding}, 0)`)
.call(yAxis); // if need to move the chart to the right (for yaxis)
My recommendation is to use a "split" y-axis. Something like this:
<html>
<meta charset="utf-8" />
<style>
/* set the CSS */
.bar {
fill: steelblue;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v6.min.js"></script>
<script>
// set the dimensions and margins of the graph
var margin = { top: 20, right: 20, bottom: 30, left: 100 },
width = 500 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
pbottomsize = 0.66,
ptopsize = 0.25;
// set the ranges
var xd = ['A', 'B', 'C', 'D'];
var x = d3.scaleBand().range([0, width]).padding(0.1).domain(xd);
var y = d3.scaleLinear().range([height * pbottomsize, 0]).domain([0, 20]);
var y1 = d3.scaleLinear().range([height * ptopsize, 0]).domain([1000000, 2000000]);
var svg = d3
.select('body')
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
var g1 = svg
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + (margin.top + height * 0.33) + ')');
var g2 = svg
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var data = d3.range(1, 100).map(function (d, i) {
return {
x: xd[Math.floor(Math.random() * xd.length)],
y: Math.random() < 0.75 ? Math.random() * 20 : (Math.random() + 1) * 1000000,
};
});
var data0 = data.filter(function(d){
return d.y < 1000000;
});
var data1 = data.filter(function(d){
return d.y > 1000000;
})
// append the rectangles for the bar chart
g1
.selectAll('.circle')
.data(data0)
.enter()
.append('circle')
.attr('cx', function (d) {
return x(d.x) + (x.bandwidth() / 2);
})
.attr('r', 10)
.attr('cy', function (d) {
return y(d.y);
})
.style('fill', 'steelblue');
g2
.selectAll('.circle')
.data(data1)
.enter()
.append('circle')
.attr('cx', function (d) {
return x(d.x) + (x.bandwidth() / 2);
})
.attr('r', 10)
.attr('cy', function (d) {
return y1(d.y);
})
.style('fill', 'red');
// add the x Axis
g1
.append('g')
.attr('transform', 'translate(0,' + (height * pbottomsize) + ')')
.call(d3.axisBottom(x));
// add the y Axis
g1.append('g').call(d3.axisLeft(y));
g2.append('g').call(d3.axisLeft(y1).ticks(3));
var g3 = svg.append('g')
.attr('transform', 'translate(' + 70.5 + ',' + 136 + ')')
g3.append('path')
.attr('d', 'M 10 10 Q 20 0, 30 10 T 50 10 M 30 10 L 30 -2')
.attr('stroke', 'black')
.attr('fill', 'none');
g3.append('path')
.attr('d', 'M 10 20 Q 20 10, 30 20 T 50 20 M 30 20 L 30 32')
.attr('stroke', 'black')
.attr('fill', 'none');
</script>
</body>
</html>
I have a line graph with two linear paths. Everything works great, only when I select area on graph, the lines goes over x-axis and also y-axis:
Please help me finding the issue. Following is my code:
var data = JSON.parse("[{\"time\": 1496511413,\"correlation\": 0.1,\"offset\": 8104}, {\"time\": 1496511414,\"correlation\": 0.2,\"offset\": 8105},{\"time\": 1496511415,\"correlation\": 0.4,\"offset\": 8106},{\"time\": 1496511416,\"correlation\": 0.5,\"offset\": 8107},{\"time\": 1496511417,\"correlation\": 0.7,\"offset\": 8120},{\"time\": 1496511418,\"correlation\": 0.8,\"offset\": 8120},{\"time\": 1496511419,\"correlation\": 0.3,\"offset\": 8108},{\"time\": 1496511420,\"correlation\": 0.6,\"offset\": 8109},{\"time\": 1496511421,\"correlation\": 0.9,\"offset\": 8110}]");
console.log(data);
var margin = {top: 30, right: 40, bottom: 30, left: 50},
width = 700 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// Scale axis
var timeScale = d3.scaleTime().range([0, width]);
var corrScale = d3.scaleLinear().range([height, 0]);
var offsetScale = d3.scaleLinear().range([height, 0]);
// Define Axes
var timeAxis = d3.axisBottom(timeScale).ticks(5);
var corrAxis = d3.axisLeft(corrScale).ticks(5);
var offsetAxis = d3.axisRight(offsetScale).ticks(5);
// Define Lines
var corrLine = d3.line()
.x(function(d) { return timeScale(d.time); })
.y(function(d) { return corrScale(d.correlation); });
var offsetLine = d3.line()
.x(function(d) { return timeScale(d.time); })
.y(function(d) { return offsetScale(d.offset); });
// plot graph
function plot_graph() {
data.forEach(function(d) {
d.time = new Date(d.time * 1000);
d.correlation = +d.correlation;
d.offset = +d.offset / 1000;
});
console.log(data);
// Define brush
var brush = d3.brush()
.extent([[0, 0], [width, height]])
.on('end', brushEnded),
idleTimeout,
idleDelay = 350;
//Add domain for scale x-y axis
timeScale.domain(d3.extent(data, function(d) { return d.time; }));
corrScale.domain(d3.extent(data, function(d) { return d.correlation; }));
offsetScale.domain(d3.extent(data, function(d) { return d.offset; }));
// create svg element
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')
.attr('class', 'time axis')
.attr("transform", "translate(0," + height + ")")
.call(timeAxis);
svg.append('g')
.attr('class', 'corr axis')
.style('fill', 'steelblue')
.call(corrAxis);
svg.append('g')
.attr('class', 'offset axis')
.attr('transform', 'translate(' + width + ', 0)')
.style('fill', 'red')
.call(offsetAxis);
var focus = svg.append('g')
.attr("clip-path", "url(#clip)");
focus.append('svg:path')
.datum(data)
.attr('class', 'corr line')
.attr('d', corrLine);
focus.append('svg:path')
.datum(data)
.attr('class', 'offset line')
.style('stroke', 'red')
.attr('d', offsetLine);
focus.append('g')
.attr('class', 'brush')
.call(brush);
function idled() {
idleTimeout = null;
}
function brushEnded() {
var s = d3.brushSelection(d3.select('.brush').node());
if (!s) {
if (!idleTimeout) return idleTimeout = setTimeout(idled, idleDelay);
timeScale.domain(d3.extent(data, function(d) { return d.time; }));
corrScale.domain(d3.extent(data, function(d) { return d.correlation; }));
offsetScale.domain(d3.extent(data, function(d) { return d.offset; }));
} else {
console.log(s);
console.log([s[0][0], s[1][0]].map(timeScale.invert, timeScale));
console.log([s[1][1], s[0][1]].map(offsetScale.invert, offsetScale));
console.log([s[1][1], s[0][1]].map(corrScale.invert, corrScale));
timeScale.domain([s[0][0], s[1][0]].map(timeScale.invert, timeScale));
corrScale.domain([s[1][1], s[0][1]].map(corrScale.invert, corrScale));
offsetScale.domain([s[1][1], s[0][1]].map(offsetScale.invert, offsetScale));
svg.select(".brush").call(brush.move, null);
}
zoom();
}
function zoom() {
var t = svg.transition()
.duration(750);
svg.select('.time.axis').transition(t).call(timeAxis);
svg.select('.corr.axis').transition(t).call(corrAxis);
svg.select('.offset.axis').transition(t).call(offsetAxis);
console.log(timeScale(0));
console.log(corrScale(0));
console.log(corrLine(data));
svg.select('path.corr.line')
.transition(t)
.attr('d', corrLine);
svg.select('path.offset.line')
.transition(t)
.attr('d', offsetLine);
}
}
I should have a white border when I select any bar in my d3 bar chart. So here the border is achieved using stroke, but the bottom border is getting hidden under the x domain line.
// container size
var margin = {top: 10, right: 10, bottom: 30, left: 30},
width = 400,
height = 300;
var data = [
{"month":"DEC","setup":{"count":26,"id":1,"label":"Set Up","year":"2016","graphType":"setup"}},
{"month":"JAN","setup":{"count":30,"id":1,"label":"Set Up","year":"2017","graphType":"setup"}},
{"month":"FEB","setup":{"count":30,"id":1,"label":"Set Up","year":"2017","graphType":"setup"}}];
var name = 'dashboard';
// x scale
var xScale = d3.scale.ordinal()
.rangeRoundBands([0, width], 0.2);
// set x and y scales
xScale.domain(data.map(function(d) { return d.month; }));
// x axis
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom')
.outerTickSize(0);
var yScale = d3.scale.linear()
.domain([0, d3.max(data, function(d) {
return d.setup.count;
})])
.range([height, 0]);
var ticks = yScale.ticks(),
lastTick = ticks[ticks.length-1];
var newLastTick = lastTick + (ticks[1] - ticks[0]);
if (lastTick < yScale.domain()[1]){
ticks.push(lastTick + (ticks[1] - ticks[0]));
}
// adjust domain for further value
yScale.domain([yScale.domain()[0], newLastTick]);
// y axis
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(-width, 0, 0)
.tickFormat(d3.format('d'))
.tickValues(ticks);
// create svg container
var svg = d3.select('#chart')
.append('svg')
.attr('class','d3-setup-barchart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
//.on('mouseout', tip.hide);
// apply tooltip
//svg.call(tip);
// Horizontal grid (y axis gridline)
svg.append('g')
.attr('class', 'grid horizontal')
.call(d3.svg.axis()
.scale(yScale)
.orient('left')
.tickSize(-width, 0, 0)
.tickFormat('')
.tickValues(ticks)
);
// create bars
var bars = svg.selectAll('.bar')
.data(data)
.enter()
.append('g');
bars.append('rect')
.attr('class', function(d,i) {
return 'bar';
})
.attr('id', function(d, i) {
return name+'-bar-'+i;
})
.attr('x', function(d) { return xScale(d.month); })
.attr('width', xScale.rangeBand())
.attr('y', function(d) { return yScale(d.setup.count); })
.attr('height', function(d) { return height - yScale(d.setup.count); })
.on('click', function(d, i) {
d3.select(this.nextSibling)
.classed('label-text selected', true);
d3.select(this)
.classed('bar selected', true);
d3.select('#'+name+'-axis-text-'+i)
.classed('axis-text selected', true);
});
//.on('mouseover', tip.show)
//.on('mouseout', tip.hide);
// apply text at the top
bars.append('text')
.attr('class',function(d,i) {
return 'label-text';
})
.attr('x', function(d) { return xScale(d.month) + (xScale.rangeBand()/2) - 10; })
.attr('y', function(d) { return yScale(d.setup.count) + 2 ; })
.attr('transform', function() { return 'translate(10, -10)'; })
.text(function(d) { return d.setup.count; });
// draw x axis
svg.append('g')
.attr('id', name+'-x-axis')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
// apply class & id to x-axis texts
d3.select('#'+name+'-x-axis')
.selectAll('text')
.attr('class', function(d,i) {
return 'axis-text';
})
.attr('id', function(d,i) { return name+'-axis-text-' + i; });
// draw y axis
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');
// remove 0 in y axis
svg.select('.y')
.selectAll('.tick')
.filter(function (d) {
return d === 0 || d % 1 !== 0;
}).remove();
svg
.select('.horizontal')
.selectAll('.tick')
.filter(function (d) {
return d === 0 || d % 1 !== 0;
}).remove();
JSFiddle
In a SVG, whoever is painted last stays on top.
That being said, simply append your x axis...
svg.append('g')
.attr('id', name + '-x-axis')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
... before the bars:
var bars = svg.selectAll('.bar')
.data(data)
.enter()
.append('g');
Here is your updated fiddle: https://jsfiddle.net/5bnzt6nb/
I'm rather a beginner at both JS and D3.js; my JSFiddle is here.
JSHint shows no errors, so I think I'm doing the D3 wrong.
I'm trying to do the same thing as in these questions, adapting a Tributary example to run outside of that platform: "Exporting" a Tributary example that makes use of the tributary object - d3.js and Getting horizontal stack bar example to display using d3.js (I'm even adapting the same code as the latter) Unfortunately, there is no corrected JSFiddle or other example in either answer
Here's my code:
var VanillaRunOnDomReady = function() {
var margins = {
top: 12,
left: 48,
right: 24,
bottom: 24
};
var data = [
{"key":"Nonviolent", "cat1":0.69, "cat2":0.21, "cat3":0.10},
{"key":"Violent", "cat1":0.53, "cat2":0.29, "cat3":0.18}
];
var catnames = {cat1: "No mental illness",
cat2: "Mild mental illness",
cat3: "Moderate or severe mental illness"};
var svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
var n = 3, // number of layers
m = data.length, // number of samples per layer
stack = d3.layout.stack(),
labels = data.map(function(d) {return d.key;}),
//go through each layer (cat1, cat2 etc, that's the range(n) part)
//then go through each object in data and pull out that objects's catulation data
//and put it into an array where x is the index and y is the number
layers = stack(d3.range(n).map(function(d) {
var a = [];
for (var i = 0; i < m; ++i) {
a[i] = {x: i, y: data[i]['cat' + (d+1)]};
}
return a;
})),
//the largest single layer
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
//the largest stack
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var margin = {top: 40, right: 10, bottom: 20, left: 50},
width = 677 - margin.left - margin.right,
height = 533 - margin.top - margin.bottom;
var y = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([2, height], 0.08);
var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.x); })
.attr("x", function(d) { return x(d.y0); })
.attr("height", y.rangeBand())
.attr("width", function(d) { return x(d.y); });
var yAxis = d3.svg.axis()
.scale(y)
.tickSize(1)
.tickPadding(6)
.tickValues(labels)
.orient("left");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
};
I think you are just missing a (); at the end of your function and before the semicolon. That would make it self executing. I forked your fiddle.
var VanillaRunOnDomReady = function() {
var margins = {
top: 12,
left: 48,
right: 24,
bottom: 24
};
var data = [
{"key":"Nonviolent", "cat1":0.69, "cat2":0.21, "cat3":0.10},
{"key":"Violent", "cat1":0.53, "cat2":0.29, "cat3":0.18}
];
var catnames = {cat1: "No mental illness",
cat2: "Mild mental illness",
cat3: "Moderate or severe mental illness"};
var svg = d3.select('body')
.append('svg')
.attr('width', width + margins.left + margins.right)
.attr('height', height + margins.top + margins.bottom)
.append('g')
.attr('transform', 'translate(' + margins.left + ',' + margins.top + ')');
var n = 3, // number of layers
m = data.length, // number of samples per layer
stack = d3.layout.stack(),
labels = data.map(function(d) {return d.key;}),
//go through each layer (cat1, cat2 etc, that's the range(n) part)
//then go through each object in data and pull out that objects's catulation data
//and put it into an array where x is the index and y is the number
layers = stack(d3.range(n).map(function(d) {
var a = [];
for (var i = 0; i < m; ++i) {
a[i] = {x: i, y: data[i]['cat' + (d+1)]};
}
return a;
})),
//the largest single layer
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
//the largest stack
yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });
var margin = {top: 40, right: 10, bottom: 20, left: 50},
width = 677 - margin.left - margin.right,
height = 533 - margin.top - margin.bottom;
var y = d3.scale.ordinal()
.domain(d3.range(m))
.rangeRoundBands([2, height], 0.08);
var x = d3.scale.linear()
.domain([0, yStackMax])
.range([0, width]);
var color = d3.scale.linear()
.domain([0, n - 1])
.range(["#aad", "#556"]);
var svg = d3.select("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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.x); })
.attr("x", function(d) { return x(d.y0); })
.attr("height", y.rangeBand())
.attr("width", function(d) { return x(d.y); });
var yAxis = d3.svg.axis()
.scale(y)
.tickSize(1)
.tickPadding(6)
.tickValues(labels)
.orient("left");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
}();
Im using d3 line chart and created cross hair with value on it.
Facing issue when zoom in.
Cross hair not updated with new zoomed value. its have the old value and shows the value as first.
How do i fix this, and make cross hair with zoomed values
Here is my code:
var self = this;
this.margin = { top: 0, right: 45, bottom: 20, left: 0 };
this.size = {
width: $('#realtimechart').width() - 40,
height: 300
};
$('#realtimechart').empty();
this.x = d3.time.scale.utc()
.domain(d3.extent(self.plots, function (d) {
return d.date;
}))
.range([0, this.size.width])
.clamp(true);
this.y = d3.scale.linear()
.domain([0, d3.max(self.plots, function (d) {
return d.close;
})])
.range([this.size.height, 0])
.clamp(true);
this.line = d3.svg.line()
.x(function (d) {
return self.x(d.date);
})
.y(function (d) {
return self.y(d.close);
});
this.xAxis = d3.svg.axis().scale(this.x).ticks(12).orient('bottom');
this.yAxis = d3.svg.axis().scale(this.y).ticks(8).orient('right');
this.zoom = d3.behavior.zoom().x(this.x).scaleExtent([1,10]).on('zoom', this.redraw());
this.zoom.scale(currentZoom).translate([0, 0]);
this.svg = d3.select('#realtimechart').append('svg')
.attr('width', this.size.width + this.margin.left + this.margin.right)
.attr('height', this.size.height + this.margin.top + this.margin.bottom)
.append('g')
.attr('transform', 'translate(' + this.margin.left + ',' + this.margin.top + ')')
.call(this.zoom);
this.makeX = function () {
return d3.svg.axis()
.scale(self.x)
.orient('bottom')
.ticks(12);
};
this.makeY = function () {
return d3.svg.axis()
.scale(self.y)
.orient('left')
.ticks(8);
};
this.svg.append('rect')
.attr('width', this.size.width)
.attr('height', this.size.height);
var focus = this.svg.append("g")
.attr("class","focus")
.style("display", "none");
focus.append("line")
.attr({
"x1": -this.size.width,
"y1": 0,
"x2": this.size.width,
"y2": 0
});
focus.append("line")
.attr({
"x1": 0,
"y1": -this.size.height,
"x2": 0,
"y2": this.size.height
});
var tipValue = this.svg.append("g")
.attr("class","focus")
.style("display", "none");
tipValue.append("text")
.attr("x", -10);
var bisectDate = d3.bisector(function(d) { return d.date; }).left;
var formatValue = d3.format(",.2f");
var formatCurrency = function(d) { return "$" + formatValue(d); };
var xDomain = d3.extent(self.chartData, function (d, i){ return d.date; });
var yMin = d3.min(self.chartData, function(d){ return Math.min(d.close); });
var yMax = d3.max(self.chartData, function(d){ return Math.max(d.close); });
var x = d3.time.scale()
.range([0, this.size.width]);
var y = d3.scale.linear()
.range([this.size.height, 0]);
x.domain([self.chartData[0].date, self.chartData[self.chartData.length - 1].date]);
y.domain(d3.extent(self.chartData, function(d) { return d.close; }));
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(self.chartData, x0, 1),
d0 = self.chartData[i - 1],
d1 = self.chartData[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.date) + "," + y(d.close) + ")");
tipValue.attr("transform", "translate(10, 10)");
tipValue.select("text").text(formatCurrency(d.close));
}
this.svg.on('mouseover', function() {
focus.style("display", null);
tipValue.style("display", null);
}).on('mouseout', function() {
focus.style("display", "none");
tipValue.style("display", "none");
}).on('mousemove', mousemove);
this.svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + this.size.height + ')')
.call(this.xAxis);
this.svg.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(' + this.size.width + ', 0)')
.call(this.yAxis);
this.svg.append('g')
.attr('class', 'x grid')
.attr('transform', 'translate(0,' + this.size.height + ')')
.call(this.makeX()
.tickSize(-this.size.height, 0, 0)
.tickFormat(''));
this.svg.append('g')
.attr('class', 'y grid')
.call(this.makeY()
.tickSize(-this.size.width, 0, 0)
.tickFormat(''));
this.clip = this.svg.append('svg:clipPath')
.attr('id', 'clip')
.append('svg:rect')
.attr('x', 0)
.attr('y', 0)
.attr('width', this.size.width)
.attr('height', this.size.height);
this.chartBody = this.svg.append('g')
.attr('clip-path', 'url(#clip)')
.append('svg:path')
.datum(self.plots)
.attr('class', 'line')
.attr('d', this.line);
}
Chart.prototype.redraw = function() {
var self = this;
return function() {
currentZoom = d3.event.scale;
self.svg.select('.x.axis').call(self.xAxis);
self.svg.select('.y.axis').call(self.yAxis);
self.svg.select('.x.grid')
.call(self.makeX()
.tickSize(-self.size.height, 0, 0)
.tickFormat(''));
self.svg.select('.y.grid')
.call(self.makeY()
.tickSize(-self.size.width, 0, 0)
.tickFormat(''));
self.svg.select('.line')
.attr('class', 'line')
.attr('d', self.line);
};