/** * 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>