Goowik Goowik - 2 months ago 5
CSS Question

Adding legends to selfmade barchart

I know you are probably wondering self-made barchart? Why not an existing library? Well I hate to use files with 20000 lines of code while only 500 are necessary.

Oh and it's fun :) The main objective is that I'll be using this script for an app I'll be making using Phonegap. So the lower the size, the better.

So the idea is to achieve the following:
enter image description here

I've been able to to draw the bars, make sure they are of equal width and have their height dependent on the height of the parent container. As you'll see in the code below I also added a font-size to the options. As some chart will expand around 300px of height (which would be using the default 16px for example). And some only 50px with a font-size of 12 or less. So I reduced the bar ChartContainer by 3 x the fontsize (+ rest) to make sure there is enough space for the top (amounts) & bottom (legends + title)

Now I'm not entirely sure how to add and center the amounts. I tried searching existing chart libraries to check on how it all has been rendered, unfortunately they all use canvasses or SVG containers. Any suggestions?



/* dataset
------------------

add legends
add result / amount
add bottom-border: 8px extra to both sides?
add chart name

*/

(function ($) {

var methods = {
init : function(options) {
return this.each(function() {

var $this = $(this),
dataset = options.dataset,
fontSize = options.fontSize,
widthOfContainer = $this.width(),
heightOfContainer = $this.height() - (3 * (fontSize + 4)), // make room for title (bottom), legend (bottom), amounts (top)
widthOfBar = parseInt(widthOfContainer / options.dataset.length) - 2,
bar;

$this.bind('draw', function(e) {
$this.empty();

var maxValueInDataset = Math.max.apply(Math, dataset.map(function(o){return o.a;})),
heightPerUnit = parseInt(heightOfContainer / maxValueInDataset);
for (var i = 0; i < dataset.length; i++) {
bar = $(document.createElement('div'));
bar.addClass('bar');
bar.css({
'height': parseInt(dataset[i].a * heightPerUnit) + 'px',
'width': parseInt(widthOfBar) + 'px',
'margin-left': parseInt(i * 2 + i * widthOfBar) + 'px',
'bottom': 2 * (fontSize + 4)
});
$this.append(bar);
}
});

$this.trigger('draw');
});
},
draw : function(n) {
$(this).trigger('draw');
}
};

$.fn.chart = function(methodOrOptions) {
if ( methods[methodOrOptions] ) {
return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
} else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
// Default to "init"
return methods.init.apply( this, arguments );
} else {
$.error( 'Method ' + methodOrOptions + ' does not exist on jQuery.tooltip' );
}
};

$(document).ready(function(){
$('div.barchart').chart({
// Add font-size?
fontSize: 14,
name: 'mana cost',
dataset: [
{a: 2, label: '0'},
{a: 8, label: '1'},
{a: 9, label: '2'},
{a: 4, label: '3'},
{a: 7, label: '4'},
{a: 3, label: '5'},
{a: 1, label: '6'},
{a: 1, label: '7'},
{a: 2, label: '8'},
{a: 5, label: '9'}
]
});
});
}( jQuery ));

/* Barchart
========================================================================== */

.barchart {
color: black;
}

/* Bar
========================================================================== */

.bar {
position: absolute;
height: 0px;
width: 0px;
margin-left: 0px;
bottom: 0px;
background: black;
}

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="padding: 20px;">
<div class="barchart" style="height: 100px; position: relative"></div>
</div>




Answer

nice chart! clean. I know what you mean, spent months trying to manipulate the layout of bxSlider (for example) then realised it was less code to write my own. Here's an attempt at your query, I've made it width responsive by using percentages, but not a problem to change back, added an extra css class for the legend, values, and count and modified your plugin to include your name option (called legend) these bits are then just formatted and appended. hope it helps.

/* dataset
   ------------------

   add legends
   add result / amount
   add bottom-border: 8px extra to both sides?
   add chart name

 */

(function ($) {

    var methods = {
        init : function(options) {
            return this.each(function() {

                var $this = $(this),
                    dataset = options.dataset,
                    fontSize = options.fontSize,
                    legend = options.name,
                    widthOfContainer = $this.width(),
                    heightOfContainer = $this.height() - (3 * (fontSize + 4)), // make room for title (bottom), legend (bottom), amounts (top)
                    widthOfBar = parseInt(widthOfContainer / options.dataset.length) - 2,
                    widthOfBarPer = (widthOfBar / widthOfContainer) *100,
                    bar;

                $this.bind('draw', function(e) {
                    $this.empty();

                    var maxValueInDataset = Math.max.apply(Math, dataset.map(function(o){return o.a;})),
                        heightPerUnit = parseInt(heightOfContainer / maxValueInDataset);
                    for (var i = 0; i < dataset.length; i++) {
                    		var dataVal = dataset[i].a;
                        bar = $(document.createElement('div'));
                        bar.addClass('bar');
                        bar.css({
                            'height': parseInt( dataVal * heightPerUnit) + 'px',
                            'width': widthOfBarPer + '%', // percentages to make more responsive?
                            'margin-left': (i + i * widthOfBarPer ) + '%', // no need to parseInt as you have already on widthOfBar. now your chart stretches with the width .
                            'bottom': 2 * (fontSize + 4)
                        });
                        bar.append('<p class="count">'+ i +'</p>');	// defines the bar count, this could be dataset[i].label but if you just use i then you don't need to type it out for each bar?
                        bar.append('<p class="value">'+ dataVal +'</p>');	// defines the bar value
                        $this.append(bar); // adds the bar count
                    }
                    var chartHeight = $this.height();
                    $('.bar .count').css({ bottom: fontSize - chartHeight * 0.5 });
                    $('.bar .value').css({ bottom: chartHeight * 0.5 - fontSize });
                    if(legend){
												legend = '<p class="legend">'+legend+'</p>';
											  $this.append(legend);
                  //      $this.css({ border: '1px solid #f90'}); // temp to see the current chart size
                        $this.find('.legend').css({ top: chartHeight - fontSize });
                    }
                });

                $this.trigger('draw');
            });
        },
        draw : function(n) {
            $(this).trigger('draw');
        }
    };

    $.fn.chart = function(methodOrOptions) {
        if ( methods[methodOrOptions] ) {
            return methods[ methodOrOptions ].apply( this, Array.prototype.slice.call( arguments, 1 ));
        } else if ( typeof methodOrOptions === 'object' || ! methodOrOptions ) {
            // Default to "init"
            return methods.init.apply( this, arguments );
        } else {
            $.error( 'Method ' +  methodOrOptions + ' does not exist on jQuery.tooltip' );
        }
    };

    $(document).ready(function(){
        $('div.barchart').chart({
            // Add font-size?
            fontSize: 14,
            name: 'mana cost',
            dataset: [
                {a: 2, label: '0'},
                {a: 8, label: '1'},
                {a: 9, label: '2'},
                {a: 4, label: '3'},
                {a: 7, label: '4'},
                {a: 3, label: '5'},
                {a: 1, label: '6'},
                {a: 1, label: '7'},
                {a: 2, label: '8'},
                {a: 5, label: '9'}
            ]
        });
    });
}( jQuery ));
/* Barchart
   ========================================================================== */

.barchart {
  color: black;
}

/* Bar
   ========================================================================== */

.bar {
  position: absolute;
  height: 0px;
  width: 0px;
  margin-left: 0px;
  bottom: 0px;
  background: black;
}

.count, .value{
  z-index: 7;
  position: absolute;
  text-align: center;
  width: 100%;
}

.legend{
  position: relative;
  text-align: center;
  width: 100%;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="padding: 20px;">
            <div class="barchart" style="height: 100px; position: relative"></div>
        </div>

Comments