Skip to content

Instantly share code, notes, and snippets.

@cstrelioff
Created April 10, 2015 20:55
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cstrelioff/b7a3bb7c2a2a56c23d49 to your computer and use it in GitHub Desktop.
Save cstrelioff/b7a3bb7c2a2a56c23d49 to your computer and use it in GitHub Desktop.
responsive svg bar chart
<!DOCTYPE html>
<html>
<head>
<title>responsive svg bar chart</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- styles: should have reset.css first -->
<link rel="stylesheet" href= "reset.css">
<link rel="stylesheet" href= "main.css">
</head>
<body>
<div id="fullpage">
<header role="banner">
<h1>header</h1>
<nav role="navigation">
</nav>
</header>
<article id="mainviz">
</article>
<aside role="complementary">
<h1>data</h1>
<div class="radio-toolbar">
<input type="radio" id="radio1" name="data" value="a" checked/>
<label for="radio1">set a</label> <br>
<input type="radio" id="radio2" name="data" value="b"/>
<label for="radio2">set b</label>
</div>
</aside >
<footer role="contentinfo">
<h1>footer</h1>
</footer>
</div> <!-- end fullpage -->
<!-- javascript links -->
<script src="main.js"></script>
</body>
</html>
header, article, aside, footer, ul, li, p {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
#fullpage {
max-width: 62em;
margin: auto;
}
body {
font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
}
header, article, aside, footer {
padding: 10px 1em;
margin: 0 5% 10px;
}
header {
margin-top: 10px;
}
article {
min-height: 10em;
border-left: 5px solid black;
}
aside {
min-height: 5em;
border-left: 5px solid black;
}
h1 {
font-size: 2em;
}
h2 {
font-size: 1.5em;
}
h1, h2 {
font-weight: bold;
margin: 5px 0;
}
p {
font-size: 1em;
margin: 5px 0;
}
strong {
font-weight: bold;
}
/* spacing for radio buttons
*
* credit: http://stackoverflow.com/questions/4641752/css-how-to-style-a-selected-radio-buttons-label
*
*/
.radio-toolbar input[type="radio"] {
display: none;
}
.radio-toolbar label {
display: inline-block;
padding: 4px 11px;
margin: 10px 0px;
border: 1px solid #bbb;
border-radius: 5px;
font-size: 1.2em;
}
.radio-toolbar input[type="radio"]:checked + label {
background-color: #bbb;
border: 2px solid black;
}
/* Chart */
.chart {
width: 98%;
padding: 10px 2% 10px 0;
}
.chart rect:hover {
fill: red;
}
.chart text {
fill: black;
text-anchor: start;
font-size: 1em;
}
.chart rect {
fill: steelblue;
}
.type2 rect {
fill: burlywood;
}
/* large screens */
@media only screen and (min-width: 38em) {
article {
width: 67%;
float: left;
margin-right: 0;
}
aside {
width: 21%;
float: right;
margin-left: 0;
}
footer {
clear: both;
}
}
/*
* main.js
* Copyright (C) 2015 Christopher C. Strelioff <chris.strelioff@gmail.com>
*
* Distributed under terms of the MIT license.
*/
// ---
var ds = {
current: "a",
a: [40, 50, 10, 140, 300, 25],
b: [200, 40, 80, 15, 400, 88, 45],
getValues: function () {
return this[this.current].slice();
},
getLength: function () {
return this[this.current].length;
},
getMax: function() {
return Math.max.apply(Math, this[this.current]);
},
getAvg: function() {
var sum = 0,
length = this.getLength()
data = this.getValues();
for (var n=0; n < length; n++) {
sum += data[n];
}
return Math.round(sum/length * 100)/100;
}
};
var barHeight = 30,
barLengthFactor = 0.7;
// ---
window.onload = function init() {
ds.current = get_radio_val();
draw_svg_chart();
}
// ---
function draw_svg_chart () {
var data = ds.getValues(),
dataLength = ds.getLength(),
maxData = ds.getMax(),
vizDiv = document.getElementById('mainviz'),
svg = document.createElementNS("http://www.w3.org/2000/svg", 'svg'),
infoDiv = document.createElement('div');
// setup svg and append to vizDiv
svg.setAttributeNS(null, "id", 'mainviz_svg');
svg.setAttributeNS(null, "height", dataLength*(barHeight+1));
vizDiv.appendChild(svg);
// set scales, widths, etc
var svgWidth = svg.parentElement.clientWidth,
maxBarLength = barLengthFactor*svgWidth,
scale_factor = maxBarLength/maxData;
// setup infoDiv and append to vizDiv
infoDiv.setAttribute("id", "dataInfo");
infoDiv.innerHTML = "<p><strong>set " + ds.current +
"</strong> -- click bars for more information.</p>";
vizDiv.appendChild(infoDiv);
if (ds.current === "a") {
svg.setAttributeNS(null, "class", 'chart');
}
else {
svg.setAttributeNS(null, "class", 'chart type2');
}
var newG,
newRect,
newText,
textNode;
// loop through data
for (n=0; n < dataLength; n++) {
//create new group for this bar
newG = document.createElementNS("http://www.w3.org/2000/svg", 'g');
newG.setAttributeNS(null, "id", "group-" + n);
newG.setAttributeNS(null, "data-value", data[n]);
newG.setAttributeNS(null, "transform", "translate(0," + ((barHeight+1)*n) +")");
newG.addEventListener("click", bar_mouse_click(data[n]));
// create rectangle/bar
newRect = document.createElementNS("http://www.w3.org/2000/svg", 'rect');
newRect.setAttributeNS(null, "id", "bar-val-" + n);
newRect.setAttributeNS(null, "width", data[n]*scale_factor);
newRect.setAttributeNS(null, "height", barHeight);
// create text/label
newText = document.createElementNS("http://www.w3.org/2000/svg", 'text');
newText.setAttributeNS(null, "id", "text-val-" + n);
newText.setAttributeNS(null, "x", data[n]*scale_factor + 2);
newText.setAttributeNS(null, "y", barHeight/2);
newText.setAttributeNS(null, "dy", "0.35em");
// text node
textNode = document.createTextNode(data[n]);
newText.appendChild(textNode);
// add rectangle to g
newG.appendChild(newRect);
// add text to g
newG.appendChild(newText);
// add g to svg
svg.appendChild(newG);
}
}
// ---
function update_svg() {
var data = ds.getValues(),
dataLength = ds.getLength(),
maxData = ds.getMax(),
svg = document.getElementById('mainviz_svg'),
svgWidth = svg.parentElement.clientWidth,
maxBarLength = barLengthFactor*svgWidth,
scaleFactor = maxBarLength/maxData,
rCurr,
tCurr;
for (n=0; n < dataLength; n++) {
// get current bar and update width
rCurr = document.getElementById("bar-val-" + n);
rCurr.setAttributeNS(null, "width", data[n]*scaleFactor);
// get current text and update x-position
var tCurr = document.getElementById("text-val-" + n);
tCurr.setAttributeNS(null, "x", data[n]*scaleFactor + 2);
}
}
// ---
function clear_svg_chart() {
var v = document.getElementById("mainviz"),
s = document.getElementById("mainviz_svg"),
d = document.getElementById("dataInfo");
v.removeChild(s);
v.removeChild(d);
}
// ---
function get_radio_val() {
var radio1 = document.getElementById("radio1"),
radio2 = document.getElementById("radio2");
if (radio1.checked) {
return radio1.value;
}
else if (radio2.checked) {
return radio2.value;
}
}
// ---
function check_data_buttons() {
var newSelectedData = get_radio_val();
if (newSelectedData !== ds.current){
ds.current = newSelectedData;
clear_svg_chart();
draw_svg_chart();
}
}
// ---
function bar_mouse_click(value) {
var outDiv = document.getElementById("dataInfo"),
dataAvg = ds.getAvg();
function data_info() {
outDiv.innerHTML = "<p><strong>set:</strong> " + ds.current +
"<br><strong>value:</strong> " + value;
if(value > dataAvg) {
outDiv.innerHTML += "<br>This value is <strong>greater than</strong> the average of " +
dataAvg + "</p>"
}
else {
outDiv.innerHTML += "<br>This value is <strong>less than</strong> the average of " +
dataAvg + "</p>"
}
}
return data_info;
}
// events
window.addEventListener('resize', function() {
update_svg();
});
document.getElementById("radio1")
.addEventListener("click", check_data_buttons);
document.getElementById("radio2")
.addEventListener("click", check_data_buttons);
/* http://meyerweb.com/eric/tools/css/reset/
v2.0 | 20110126
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section {
display: block;
}
body {
line-height: 1;
}
ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment