/**
* Table Bar Chart jQuery plugin 1.0
*
* Copyright (c) 2014, AIWSolutions
* License: GPL2
* Project Website: http://wiki.aiwsolutions.net/Snld9
**/
jQuery.fn.tableBarChart = function(targetDiv, caption, reverseGroup) {
var source = $(this);
var target = $(targetDiv);
var maxValue = 0;
var minValue = Number.MAX_VALUE;
var yAxisMax = 0;
var yAxisMin = 0;
var groupTitles = [];
var legends = [];
var tableData = [];
function getHeaderNames(isColumn) {
var parentNode = isColumn ? 'thead' : 'tbody';
var names = [];
source.find(parentNode + ' th').each(function(index, element) {
// skip first cell
if (index > 0 || isColumn === undefined || !isColumn) {
names.push($(element).text());
}
});
return names;
}
function getDataByRow() {
var dataMatrix = [];
source.find('tbody tr').each(function(i, trElement) {
var rowValue = [];
$(trElement).find('td').each(function(j, tdElement) {
var intValue = parseInt($(tdElement).text());
if (intValue > maxValue) {
maxValue = intValue;
} else if (intValue < minValue) {
minValue = intValue;
}
rowValue.push(intValue);
});
dataMatrix.push(rowValue);
});
return dataMatrix;
}
function getDataByColumn() {
var dataMatrix = [];
var numCols = source.find('tbody tr:eq(0) td').size();
for (var i = 0; i < numCols; i++) {
var colValue = [];
source.find('tbody tr').each(function() {
var intValue = parseInt($($(this).find('td:eq(' + i + ')')[0]).text());
if (intValue > maxValue) {
maxValue = intValue;
} else if (intValue < minValue) {
minValue = intValue;
}
colValue.push(intValue);
});
dataMatrix.push(colValue);
}
return dataMatrix;
}
function getCaption() {
if (caption === undefined || caption.length === 0) {
caption = '';
source.find('caption').each(function() {
caption = $(this).text();
});
}
return caption;
}
function getAxisHTML(sourceArray, cssClass) {
var axis = $('<ul class="' + cssClass + '"></ul>');
$(sourceArray).each(function() {
axis.append('<li><span>' + this + '</span></li>');
});
return axis;
}
function getYAxisArray(stepCount) {
var maxDigitCount = String(maxValue).length;
var minDigitCount = String(minValue).length;
var base10 = Math.pow(10, maxDigitCount - 1);
yAxisMax = base10 * (Math.floor(maxValue / base10) + 1);
if (maxDigitCount === minDigitCount) {
yAxisMin = base10 * (Math.floor(minValue / base10));
}
var result = [];
var step = (yAxisMax - yAxisMin) / stepCount;
for (var i = stepCount; i >= 0; i--) {
var stepValue = yAxisMin + step * i;
if (stepValue % 1 !== 0) {
stepValue = parseFloat(Math.round(stepValue * 100) / 100).toFixed(2);
}
result.push(stepValue);
}
return result;
}
function animateBar(index) {
$('.bar.item-' + index).each(function() {
var bar = $(this);
bar.css('height', 0);
var value = bar.attr('value');
bar.animate({
'height': value
}, 800);
});
}
function getLegendHTML() {
var legendContainer = $('<ul class="legend"></ul>');
$(legends).each(function(index) {
var legendItem = $('<li><span class="icon item-' + index + '"></span>' +
this + '</li>');
legendItem.mouseenter(function() {
animateBar(index);
});
legendContainer.append(legendItem);
});
return legendContainer;
}
function getBarChartHTML() {
var barsContainer = $('<div class="bars"></div>');
$(tableData).each(function(i, columnGroup) {
var barGroup = $('<div class="bar-group"></div>');
$(columnGroup).each(function(j, cell) {
var bar = $('<div class="bar item-' + j + '" value="' +
Math.floor((cell - yAxisMin) / (yAxisMax - yAxisMin) * 100) + '%"><span>' +
cell + '</span></div>');
// CSS :hover won't work on IE
bar.hover(function() {
bar.find('span').css('display', 'block');
}, function() {
bar.find('span').css('display', 'none');
});
barGroup.append(bar);
});
barsContainer.append(barGroup);
});
return barsContainer;
}
function layout() {
var defaultMargin = 10;
var yAxisWidth = 50;
$('.y-axis').css('width', '100%');
$('.y-axis span').css('width', yAxisWidth).css('margin', '-' + defaultMargin + 'px 0 0 -' + (yAxisWidth + defaultMargin) + 'px');
var graphWidth = target.width() - (yAxisWidth + 2 * defaultMargin);
var graphHeight = target.height() - $('.caption').height() - $('.legend').height() - 3 * defaultMargin;
$('.graph').css('width', graphWidth).css('height', graphHeight);
var stepHeight = Math.floor((graphHeight - $('.x-axis').height() - 2 * defaultMargin) / (groupTitles.length + 1));
$('.y-axis li').css('height', stepHeight).css('width', '100%');
var barGroupHeight = ($('.y-axis li').height() + 1) * (groupTitles.length + 1);
$('.bars').css('height', barGroupHeight).css('width', '100%');
var barGroupWidth = graphWidth / groupTitles.length - 2 * defaultMargin;
$('.bar-group').css('width', barGroupWidth).css('margin', '0 ' + defaultMargin);
$('.x-axis li').css('width', barGroupWidth);
var barWidth = barGroupWidth / legends.length - 2;
$('.bar').css('width', barWidth);
for (var i = 0; i < legends.length; i++) {
$('.bar.item-' + i).css('left', i * (barWidth + 2));
animateBar(i);
}
}
function render() {
target.append('<div class="caption">' + getCaption() + '</div>');
var graphContainer = $('<div class="graph"></div>');
graphContainer.append(getAxisHTML(groupTitles, 'x-axis'));
graphContainer.append(getAxisHTML(getYAxisArray(groupTitles.length + 1), 'y-axis'));
graphContainer.append(getBarChartHTML());
target.append(graphContainer);
target.append(getLegendHTML());
layout();
}
function initialize() {
groupTitles = getHeaderNames(reverseGroup ? false : true);
legends = getHeaderNames(reverseGroup ? true : false);
tableData = reverseGroup ? getDataByRow() : getDataByColumn();
render();
}
initialize();
}
$(function() {
$('#source').tableBarChart('#target', '', false);
});
table {
border-collapse: collapse;
width:500px;
}
caption {
background: #ddd;
}
th {
background: #333;
border: 1px solid #ccc;
color: #ffffff;
font-weight: bold;
text-align: left;
padding: 3px;
}
td {
border: 1px solid #ccc;
text-align: left;
font-weight: normal;
color: #333;
padding: 3px;
}
tr:nth-child(odd) {
background: #ffffff;
}
tbody tr:nth-child(odd) th {
background: #ffffff;
color: #333;
}
tr:nth-child(even) {
background: #eee;
}
tbody tr:nth-child(even) th {
background: #ddd;
color: #333;
}
#target {
width: 400px;
height: 400px;
margin: 0;
}
#target ul {
margin: 0px;
list-style: none outside none;
}
.caption {
display: block;
text-align: center;
font-weight: bold;
height: 40px;
}
.graph {
position: relative;
float: right;
}
.graph ul {
list-style: none outside none;
margin: 0;
padding: 0;
}
.legend {
background: #f0f0f0;
border-radius: 4px;
bottom: 0;
position: relative;
text-align: left;
width: 100%;
}
.legend li {
display: block;
float: left;
height: 20px;
margin: 0;
padding: 10px 20px;
}
.legend span.icon {
background-position: 50% 0;
border-radius: 2px;
display: block;
float: left;
height: 16px;
margin: 2px 10px 0 0;
width: 16px;
}
.x-axis {
bottom: 0;
color: #555;
position: absolute;
text-align: center;
}
.x-axis li {
float: left;
margin: 0 10px;
}
.y-axis {
color: #555;
position: absolute;
text-align: left;
}
.y-axis ul {
padding: 0px;
margin: 0px;
}
.y-axis li {
border-top: 1px solid #ccc;
display: block;
}
.y-axis li span {
display: block;
position: relative;
text-align: right;
}
.bars {
position: absolute;
width: 100%;
z-index: 10;
}
.bar-group {
float: left;
height: 100%;
position: relative;
margin: 0 10px;
}
.bar {
bottom: 0;
position: absolute;
text-align: center;
#cursor: pointer;
display: block;
}
.bar span {
margin-top: -25px;
*zoom: 1;
display: none;
position: relative;
}
.item-0 {
background: green;
border:1px solid #333;
}
.item-1 {
background: yellow;
border:1px solid #333;
}
.item-2 {
background:red;
border:1px solid #333;
}
.item-3 {
background: blue;
border:1px solid #333;
}
<table id="source">
<caption>動作テスト</caption>
<thead>
<tr>
<th></th>
<th>やる気</th>
<th>休みたい気持ち</th>
<th>サボりたい気持ち</th>
</tr>
</thead>
<tbody>
<tr>
<th>今日</th>
<td>80</td>
<td>40</td>
<td>20</td>
</tr>
<tr>
<th>明日</th>
<td>50</td>
<td>60</td>
<td>40</td>
</tr>
<tr>
<th>明後日</th>
<td>10</td>
<td>80</td>
<td>60</td>
</tr>
<tr>
<th>明々後日</th>
<td>1</td>
<td>100</td>
<td>100</td>
</tr>
</tbody>
</table>
<div id="target"></div>