d3 rect in one group interfering with rect in another group - d3.js

I have a group called groove which has two rects. These are not bound to the data.
I also have a group called group which has many rects which are bound to the data.
In the second group called group there are only three data points, but only two are displaying.
Why is the first one not being rendered?
I have seen this before but can't remember how to solve it.
var margin = {
top: 20,
right: 20,
bottom: 20,
left: 20
},
width = 600 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var svg = d3.select("svg");
var data = [{
x: 200
},
{
x: 300
},
{
x: 400
}
];
var groove = svg.append("g")
.attr("class", "groove_group");
groove.append("rect")
.attr("x", 100)
.attr("y", 150)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 6)
.attr("width", 800)
.style("fill", "grey");
groove.append("rect")
.attr("x", 102)
.attr("y", 152)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 2)
.attr("width", 796)
.style("fill", "black");
// create group
var group = svg.selectAll("g")
.data(data)
.enter().append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
group.append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 100)
.attr("height", 100)
.attr("width", 15)
.style("fill", "lightblue")
.attr('id', function(d, i) {
return 'handle_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black");
group.append("text")
.attr("x", function(d) {
return d.x
})
.attr("y", 100)
.attr("text-anchor", "start")
.style("fill", "black")
.text(function(d) {
return "x:" + d.x
});
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this).select("text")
.attr("x", d.x = d3.event.x);
d3.select(this).select("rect")
.attr("x", d.x = d3.event.x);
}
function dragended(d) {
d3.select(this)
.classed("active", false);
}
function removeElement(d) {
d3.event.stopPropagation();
data = data.filter(function(e) {
return e != d;
});
d3.select(this)
.remove();
}
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.min.js"></script>

When you do this:
var group = svg.selectAll("g")
You are selecting a previously existing group (which is groove). Therefore, your enter selection has one less element.
Solution
Select nothing:
var group = svg.selectAll(null)
Here is your code with that change:
var margin = {
top: 20,
right: 20,
bottom: 20,
left: 20
},
width = 600 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom;
var svg = d3.select("svg");
var data = [{
x: 200
}, {
x: 300
}, {
x: 400
}];
var groove = svg.append("g")
.attr("class", "groove_group");
groove.append("rect")
.attr("x", 100)
.attr("y", 150)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 6)
.attr("width", 800)
.style("fill", "grey");
groove.append("rect")
.attr("x", 102)
.attr("y", 152)
.attr("rx", 2)
.attr("ry", 2)
.attr("height", 2)
.attr("width", 796)
.style("fill", "black");
// create group
var group = svg.selectAll(null)
.data(data)
.enter().append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.on("click", removeElement);
group.append("rect")
.attr("x", function(d) {
return d.x;
})
.attr("y", 100)
.attr("height", 100)
.attr("width", 15)
.style("fill", "lightblue")
.attr('id', function(d, i) {
return 'handle_' + i;
})
.attr("rx", 6)
.attr("ry", 6)
.attr("stroke-width", 2)
.attr("stroke", "black");
group.append("text")
.attr("x", function(d) {
return d.x
})
.attr("y", 100)
.attr("text-anchor", "start")
.style("fill", "black")
.text(function(d) {
return "x:" + d.x
});
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this).select("text")
.attr("x", d.x = d3.event.x);
d3.select(this).select("rect")
.attr("x", d.x = d3.event.x);
}
function dragended(d) {
d3.select(this)
.classed("active", false);
}
function removeElement(d) {
d3.event.stopPropagation();
data = data.filter(function(e) {
return e != d;
});
d3.select(this)
.remove();
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="960" height="500"></svg>

Related

d3.js plot not displaying within R Shiny plot area

First, let me accept that i am new to d3.js. I am fairly accustomed to R. I have been doing plot in d3.js, thanks to the stack overflow community and some may d3.js example. I came across r2d3 as a mode to connect R and d3.js plots and hence have used the same to build a connection.
I have created a plot in d3.js and wanted to connect it with the R Shiny output. I am able to connect the plot to the R Shiny. But the plot is always coming out of the shiny plot area.
This is How my plot looks in the R Shiny area :
My Existing d3 plot in R Shiny
Requesting your suggestions on the following :
How to fix the d3.js plot within the R Shiny Area.
My Ui.R code is as below :
column(d3Output("clplot"),width = 12)
My server code is as below :
output$clplot <-r2d3::renderD3(
r2d3(data = cl_d3(),script="d3/cl_dilip_v1.js", d3_version = "5")
)
The js code is attached as below :
var margin = { top: 20, right: 20, bottom: 30, left: 50 },
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var jsondata = [{ "promotedprice": 100, "nonpromotedprice": 350, "avgprice": 230, "x_value": 80, "brand": "Brand1" }, { "promotedprice": 99, "nonpromotedprice": 170, "avgprice": 130, "x_value": 140, "brand": "Brand2" }, { "promotedprice": 47, "nonpromotedprice": 147, "avgprice": 80, "x_value": 200, "brand": "Brand3" }, { "promotedprice": 100, "nonpromotedprice": 250, "avgprice": 220, "x_value": 260, "brand": "Brand4" }, { "promotedprice": 99, "nonpromotedprice": 170, "avgprice": 130, "x_value": 320, "brand": "Brand5" }];
// Creating the colour Category
var color = d3.scaleOrdinal(d3.schemeCategory10);
// Creating the 1st Comapartment
var svg = d3.select("body").append("svg")
.attr("width", 400)
.attr("height", 450);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
// Attach the Promoted Price Rectangle
var g = svg.selectAll("rect")
.data(data)
.enter()
.append("g")
.classed('rect', false)
.on("mouseover", function (d) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(formatTime(d.x_value) + "<br/>" + d.nonpromotedprice);
//.style("left", (d3.event.pageX) + "px")
//.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function (d) {
div.transition()
.duration(0)
.style("opacity", 0);
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var accent = d3.scaleOrdinal(d3.schemeAccent);
// Line for the 1st Block
g.append("line") // attach a line
.style("stroke", "#E6EAEE")
.style("stroke-width", 17) // colour the line
.attr("x1", function (d) { return d.x_value; }) // x position of the first end of the line
.attr("y1", function (d) { return d.nonpromotedprice; }) // y position of the first end of the line
.attr("x2", function (d) { return d.x_value; }) // x position of the second end of the line
.attr("y2", function (d) { return d.promotedprice; });
// Promoted Price Rectangle for the 1st Block
g.append("rect")
.attr("width", 24)
.attr("height", 13)
.attr("x", function (d) { return d.x_value - 12; })
.attr("y", function (d) { return d.promotedprice; })
.attr("fill", function (d) { return color(d.x_value); })
// Non Promoted Price Rectangle for the 1st Block
g.append("rect")
.attr("width", 24)
.attr("height", 13)
.attr("x", function (d) { return d.x_value - 12; })
.attr("y", function (d) { return d.nonpromotedprice; })
.attr("fill", function (d) { return color(d.x_value); })
// Average Price Rectangle for the 1st Block
g.append("rect")
.attr("width", 24)
.attr("height", 13)
.attr("x", function (d) { return d.x_value - 12; })
.attr("y", function (d) { return d.avgprice; })
.attr("fill", function (d) { return color(d.x_value); });
// Graph X- Axis and Title Text for 1st svg
var y_scale = d3.scaleLinear()
//.domain([d3.min(function (d) { return d.promotedprice }), d3.max(function (d) { return d.nonpromotedprice; })])
.range([370, 0]);
var y_axis = d3.axisLeft()
.scale(y_scale);
y_scale.domain([0, d3.max(data, function (d) { return d.nonpromotedprice; })]).nice();
g.append("g")
.attr("class", "grid")
.attr("transform", "translate(0, 40)")
.attr("fill", "lightgrey")
.attr("stroke-width", 0.15)
.attr("stroke-opacity", 0.2)
//.attr("shape-rendering", crispEdges)
//stroke-opacity: 0.7;shape-rendering: crispEdges;
.call(y_axis
.tickSize(-420)
.tickFormat(""))
;
// PEPSICO AS-IS BRAND CALL OUT
g.append("rect")
.attr("width", 38)
.attr("height", 20)
.attr("x", function (d) { return d.x_value - 2; })
.attr("y", function (d) { return d.promotedprice; })
.attr("fill", function (d) { return color(d.x_value); })
.attr("transform", "translate(" + -15 + "," + -40 + ")");
g.append("text")
//.classed('rotation', true)
//.attr('x', (d,i)=> xScale(i))
.attr("x", function (d) { return d.x_value - 13; })
.attr("y", function (d) { return d.promotedprice - 28; })
.attr("dy", ".35em")
.text(function (d) { return d.brand; })
.style("font-family", "arial")
.style("font-size", 8)
.attr("stroke-width", 0.15)
.attr("fill", "white");
function dragstarted(d) {
d3.select(this).raise().classed("active", true);
}
function dragged(d) {
d3.select(this).select("rect").attr("y", d.y = d3.event.y);
//.attr("x", d.x = d3.event.x)
}
function dragended(d)
{
d3.select(this).classed("active", false);
}
Try to delete this part of code:
var svg = d3.select("body").append("svg")
.attr("width", 400)
.attr("height", 450);

How to layer D3 Force Simulation nodes based on element and not node order?

I have a a D3 v4 force simulation with nodes moving around the screen. Each node is a group consisting of a circle and some text below it. How do I order this so that the circles are on a bottom layer and the text on a top layer always. It's okay for a circle to overlap a circle, but it's never okay for a circle to overlap on top of text. Here is what I've got. Currently, the node's circle that is ahead of the other node will overlap that node's text.
this.centerNode = this.d3Graph.selectAll(null)
.data(this.nodes.slice(10,20))
.enter()
.append("g")
this.centerNode.append("circle")
.attr("class", "backCircle")
.attr("r", 60)
.attr("fill", "red")
this.centerNode
.append("text")
.attr("fill", "black")
.attr("font-size", "20px")
.attr("y", -60)
.text("test text" )
You cannot achieve the desired outcome with your current approach. The reason is simple: each group has a text and a circle. However, the painting order depends on the order of the groups:
<g>
<circle></circle>
<text></text><!-- this text here... -->
</g>
<g>
<circle></circle><!-- ...will be behind this circle here -->
<text></text>
</g>
<!-- etc... -->
So, grouping the texts and the circles inside <g> elements, you will have the groups painted in a given order and, consequently, a circle over a text (the circle of a given group is painted over the texts of all groups before it).
Here is a demo (the Baz circle will be on top of all texts, and the Bar circle will be on top of Foo text):
var width = 300;
var height = 200;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "Foo"
}, {
name: "Bar"
}, {
name: "Baz"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var nodeCircle = node.append("circle")
.attr("r", 20)
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d, i) {
return color(i)
});
var nodeTexts = node.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
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;
})
node.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")")
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
Solution
A possible solution is creating two selections, one for the circles and one for the texts. Append the circles before, and the texts later. Remember to use the same nodes array for both:
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
//etc...
var nodeTexts = svg.selectAll(null)
.data(nodes)
.enter()
.append("text")
//etc...
That way, the texts will be always on top of the circles.
Check the demo:
var width = 300;
var height = 200;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
name: "Foo"
}, {
name: "Bar"
}, {
name: "Baz"
}];
var links = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}];
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var link = svg.selectAll(null)
.data(links)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scaleOrdinal(d3.schemeCategory20);
var node = svg.selectAll(null)
.data(nodes)
.enter()
.append("circle")
.attr("r", 20)
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", function(d, i) {
return color(i)
})
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
var nodeTexts = svg.selectAll(null)
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.name;
});
simulation.nodes(nodes);
simulation.force("link")
.links(links);
simulation.on("tick", function() {
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;
})
node.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")")
nodeTexts.attr("transform", (d) => "translate(" + d.x + "," + d.y + ")")
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
<script src="https://d3js.org/d3.v4.min.js"></script>

Add labels under every bar of the grouped bar chart on D3

I'm relatively new to D3 and trying to add labels to a grouped bar chart.. With below implementation, I'm only able to see them under one group instead of both.
Below is how the data looks in the db:
category,Exceed,Fully Meets,Partially Meets,Does not meet
business,10,20,30,30
leadership,15,5,30,50
Below is the code:
var chart1 = d3.select("#svgarea2"),
margin = { top: 70, right: 0, bottom: 30, left: 40 },
width = +chart1.attr("width") - margin.left - margin.right,
height = +chart1.attr("height") - margin.top - margin.bottom,
g = chart1.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//chart background color
var bg = d3.select("g").append("svg")
// .attr("width", width + margin.right + margin.left)
.attr("width", 510 + "px")
// .attr("height", height + margin.top + margin.bottom);
.attr("height", 310 + "px");
bg.append("rect")
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "#f8f8ff");
bg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
//scale chart
var x0 = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.4);
var x1 = d3.scaleBand()
.padding(0.05);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal() //d3.schemeCategory20
// .range(["#0000ff", "#dcdcdc", "#696969", "#00008b"]);
.range(["#00008b", "#696969", "#dcdcdc", "#0000ff"]);
var columns = ['category', 'Does Not Meet', 'Partially Meets', 'Fully Meets', 'Exceed'];
var keys = columns.slice(1);
var color = d3.scaleOrdinal()
.range(["#00008b", "#696969", "#dcdcdc", "#0000ff"]);
x0.domain(data.map(function (d) { return d.category; }));
x1.domain(keys).rangeRound([0, x0.bandwidth()]);
y.domain([0, 100]).nice();
g.append("g")
.selectAll("g")
.data(data)
.enter().append("g")
.attr("transform", function (d) { return "translate(" + x0(d.category) + ",0)"; })
.selectAll("rect")
.data(function (d) { return keys.map(function (key) { return { key: key, value: d[key] }; }); })
.enter().append("rect")
.attr("x", function (d) { return x1(d.key); })
.attr("y", function (d) {
return y(d.value);
})
.attr("width", x1.bandwidth() - 7)
.attr("height", function (d) { return height - y(d.value); })
.attr("fill", function (d) { return z(d.key); })
.on("mousemove", function(d){
tooltip
.style("left", d3.event.pageX - 50 + "px")
.style("top", d3.event.pageY - 70 + "px")
.style("display", "inline-block")
.html((d.key) + "<br>" + (d.value) + "%");
});
g.append("g")
.selectAll("g")
.data(data).enter()
.append("g")
.attr("transform", function (d) { return keys })
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.attr("x", function (d) { return x0(d.category); })
.call(d3.axisBottom(x1))
.selectAll("text")
.attr("y", 15)
.attr("x", 0)
.attr("dy", ".35em")
.attr("transform", "rotate(50)")
.style("text-anchor", "start");;
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) - 5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Employees (%)");
//chart title
g.append("text")
.attr("x", (width / 2) + 30)
.attr("y", 1 - (margin.top / 2) + 20)
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("font-weight", "bold")
.style("text-decoration", "underline")
.attr("font-family", "sans-serif")
.text("Performance Distribution");
};
Any help is appreciated!
It will work if you create a g element for each barchart, and then add you axes, bars etc to each. See here for example:
http://blockbuilder.org/tomshanley/aa5471a3ecaf9e41283d68188aecf042
Relevant code:
var chart = g.append("g")
.selectAll("g")
.data(data)
.enter().append("g");
chart.attr("transform", function (d) { return "translate(" + x0(d.category) + ",0)"; })
.selectAll("rect")
.data(function (d) { return keys.map(function (key) { return { key: key, value: d[key] }; }); })
.enter().append("rect")
.attr("x", function (d) { return x1(d.key); })
.attr("y", function (d) {
return y(d.value);
})
.attr("width", x1.bandwidth() - 7)
.attr("height", function (d) { return height - y(d.value); })
.attr("fill", function (d) { return z(d.key); })
//APPEND AN AXIS TO THE CHART G'S
chart.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.attr("x", function (d) { return x0(d.category); })
.call(d3.axisBottom(x1))
.selectAll("text")
.attr("y", 15)
.attr("x", 0)
.attr("dy", ".35em")
.attr("transform", "rotate(50)")
.style("text-anchor", "start");

Add labels to bar chart D3

I'm trying to add labels to my bar chart, and I can't make it work. I read a lot about it and tried many things already, but no success.
var margin = {top: 20, right: 20, bottom: 70, left: 40},
width = 600 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var parseDate = d3.timeParse("%Y-%m");
var x = d3.scaleBand().rangeRound([0, width]).paddingInner(0.05);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y-%m"));
var yAxis = d3.axisLeft(y)
.ticks(10);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
d3.csv("bar-data.csv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d.value = +d.value;
});
x.domain(data.map(function(d) { return d.date; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", "-.55em")
.attr("transform", "rotate(-90)" );
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Value ($)");
svg.selectAll("bar")
.data(data)
.enter().append("rect")
.style("fill", "steelblue")
.attr("x", function(d) { return x(d.date); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
svg.selectAll("text")
.data(data)
.enter()
.append("text")
.text(function(d) {
return d.value;
})
.attr("text-anchor", "middle")
.attr("fill", "white")
.attr("x", function(d, i) {
return i * (width / data.length);
})
.attr("y", function(d) {
return height - (d * 4);
});
});
Here is my Plunker.
Any help is appreciated. Thank you in advance.
You can add labels to the bars using the snippet below -
svg.selectAll(".text")
.data(data)
.enter()
.append("text")
.attr("class","label")
.attr("x", (function(d) { return x(d.date); } ))
.attr("y", function(d) { return y(d.value) - 20; })
.attr("dy", ".75em")
.text(function(d) { return d.value; });
Your problem was here. The correct approach to get x-y positions for the labels would be the same approach you used for the bars using the x and y variables.
.attr("x", function(d, i) {
return i * (width / data.length);
})
.attr("y", function(d) {
return height - (d * 4);
});
Updated Plunkr - https://plnkr.co/edit/e6HPuph3OSbpxeEVQXfF?p=preview

D3 toggle stacked to grouped on multiple bar charts

I'm trying to run multiple "stacked-to-grouped" d3 bar charts on one page. I can get the charts to render, but have not been successful in getting the two charts to toggle between the options in unison.
I'm guessing that this might have to do with calling the "change" function more than once (since I'm calling it in the rendering of both charts), but I'm not sure how to place it outside of the charts and enable it to have access to what it needs. Thought it might have something to do with "each", but I'm not sure.
Here's a link to the jsfiddle that I've created: http://jsfiddle.net/1amsknh2/
And here's the js for the charts:
$( document ).ready(function() {
chart1();
});
function chart1(){
var stack = d3.layout.stack(),
layers = [
[
{"x":0,"y":1.5,"y0":0,"name":"cat1","color":"#B7A4DB"},
{"x":1,"y":1.5,"y0":0,"name":"cat2","color":"#B7A4DB"},
{"x":2,"y":1.5,"y0":0,"name":"cat3","color":"#B7A4DB"}
],
[
{"x":0,"y":2.5,"y0":1.5,"name":"cat1","color":"#85E6B5"},
{"x":1,"y":1.5,"y0":1.5,"name":"cat2","color":"#85E6B5"},
{"x":2,"y":1.0,"y0":1.5,"name":"cat3","color":"#85E6B5"}
]
];
n = 2, // number of layers
m = layers.length, // number of samples per layer
mb = ['cat1', 'cat2', 'cat3']
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
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: 40},
width = 400 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(mb)
.rangeRoundBands([0, width], .08);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(0)
.tickPadding(6)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("#chart1").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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer");
// .style("fill", function(d) { return x(d.color); });
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.name); })
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0)
.style("fill", function(d) { return d.color; });
rect.transition()
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
// .attr("y", 6)
.attr("dy", ".71em")
// .style("text-anchor", "middle")
.text("Amount");
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
console.log(1)
if (this.value === "grouped") transitionGrouped();
else transitionStacked();
svg.select("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
// .attr("y", 6)
.attr("dy", ".71em")
// .style("text-anchor", "middle")
.text("Contribution Amount");
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d, i, j) { return x(d.name) + x.rangeBand() / n * j; })
.attr("width", x.rangeBand() / n)
.transition()
.attr("y", function(d) { return y(d.y); })
.attr("height", function(d) { return height - y(d.y); });
}
function transitionStacked() {
y.domain([0, yStackMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.transition()
.attr("x", function(d) { return x(d.name); })
.attr("width", x.rangeBand());
}
chart2();
}
function chart2(){
var stack = d3.layout.stack(),
layers = [
[
{"x":0,"y":0.5,"y0":0,"name":"cat1","color":"#B7A4DB"},
{"x":1,"y":1.5,"y0":0,"name":"cat2","color":"#B7A4DB"},
{"x":2,"y":1.0,"y0":0,"name":"cat3","color":"#B7A4DB"}
],
[
{"x":0,"y":2.5,"y0":0.5,"name":"cat1","color":"#85E6B5"},
{"x":1,"y":1.5,"y0":1.5,"name":"cat2","color":"#85E6B5"},
{"x":2,"y":1.0,"y0":1.0,"name":"cat3","color":"#85E6B5"}
]
];
n = 2, // number of layers
m = layers.length, // number of samples per layer
mb = ['cat1', 'cat2', 'cat3']
yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
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: 40},
width = 400 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(mb)
.rangeRoundBands([0, width], .08);
var y = d3.scale.linear()
.domain([0, yStackMax])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(0)
.tickPadding(6)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(d3.format(".2s"));
var svg = d3.select("#chart2").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 layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer");
var rect = layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("x", function(d) { return x(d.name); })
.attr("y", height)
.attr("width", x.rangeBand())
.attr("height", 0)
.style("fill", function(d) { return d.color; });
rect.transition()
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.select("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
// .attr("y", 6)
.attr("dy", ".71em")
// .style("text-anchor", "middle")
.text("Contribution Amount");
d3.selectAll("input").on("change", change);
function change() {
console.log(2);
if (this.value === "grouped") transitionGrouped();
else transitionStacked();
svg.select("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
// .attr("y", 6)
.attr("dy", ".71em")
// .style("text-anchor", "middle")
.text("Amount");
}
function transitionGrouped() {
y.domain([0, yGroupMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("x", function(d, i, j) { return x(d.name) + x.rangeBand() / n * j; })
.attr("width", x.rangeBand() / n)
.transition()
.attr("y", function(d) { return y(d.y); })
.attr("height", function(d) { return height - y(d.y); });
}
function transitionStacked() {
y.domain([0, yStackMax]);
rect.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.attr("y", function(d) { return y(d.y0 + d.y); })
.attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); })
.transition()
.attr("x", function(d) { return x(d.name); })
.attr("width", x.rangeBand());
}
}
After struggling here and there, finally I made one fiddle for you.check it once.I hope you are looking for this http://jsfiddle.net/jxbyf82u/
In this fiddle Observe these line
d3.selectAll("input").on("change", change1);
chart1.change1 = change1;
and I'm calling this function from chart2()'s change() function by passing input value i.e. like below
chart1.change1(this.value);
If it is not what you are looking for, then ask what you want.Okay

Resources