Creating Simple Comparative Bars with JavaScript and CSS
Join the DZone community and get the full member experience.
Join For Freeback a few months ago i reviewed the excellent sumall service. one of the cooler parts of their service is a daily/weekly email summary of your stats. here is a screen shot from my email this morning.
what i like about this are the simple bars between each number. they give you a real quick way to see your relative growth/drop from one day to the next. like any good web developer, i was curious as to how they built this, so i right clicked, selected inspect element, and took a look at the code.
so – first off – there’s a lot of markup to make this work. that isn’t because the sumall developers suck, it’s simply a matter of life when dealing with html email. but the base mechanism isn’t that difficult – a simple div with css. obviously you could use one of the hundred or so different js charting libraries out there, or canvas, but why do all that when a bit of css is all you need.
i thought it would be interesting to try to replicate the look for a web page outside of email where i could use javascript to make it more dynamic. i began by creating a simple html page to represent a particular metric – the number of page views from last week and this week.
<!doctype html> <html> <head> <meta charset="utf-8"> <title></title> <meta name="description" content=""> <meta name="viewport" content="width=device-width"> </head> <body> <h2>scores</h2> <table> <tr> <th></th> <th>last week</th> <th></th> <th>this week</th> </tr> <tr> <td>page views</td> <td><span id="pageviews_lw" data-raw="490121">490k</span></td> <td><span id="dobar"></span></td> <td><span id="pageviews_tw" data-raw="361902">362k</span></td> </tr> </table> </body> </html>
there isn’t anything particularly special about this layout, but note that i’m using a formatted number (490k) versus the real number (490121). i wanted it to be simpler to read for the end user. however, i know i’m going to need the real number, so i embed it in the html using a data property. (off topic aside – but i freaking love data attributes. so simple, so practical !)
you can view this version of the page here: http://www.raymondcamden.com/demos/2015/mar/19/test1.html . before we go any further – please actually view that link. it isn’t pretty, but guess what? it works in every single browser known to humankind. everything i do from now on will simply enhance the experience for people with javascript and more modern browsers. that’s something we should all consider when adding interactivity/fancy ui/etc to our pages! (and to be fair, i’m guilty of not doing proper progressive enhancement as well.)
ok, so let’s build the next version. i began by modifying the
dobar
span to include a table to hold my bars. that may not be necessary, but
i was mimicking what sumall had built. i also included the css for each
bar minus the portion that determined the height and the color. sumall
used black for the left side only, but i decided to use black for the
100% value and a different color for the other one. that just made more
sense to me. this is the new html for the span:
<span id="dobar"> <table><tr style="vertical-align:bottom"> <td> <div style="margin-right:3px!important;width:4px;"> </div> </td><td> <div style="margin-right:3px!important;width:4px;"> </div> </td> </tr></table> </span>
and now let’s look at the javascript.
var big_color = "#000"; var small_color = "#3cb4e7"; $(document).ready(function() { //get our numbers var pv_lw = $("#pageviews_lw").data("raw"); var pv_tw = $("#pageviews_tw").data("raw"); var biggest = math.max(pv_lw, pv_tw); var smallest = math.min(pv_lw, pv_tw); //so what perc of biggest is smallest? var perc = math.floor((smallest / biggest)*100); //so biggest uses 30, perc determines other var smallerbar = math.floor((perc/100)*30); //do left side var css_lw, css_nw; if(pv_lw == biggest) { css_lw = "30px solid "+big_color; css_nw = smallerbar+"px solid "+small_color; } else { css_nw = "30px solid "+big_color; css_lw = smallerbar+"px solid "+small_color; } $("span#dobar td:first-child div").css("border-top", css_lw); $("span#dobar td:last-child div").css("border-top", css_nw); });
so really – it just comes down to math. figure out the highest value, then the percentage difference for the other value. i used “30” to represent the highest bar so the other bar is a percentage of that. then it is a simple matter of updating the css. let me quickly thank ian devlin for his help finding a rookie mistake i made using jquery.css. i had included a semicolon in the css value which totally broke the update. i’m sure i’ll never make that mistake again.
here’s a screen shot of the result:
you can see this version in all its glory here: http://www.raymondcamden.com/demos/2015/mar/19/test2.html
so not rocket science, but nice i think. for the hell of it, and because i’m easily amused, i made a third version. i added some range fields to the bottom of the page:
<p> last week: 0 <input type="range" min="0" max="200" value="99" id="leftrange"> 200<br/> this week: 0 <input type="range" min="0" max="200" value="32" id="rightrange"> 200<br/> </p>
i was kinda surprised by how well these are supported now ( caniuse data ) but as this version is just for fun, i don’t really care about what happens in older browsers. i then wrote a simple event listener for change on them and had them update the data when used.
var big_color = "#000"; var small_color = "#3cb4e7"; function renderbar() { //get our numbers var pv_lw = $("#pageviews_lw").data("raw"); var pv_tw = $("#pageviews_tw").data("raw"); var biggest = math.max(pv_lw, pv_tw); var smallest = math.min(pv_lw, pv_tw); //so what perc of biggest is smallest? var perc = math.floor((smallest / biggest)*100); //so biggest uses 30, perc determines other var smallerbar = math.floor((perc/100)*30); var css_lw, css_nw; if(pv_lw == biggest) { css_lw = "30px solid "+big_color; css_nw = smallerbar+"px solid "+small_color; } else { css_nw = "30px solid "+big_color; css_lw = smallerbar+"px solid "+small_color; } $("span#dobar td:first-child div").css("border-top", css_lw); $("span#dobar td:last-child div").css("border-top", css_nw); }; $(document).ready(function() { renderbar(); var $leftrange = $("#leftrange"); var $rightrange = $("#rightrange"); var $leftspan = $("#pageviews_lw"); var $rightspan = $("#pageviews_tw"); $("input[type=range]").on("input", function(e) { $leftspan.text($leftrange.val()); $leftspan.data("raw", $leftrange.val()); $rightspan.text($rightrange.val()); $rightspan.data("raw", $rightrange.val()); renderbar(); }); });
you can then play around with the data and see the bars go up and down. because… i don’t know. it’s fun.
you can test this version here: http://www.raymondcamden.com/demos/2015/mar/19/test3.html
Published at DZone with permission of Raymond Camden, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments