Column Chart

This chart was inspired by Mike Bostock's Stacked to Grouped Bar Chart. A variation of this chart is currently being used by SEMOSS, an open-source linked-data business intelligence platform.

This chart combines the stacked-to-grouped functionality with the filtering in order to customize what series are shown. The grouped or stacked option can be chosen from the radio buttons while filtering is done through clicking the legend squares.

index.html #

<!DOCTYPE html> <meta charset="utf-8"> <html> <head> <link rel="stylesheet" type="text/css" href="../css/bootstrap.css"> <script src="../lib/jquery-1.11.0.min.js"></script> <script src="../lib/d3.v3.js"></script> <script src="../lib/tooltip.js"></script> <!--<script src="data/heatmap-data.js"></script>--> <style> #graphSpace { position: absolute; width: 960px; height: 470px; } .axis path { fill: none; stroke: black; shape-rendering: crispEdges; } .d3-tip { line-height: 1.4; font-weight: bold; padding: 12px; background: rgba(0, 0, 0, 0.8); color: #fff; border-radius: 3px; margin-top: -7px; font-family: sans-serif; font-size: 12px; } /* Creates a small triangle extender for the tooltip */ .d3-tip:after { box-sizing: border-box; display: inline; font-size: 10px; width: 100%; line-height: 1; color: rgba(0, 0, 0, 0.8); content: "\25BC"; position: absolute; text-align: center; } /* Style northward tooltips differently */ .d3-tip.n:after { margin: -1px 0 0 0; top: 98%; left: 0; } </style> <form style="float:right; margin-top:2px; margin-right:5px;"> <label><input type="radio" name="mode" value="grouped"> Grouped</label> <label><input type="radio" name="mode" value="stacked" checked> Stacked</label> </form> </head> <body> <div class="span12 formContainer"> <div style="clear: both"></div> <div id="graphSpace"></div> </div> <script type="text/javascript"> var dataString = "{\"dataSeries\":[[{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/16_Blocks\",\"y\":3.6895141E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Admission\",\"y\":1.8007E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Chicken_Little\",\"y\":1.35386665E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Curious_George\",\"y\":5.836076E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Django_Unchained\",\"y\":1.62805E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Edge_of_Darkness\",\"y\":4.331389E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Finding_Neverland\",\"y\":5.1680613E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Freaky_Friday\",\"y\":1.10230332E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Friday_the_13th\",\"y\":6.5002019E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/From_Paris_with_Love\",\"y\":2.4077427E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Gangs_of_New_York\",\"y\":7.7812E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Girl_With_a_Pearl_Earring\",\"y\":1.1670971E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Green_Lantern\",\"y\":1.16601172E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Heavens_Prisoners\",\"y\":5009305.0},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Here_on_Earth\",\"y\":1.0522168E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Hollow_Man\",\"y\":7.320934E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Hugo\",\"y\":7.3864507E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Inglourious_Basterds\",\"y\":1.20540719E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Kill_Bill_Vol._1\",\"y\":7.0099045E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Mean_Girls\",\"y\":8.6058055E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Seven_Psychopaths\",\"y\":1.5024E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Shutter_Island\",\"y\":1.28012934E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Aviator\",\"y\":1.0261033E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Boy_in_the_Striped_Pajamas\",\"y\":9046156.0},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Departed\",\"y\":1.32384315E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Girl_Next_Door\",\"y\":1.4589444E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Hills_Have_Eyes_2\",\"y\":2.0804166E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Last_Airbender\",\"y\":1.31772187E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Legend_of_Zorro\",\"y\":4.6464023E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Sixth_Sense\",\"y\":2.93506292E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Wolf_of_Wall_Street\",\"y\":1.11032E8},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Two_Can_Play_That_Game\",\"y\":2.2235901E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Two_Weeks_Notice\",\"y\":9.3354851E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Where_the_Heart_Is\",\"y\":3.3772838E7},{\"seriesName\":\"Revenue\",\"x\":\"http://semoss.org/ontologies/Concept/Title/12_Years_a_Slave\",\"y\":4.8352E7}],[{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/16_Blocks\",\"y\":5.5E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Admission\",\"y\":1.3E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Chicken_Little\",\"y\":1.5E8},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Curious_George\",\"y\":5.0E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Django_Unchained\",\"y\":1.0E8},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Edge_of_Darkness\",\"y\":8.0E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Finding_Neverland\",\"y\":2.5E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Freaky_Friday\",\"y\":2.0E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Friday_the_13th\",\"y\":1.9E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/From_Paris_with_Love\",\"y\":5.2E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Gangs_of_New_York\",\"y\":9.7E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Girl_With_a_Pearl_Earring\",\"y\":1.5564E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Green_Lantern\",\"y\":2.0E8},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Heavens_Prisoners\",\"y\":\"N-A\"},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Here_on_Earth\",\"y\":1.5E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Hollow_Man\",\"y\":9.5E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Hugo\",\"y\":1.7E8},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Inglourious_Basterds\",\"y\":7.0E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Kill_Bill_Vol._1\",\"y\":3.0E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Mean_Girls\",\"y\":1.7E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Seven_Psychopaths\",\"y\":1.5E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Shutter_Island\",\"y\":8.0E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Aviator\",\"y\":1.1E8},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Boy_in_the_Striped_Pajamas\",\"y\":1.25E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Departed\",\"y\":9.0E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Girl_Next_Door\",\"y\":\"N-A\"},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Hills_Have_Eyes_2\",\"y\":1.5E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Last_Airbender\",\"y\":1.5E8},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Legend_of_Zorro\",\"y\":7.5E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Sixth_Sense\",\"y\":4.0E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/The_Wolf_of_Wall_Street\",\"y\":1.0E8},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Two_Can_Play_That_Game\",\"y\":1.3E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Two_Weeks_Notice\",\"y\":6.0E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/Where_the_Heart_Is\",\"y\":1.5E7},{\"seriesName\":\"Budget\",\"x\":\"http://semoss.org/ontologies/Concept/Title/12_Years_a_Slave\",\"y\":2.0E7}]],\"names\":[\"Title\",\"Revenue\",\"Budget\"]}"; var rect; var margin = {top: 20, right: 40, bottom: 80, left: 40}, container = {width: 0, height: 0}, dataSeriesObject = [], dataSeriesKeys = [], dataSeriesLabels = {}, filteredLevels = [], legendArray = [], names = [], nodes = [], edges = [], xAxisText = "", yAxisText = "", standardBarOpacity = .65, barsTransitionDuration = 230, axisTransitionDuration = 1000, axisLabelPadding = 15, currentLeftMargin = 0, currentWidth = 0, absoluteBarPadding = 1, barPadding = 0.05, yGroupMax = 0, yStackMax = 0, layers = {}, stacked = true; var color; var xScale; var yScale; var yAxis; var svg; start(dataString); function start(dataString) { var jsonData = jQuery.parseJSON(dataString); var data = jsonData.dataSeries; dataSeriesObject.length = 0; dataSeriesKeys.length = 0; names.length = 0; dataSeriesObject = data; dataSeriesKeys = dataSeriesObject[0].map(function (d) { return d.x }); xAxisText = jsonData.names[0]; names = jsonData.names; legendArray.length = 0; //set container width and height containerSize('#graphSpace', container, margin); function containerSize(containerClass, containerObj, marginObj) { containerObj.width = parseInt(d3.select(containerClass).style('width')); containerObj.height = parseInt(d3.select(containerClass).style('height')); containerObj.width = containerObj.width - marginObj.left - marginObj.right; containerObj.height = containerObj.height - marginObj.top - marginObj.bottom; } var createCount = 0; //setup x xScale = d3.scale.ordinal() .rangeRoundBands([0, container.width ], barPadding); //setup y yScale = d3.scale.linear() .range([container.height, 0]); yAxis = d3.svg.axis() .scale(yScale) .orient("left") .tickSize(0); // add the graph canvas to the body of the webpage svg = d3.select('#graphSpace').append("svg") .attr("width", container.width + margin.left + margin.right) .attr("height", container.height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")") .attr("width", container.width) .attr("height", container.height); d3.select(window).on("resize", resize); d3.selectAll("input").on("change", change); //x axis svg.append("svg:g") .attr("class", "x axis") .attr("transform", "translate(0," + container.height + ")"); //x axis text/label svg.selectAll("g.x.axis").append("text") .attr("class", "label") .attr("x", container.width / 2) .attr("y", margin.bottom - 6) .style("text-anchor", "end") .text(""); //x axis line svg.selectAll("g.x.axis").append("svg:line") .attr("class", "x-axis-line") .attr("x1", 0) .attr("y1", 0) .attr("x2", 0) .attr("y2", 0) .style("stroke", "black") .style("stroke-width", 1) .style("shape-rendering", "crispEdges"); //y axis svg.append("g") .attr("class", "y axis"); svg.selectAll("g.y.axis.line") .style("stroke", "black") .style("stroke-width", 1) .style("shape-rendering", "crispEdges"); var legendGroup = svg.append("g") .attr("class", "legend"); /* Initialize tooltip */ var tip = d3.tip() .attr('class', 'd3-tip') .style("z-index", "10000") //.offset([47, 0]) .html(function (d) { return "<div> <span class='light'>" + dataSeriesLabels[d.x] + " " + d.seriesName + ":</span> " + d.y + "</div>"; }); /* Invoke the tooltip in the context of your visualization */ svg.call(tip); function change() { if (this.value === "grouped") transitionGrouped(); else transitionStacked(); } update(); //where all the magic happens function update() { //reset the color scale color = d3.scale.category20(); //establish colors setLayers(dataSeriesObject); //SET X DOMAIN xScale.domain(dataSeriesObject[0].map(function (d) { return d.x; })); //UPDATE CURRENT MARGINS BASED ON LARGEST LABEL //y axis -- left margin var maxWidth = 0; svg.selectAll("text.foo").data(yScale.ticks(10)) .enter().append("text").text(function (d) { return yScale.tickFormat()(d); }) .each(function (d) { maxWidth = Math.max(this.getBBox().width + yAxis.tickSize() + yAxis.tickPadding(), maxWidth); }) .remove(); currentLeftMargin = Math.max(margin.left + axisLabelPadding, maxWidth + axisLabelPadding) currentWidth = container.width - (currentLeftMargin - margin.left); svg.attr("transform", "translate(" + currentLeftMargin + "," + margin.top + ")"); //update xScale based on new margin xScale .rangeRoundBands([0, currentWidth ], barPadding); //BUILD AXIS:::::: // Truncate Labels for (i = 0; i < dataSeriesKeys.length; i++) { var key = dataSeriesKeys[i]; var instanceName = shortenValueFilter(key); if (instanceName.length > 20) { dataSeriesLabels[key] = (instanceName.substring(0, 20) + '...'); } else { dataSeriesLabels[key] = instanceName; } } function shortenValueFilter(str) { //convert to string str = String(str); if (str.indexOf('"') == 0) { var shortStr = str.substring(1, str.length); var returnStr = shortStr.substring(0, shortStr.indexOf('"')); return returnStr; } else if (str.indexOf('http://') !== -1) { var myRe = new RegExp("([^/]*)$"); var returnStr = myRe.exec(str); return returnStr[0]; } else { return str; } } //x axis title var xAxisTitle = svg.selectAll("g.x.axis text") .data([xAxisText]); xAxisTitle .text(function (d) { return d }); xAxisTitle .exit().remove(); //x axis ticks var xAxis = svg.select("g.x.axis"); var xTick = xAxis.selectAll("tick") .data(dataSeriesKeys); xTick.enter().append("text"); xTick.text(function (d) { return dataSeriesLabels[d]; }) .style("text-anchor", "end") .attr("x", 0) .attr("y", 15) .style("font-size", getFontSize()) .attr("class", "tick") .attr("transform", function (d, i) { return "translate(" + (xScale(d) + (xScale.rangeBand()) / 2) + ", 0)rotate(-25)" }) .attr("opacity", 0).transition().duration(axisTransitionDuration) .attr("opacity", 1); xTick .exit() .attr("opacity", 1).transition().duration(axisTransitionDuration) .attr("opacity", 0) .remove(); svg.selectAll(".x-axis-line") .attr("x2", currentWidth); //y axis svg.selectAll("g.y.axis") .transition() .duration(axisTransitionDuration) .call(yAxis); //y axis text svg.select("g.y.axis text").text(yAxisText); drawLegend(); //BUILD BARS::::: if (createCount === 0) { buildBars(); createCount++; } else { //delay bars svg.selectAll(".bar") .transition().duration(barsTransitionDuration) .attr("y", container.height) .attr("height", 0) .each("end", buildBars); } function drawLegend() { //draw legend positionLegend(); var legendData = legendGroup.selectAll("g.legendEntry") .data(legendArray, function (d) { return color(d); }) .attr("transform", function (d, i) { return "translate(" + currentWidth + ", 0)"; }); //enter legend g var legendEnter = legendData.enter().append("g"); legendEnter .attr("transform", function (d, i) { return "translate(0 ," + i * 20 + ")"; }) .attr("class", "legendEntry"); // enter legend colored rectangles legendEnter.append("rect"); legendEnter.append("text"); legendData.select("rect") .attr("x", -13) .attr("y", 0) .attr("width", 13) .attr("height", 13) .attr("fill-opacity", standardBarOpacity) .style("stroke", color) .style("stroke-width", "1px") .style("fill", color) .on("click", function (d, i) { var rect = d3.select(this); var previouslyFiltered = filteredLevels.some(function (entry) { return entry === i; }); if (!previouslyFiltered) { filteredLevels.push(i); if (filteredLevels.length === dataSeriesObject.length) { filteredLevels.length = 0; legendData.selectAll("rect").style("fill", color); } else rect.style("fill", "white"); } else { filteredLevels.splice(filteredLevels.indexOf(i), 1); rect.style("fill", color); } filterData(d); }); // enter legend text legendData.select("text") .attr("x", -24) .attr("y", 7) .attr("dy", ".25em") .style("text-anchor", "end") .text(function (d) { console.log(d); return d; }); legendData.exit().remove(); }// end of drawLegend } // end of update function buildBars() { //this contains all generic bar building //specific building exists in buildStacked and buildGrouped var layer = svg.selectAll(".layer") .data(layers); rect = layer.selectAll(".bar") .data(function (d) { return d; }); rect .enter().append("rect") .attr("y", container.height) .attr("height", 0); rect .attr("class", "bar") .attr("opacity", standardBarOpacity) .on('mouseover', function (d) { var bar = d3.select(this); bar.transition() .duration(100).style("opacity", 1); tip.show(d); }) .on('mouseout', function (d) { var bar = d3.select(this); bar.transition() .duration(400).style("opacity", standardBarOpacity); tip.hide(d); }); if (stacked) buildStacked(true); else buildGrouped(true); } // end of buildBars function filterData(d) { console.log("filtering out " + d); var newObject = []; for (var i = 0; i < dataSeriesObject.length; ++i) { var filtered = filteredLevels.some(function (entry) { return entry === i; }); if (!filtered) { console.log("adding index " + i); newObject.push(dataSeriesObject[i]); } } setLayers(newObject); svg.select('g.y.axis') .transition().duration(axisTransitionDuration) .call(yAxis); buildBars(); } function setLayers(data) { var stack = d3.layout.stack(); layers = stack(data); 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; }); }); if (stacked) yScale.domain([0, yStackMax]); else yScale.domain([0, yGroupMax]); var layer = svg.selectAll(".layer") .data(layers); layer .enter().append("g"); layer .attr("class", "layer") .style("fill", function (d) { legendArray.push(d[0].seriesName); return color(d[0].seriesName); }); layer .exit() .selectAll(".bar") .transition().duration(barsTransitionDuration) .delay(function (d, i) { return i * 10; }) .attr("y", container.height) .attr("height", 0) .remove(); } function positionLegend() { legendGroup .attr("transform", function (d, i) { return "translate(" + currentWidth + ", 0)"; }); } //this is called when initially building bars or resizing window //this animation is different than transition bars animation--thus different function function buildGrouped(transition) { if (transition) { rect .transition() .delay(function (d, i) { return i * 10; }) .attr("x", function (d, i, j) { return xScale(d.x) + xScale.rangeBand() / layers.length * j; }) .attr("width", xScale.rangeBand() / layers.length) .attr("y", function (d) { return yScale(d.y); }) .attr("height", function (d) { return container.height - yScale(d.y); }); } else { rect .attr("x", function (d, i, j) { return xScale(d.x) + xScale.rangeBand() / layers.length * j; }) .attr("width", xScale.rangeBand() / layers.length) .attr("y", function (d) { return yScale(d.y); }) .attr("height", function (d) { return container.height - yScale(d.y); }); } } //this is called when initially building bars or resizing window //this animation is different than transition bars animation--thus different function function buildStacked(transition) { rect .attr("x", function (d) { return xScale(d.x); }) .attr("width", xScale.rangeBand() - absoluteBarPadding); if (transition) { rect .transition() .delay(function (d, i) { return i * 10; }) .attr("y", function (d) { return yScale(d.y0 + d.y); }) .attr("height", function (d) { return yScale(d.y0) - yScale(d.y0 + d.y); }); } else { rect .attr("y", function (d) { return yScale(d.y0 + d.y); }) .attr("height", function (d) { return yScale(d.y0) - yScale(d.y0 + d.y); }); } } //font size is dynamically calculated based on bar width (available space) function getFontSize() { var font = Math.round(xScale.rangeBand() / 2); console.log("Range Band: " + xScale.rangeBand()); if (font > 12) { font = 12; } if (font < 7) { font = 7; } return font + "px"; } //this is called when window is resized //adjust positioning of all items function resize() { console.log("resizing"); //set container width and height containerSize('#graphSpace', container, margin); currentWidth = container.width - (currentLeftMargin - margin.left); d3.select('#graphSpace').select("svg") .attr("width", container.width + margin.left + margin.right) .attr("height", container.height + margin.top + margin.bottom); //update xScale based on new margin xScale .rangeRoundBands([0, currentWidth ], barPadding); //setup y yScale .range([container.height, 0]); yGroupMax = d3.max(layers, function (lay) { return d3.max(lay, function (d) { return d.y; }); }); yStackMax = d3.max(layers, function (lay) { return d3.max(lay, function (d) { return d.y0 + d.y; }); }); updatePositioning(); } //updates positioning based on new scales, height, and currentWidth function updatePositioning() { positionLegend(); if (stacked) { yScale.domain([0, yStackMax]); buildStacked(false); } else { yScale.domain([0, yGroupMax]); buildGrouped(false); } svg.selectAll(".x-axis-line") .attr("x2", currentWidth); var xAxis = svg.select("g.x.axis") .attr("transform", "translate(0," + container.height + ")"); xAxis.selectAll(".tick") .style("font-size", getFontSize()) .attr("transform", function (d, i) { return "translate(" + (xScale(d) + (xScale.rangeBand()) / 2) + ", 0)rotate(-25)" }); xAxis.select(".label") .attr("x", currentWidth / 2) yAxis .ticks(Math.max(container.height / 30, 2)); svg.select('g.y.axis') .call(yAxis); } } //this is called when the toggle between stacked and grouped is switched //this animation is different than build bars animation--thus different function function transitionGrouped() { console.log("transitioning to grouped"); stacked = false; yScale.domain([0, yGroupMax]); rect.transition() .duration(500) .delay(function (d, i) { return i * 10; }) .attr("x", function (d, i, j) { return xScale(d.x) + xScale.rangeBand() / layers.length * j; }) .attr("width", xScale.rangeBand() / layers.length) .transition() .attr("y", function (d) { return yScale(d.y); }) .attr("height", function (d) { return container.height - yScale(d.y); }); //y axis svg.selectAll("g.y.axis") .transition() .duration(axisTransitionDuration) .call(yAxis); } //this is called when the toggle between stacked and grouped is switched //this animation is different than build bars animation--thus different function function transitionStacked() { console.log("transitioning to stacked"); stacked = true; yScale.domain([0, yStackMax]); rect.transition() .duration(500) .delay(function (d, i) { return i * 10; }) .attr("y", function (d) { return yScale(d.y0 + d.y); }) .attr("height", function (d) { return yScale(d.y0) - yScale(d.y0 + d.y); }) .transition() .attr("x", function (d) { return xScale(d.x); }) .attr("width", xScale.rangeBand() - absoluteBarPadding); //y axis svg.selectAll("g.y.axis") .transition() .duration(axisTransitionDuration) .call(yAxis); } </script> </body> </html>