<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
<style>
body{margin:0;font-family:sans-serif;color:#333;}
i{pointer-events:none;}
#spreadsheet{
max-width:750px;
margin-left:auto;
margin-right:auto;
table-layout:fixed;
}
#spreadsheet td{
text-align:center;
}
#spreadsheet tr:nth-of-type(1) th{
text-align:left;
padding:1em;
}
#spreadsheet tr:nth-of-type(1) th button i{
margin-right:1em;
}
#spreadsheet tr:nth-of-type(2) th{
padding:0.25em;
background-color:#222;
color:#ccc;
}
#spreadsheet tfoot tr:nth-of-type(1) td{
text-align:left;
padding:0.5em;
font-weight:700;
}
#spreadsheet input[type="number"]{
width:100%;
padding:0.15em;
font-size:100%;
box-sizing:border-box;
outline:none;
border:none;
}
#spreadsheet tr td{
border:1px solid #ccc;
}
</style>
<table width="100%" id="spreadsheet" border="0" cellpadding="0" cellspacing="0">
<thead>
<tr>
<th colspan="5"><button id="add_row"><i class="fa fa-plus-square-o" aria-hidden="true"></i><span>Add New Row</span></button></th>
</tr>
<tr>
<th>A</th>
<th>B</th>
<th>C</th>
<th>A+B+C</th>
<th>Controls</th>
</tr>
</thead>
<tbody>
<tr>
<td><input type="number"></td>
<td><input type="number"></td>
<td><input type="number"></td>
<td data-type="result"></td>
<td class="row-control"><button class="remove_row"><i class="fa fa-trash" aria-hidden="true"></i> Delete Row</button></td>
</tr>
</tbody>
<tfoot>
<tr>
<td colspan="5">Column Totals</td>
</tr>
<tr>
<td>0</td>
<td>0</td>
<td>0</td>
<td>0</td>
<td></td>
</tr>
</tfoot>
</table>
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha256-k2WSCIexGzOj3Euiig+TlR8gA0EmPjuc79OEeY5L45g=" crossorigin="anonymous"></script>
<script>
'use strict';
//always wrap your init code in a "ready" statement, it makes sure the code fires after your libs/page has loaded.
/*
I prefer the vanilla javascript alternative...
Why, cause with the $ symbol you are relying on jQuery to be loaded before the ready callback is called.
With the vanilla javascript, the load event you call is already in the browser core.
//vanilla JS
window.addEventListener('load',function(){
//init code goes here...
});
//jQuery
jQuery(document).ready(function($){
//init code goes here...
});
I'll be using jQuery, as that is what you are intent on learning.
Please note that this is "Once off code", and is not what production looks like, this is just the basics.
How long does this kind of things take, well this took about 30 minutes to code up...
In production I would spend about 1h dependant on the functional requirements and styling.
*/
jQuery(document).ready(function($){
//here we create a cached string template for when the user inserts a new row.
var rowTemplate = $('#spreadsheet tbody').html();
//Lets add a click listener to the "Add Row Button"
$('#add_row').on('click',function(){
//Here we select the tbody element and append the row template underneath the current ones.
$('#spreadsheet tbody').append($(rowTemplate));
});
//Lets add an Event Listener for when the user removes a row.
//Handling multiple targets with a single listener is called "event delegation".
$('#spreadsheet').on('click',function(e){
//this code handles the remove button
if($(e.target).hasClass('remove_row') === true){
$(e.target).parentsUntil('tr').parent().remove();
}
});
//here we use event bubbling to catch the event that the user performs when changing a number.
//We use a single event listener for performance reasons, and its just easier.
//Why not listen to EVERY new input? Imagine the user adds 2000 rows * 3 inputs! thats 6000 distinct events...
$('#spreadsheet tbody').on('change',function(e){
calcTable(e.target);
});
});
//where the magic happens aka where the table calc gets done.
function calcTable(target){
var totals = {
row:0
};
//we do the row calculation first.
//find the input columns so we can add the values together.
var row = $(target).parentsUntil('tr').parent();
$(row).find('td').each(function(i,el){
if($(el).children().is('input[type="number"]')){
totals.row += parseInt(($(el).children().val() || 0),10);
};
});
$(row).find('td[data-type="result"]').text(totals.row);
//now we do the column calculation
$('#spreadsheet tbody tr').each(function(tr_i,tr_el){
$(tr_el).find('td').each(function(td_i,td_el){
if($(td_el).children().is('input[type="number"]')){
totals['col'+td_i] = totals['col'+td_i] || 0;
totals['col'+td_i] += parseInt(($(td_el).children().val() || 0),10);
};
if($(td_el).is('td[data-type="result"]')){
totals['col'+td_i] = totals['col'+td_i] || 0;
totals['col'+td_i] += parseInt(($(td_el).text() || 0),10);
};
});
});
//now we map the totals to the Column Totals in the table footer
$('#spreadsheet tfoot tr:nth-of-type(2) td').each(function(i,el){
if(totals['col'+i] !== undefined){
$(el).text(totals['col'+i]);
}
});
};
</script>
</body>
</html>