Monday, February 23, 2009

Simple Calculator Deconstructed

Here's the deconstruction of the
calculator project, starting with the HTML:


<html>
<head>
<title>jQuery Calculator</title>
<link type="text/css" rel="stylesheet" href="calc.css"/>

<scripttype="text/javascript" src="/jquery/jquery.js"></script>
<script type="text/javascript" src="calc.js"></script>
</head>
<body>
<h1>jQuery Calculator</h1>
<tableid="calculator">
<tr>
<td colspan="4" id="display">&nbsp;</td>
</tr>
<tr>
<td class="btnOp">+</td>

<td class="btnOp">-</td>
<td class="btnOp">*</td>
<td class="btnOp">/</td>
</tr>
<tr>
<td class="btnNum">7</td>
<td class="btnNum">8</td>
<td class="btnNum">9</td>
<td class="btnMem" id="memClear">MC</td>
</tr>
<tr>
<td class="btnNum">4</td>
<td class="btnNum">5</td>
<td class="btnNum">6</td>
<td class="btnMem" id="memPlus">M+</td>
</tr>
<tr>
<td class="btnNum">1</td>
<td class="btnNum">2</td>
<td class="btnNum">3</td>
<td class="btnMem" id="memRecall">MR</td>
</tr>
<tr>
<td class="btnNum">0</td>
<td class="btnNum">.</td>
<td class="btnFunc" id="clear">C</td>
<td class="btnEq" id="equals">=</td>
</tr>
<tr><td colspan="4"><div id="tape"></div></td></tr>
</table>
</body>
</html>


This is very straightforward stuff.
Notice the lack of anything beyond basic HTML here—the idea is to
keep it simple and clean. Our rule is HTML = content (nouns), CSS =
appearance (adjectives), and JavaScript = behavior (verbs). So the
HTML sets up the nouns—a calculator table with number buttons,
operator buttons, function buttons, memory buttons, and an equals
button; and a “tape” div to display the journal.


This HTML, if rendered without CSS,
isn't very pretty. It looks like this:






So, we need to dress it up a bit—time
for the CSS. The CSS code contained in the project's stylesheet
looks like this:


body{
text-align: center;
font-family: Verdana, Helvetica, sans-serif;
font-size: 1em;
}
h1{
font-size: 100%;
}
table{
border: 1px solid black;
}
#display {
border: 1px solid black;
height: 30px;
color: blue;
font: Courier New, Courier, fixed;
text-align: right;
}
.button{
font-weight: bold;
height: 30px;
width: 30px;
text-align: center;
vertical-align: center;
}
.btnNum{

background-color: silver;
}
.btnFunc {

background-color: blue;
color: white;
}
.btnMem
{

background-color: blue;
color: white;
}
.btnMemFull{

background-color: green;
}
.btnOp{

background-color: blue;
color: white;
}
.btnEq{

background-color: maroon;
color: white;
}
#tape{
width: 200px;
height:auto;
border: 1px solid black;
color: black;
font: Courier New, Courier, fixed;
text-align: right;
}


All we're doing here is setting up
borders, fonts and colors for the various parts of the calculator. A
couple of classes to note in particular are the btnMemFull
class and the button class. They aren't referenced anywhere
in the HTML. These are classes that will be used by the JavaScript
via jQuery.


If we render the HTML with just the CSS
activated, it looks better than before, but not quite complete.
Notice the text on the buttons isn't centered, the buttons aren't
uniform, and the calculator isn't centered on the page.



These details will be picked up by the
JavaScript, by applying an attribute to the table and the button
class to all the buttons. We'll see that in a minute. First let's
take a look at all the JavaScript for the project:


var buffer = "0";
var expr = "0";
varmem = 0;
function appendBuffer(s){
buffer = buffer == "0" ? "" : buffer;
buffer += s;
}
function clearBuffer(){ buffer = "0"; }
function appendExpression(s){ expr += s; sd}
function clearExpression(){ expr = ""; }
function showBuffer(){ $("#display").html(buffer); }
function appendToTape(s){

$("#tape").append(s).append("<br />");

$(document).ready(function(){
$(".btnNum,.btnFunc,.btnMem,.btnMemFull,.btnOp,.btnEq").addClass("button");
$("#calculator").attr("align","center");
showBuffer();

$(".btnNum").click(function(){
appendBuffer($(this).text());
appendExpression($(this).text());
showBuffer();
});

$("#clear").click(function(){
clearBuffer();
clearExpression();
showBuffer();
});

$(".btnOp").click(function(){
if(expr > ""){

$("#equals").click();
}

appendExpression($(this).text());
clearBuffer();
});

$("#equals").click(function(){
appendToTape(expr);
buffer = eval(expr);
expr = buffer;
showBuffer();
});

$("#memPlus").click(function(){
mem += buffer * 1;

$(".btnMem").addClass("btnMemFull");
});

$("#memRecall").click(function(){
buffer = mem;

appendExpression(mem);
showBuffer();
});

$("#memClear").click(function(){
mem = 0;

$(".btnMem").removeClass("btnMemFull");
});
});

That's all there is to it—a
completely functional calculator. So let's step through the
JavaScript and see what's going on in there. First, we set up three
variables to store global values for the buffer (to be displayed),
the expression (to be calculated), and the memory value (used when
any of the M buttons is clicked):


var buffer = "0";
var expr = "0";
var mem = 0;


Next we set up six functions that will
be used later in the processing. They allow us to manipulate the
buffer, the expression, and the value in the tape div. These
are placed in separate functions instead of button-bound functions
because we want to be able to use these functions from several
different places in the application, as we'll see when we start
looking at the button-bound code.


Now we get into the code that is run
when the document loads, as indicated by the $(document).ready()
construct. Just as a reminder, this is the way jQuery allows us to
specify code that will be fun after the document has successfully
loaded into the browser. The first three lines define actions that
will take place right away. The button class is applied to every
member of the classes that define various types of buttons, then the
calculator table is aligned in the window, and the value of the
buffer variable is displayed in the calculator's display window:


$(".btnNum,.btnFunc,.btnMem,.btnMemFull,.btnOp,.btnEq").addClass("button");
$("#calculator").attr("align","center");
showBuffer();

Rendering with the JavaScript activated
shows that the calculator has taken on its final appearance as a
result of those three lines:



So that's much better. Everything is
aligned where we want it, and the buttons are all nice and uniform,
with their appropriate sizes and fonts.


Now we need a few functions to handle
the non-button-specific work of the calculator. We need a way to add
values to the buffer (for display):


function appendBuffer(s){
buffer = buffer == "0" ? "" : buffer;
buffer += s;
}

This function says, “if the value of
buffer equals zero, set the buffer to an empty string; otherwise
leave its value as it was. Then append the string passed into the
function to the buffer.” The ternary operation in the first line
gets rid of the zero when a number is appended at the beginning of a
calcuation.

We also need a way to clear the buffer.
We want the calculator to display zero when it's not busy, so the
function just replaces the buffer value with zero:


function clearBuffer(){ buffer = "0"; }


Similarly, we need functions to handle
appending to and clearing the expression. The expression is similar
to the buffer, except that it also handles the operators that are
clicked. Operators do not get displayed in the calculator's window.


function appendExpression(s){ expr += s; }
function clearExpression(){ expr = ""; }


Speaking of the calculator's window,
here's the function that displays the contents of the buffer in that
window:


function showBuffer(){ $("#display").html(buffer); }


This says, “Use the jQuery html()
method to replace the HTML inside the element whose id is 'display'
with the contents of the buffer.”


And we need a way to print out lines in the journal (or “tape”) as well:

function appendToTape(s){
$("#tape").append(s).append("<br/>");
}


This line says, “Use the jQuery append() method to add the string that is passed to me to the HTML inside the element whose id is 'tape'. Then use the same method to append a line break tag to the same element.”


The rest of the JavaScript assigns
various functions to the click event of the buttons on the
calculator. Let's take a look a them in sections and see what's
going on.


The first section is composed of four
functions which establish the basic functioning of the calculator.
First, we have a function that controls the behavior of each of the
number buttons:



$(".btnNum").click(function(){



appendBuffer($(this).text());



appendExpression($(this).text());



showBuffer();


});


This just says, “When I am clicked,
my text (number) is appended to the buffer and the expression using
the appendBuffer() and appendExpression() functions, and the buffer
is shown in the display using the showBuffer() function.”


We then have a function that controls
what happens when the “C” button is clicked:



$("#clear").click(function(){
clearBuffer();
clearExpression();
showBuffer();
});

This says, “When I am clicked, run
the clearBuffer() and clearExpression() functions to empty the buffer
and expression, and show the buffer in the display using the
showBuffer() function.”


Then we have a function that controls
the behavior of each of the operator buttons:


$(".btnOp").click(function(){
if(expr > ""){

$("#equals").click();
}

appendExpression($(this).text());
clearBuffer();
});

This says, “When I am clicked, if
there is something in the expression then click the Equals button.
Then use the appendExpression() function to add my text (operator) to
the expression, and use the clearBuffer() function to empty the
buffer.” Using this approach allows us to add operators to the
expression without showing them in the display (buffer).


Finally, we have a function that
controls what happens when the Equals button is clicked:

$("#equals").click(function(){
appendToTape(expr);
buffer = eval(expr);
expr = buffer;
showBuffer();
});

This says, “When I am clicked, use
the appendToTape() function to show the expression, then set the
buffer to the solution of the expression using the eval() function.
Replace the expression with the solution (buffer), and use the
showBuffer() function to display the buffer.”


That's all for the basic operation of
the calculator. But there are a few functions remaining. These have
to do with the operation of the calculator's memory feature.


First up is the M+ key:
$("#memPlus").click(function(){
mem += buffer * 1;

$(".btnMem").addClass("btnMemFull");
});

This says, “When I am clicked, add
the buffer to the memory, then change the appearance of all the
memory feature buttons by adding the btnMemFull class to
them.”

Then we need to be able to recall the
stored value, so we add a handler to the click event of the MR key:
$("#memRecall").click(function(){
buffer = mem;

appendExpression(mem);
showBuffer();
});

This says, “When I am clicked, replace the buffer with the value in memory, use the appendExpression() function to append the value in memory to the expression, and use the showBuffer() function to display the buffer.”

Finally, we need to be able to clear the memory:
$("#memClear").click(function(){
mem = 0;

$(".btnMem").removeClass("btnMemFull");
});

This says, “When I am clicked, replace the memory value with zero, then change the appearance of all the memory feature buttons back to normal by removing the btnMemFull class from them.”

And there you have it. As always, feel free to play around with the example and make it your own.

Thursday, February 19, 2009

Simple Calculator

I am, as the saying goes, back. Life happens, and it's been happening all over the place, thereby keeping me away from jNaB. However, the results are quite nice—I'm now in a new home, and the less -than-pleasant life occurrences in the last few months are settling down and becoming a bit more manageable. So let's get back to fun stuff.

The project this time is a simple jQuery-based calculator. It's not a big deal—just 4 functions and a memory feature, along with the ability to journal all the entries. This is what is looks like:







As usual, it's organized by HTML, CSS, and JavaScript. Let's start by looking at the HTML:


<html> <head> <title>jQuery Calculator</title> <link type="text/css" rel="stylesheet" href="calc.css" /> <script type="text/javascript" src="/jquery/jquery.js"></script> <script type="text/javascript" src="calc.js"></script> </head> <body> <h1>jQuery Calculator</h1> <table id="calculator"> <tr> <td colspan="4" id="display"> </td> </tr> <tr> <td class="btnOp">+</td> <td class="btnOp">-</td> <td class="btnOp">*</td> <td class="btnOp">/</td> </tr> <tr> <td class="btnNum">7</td> <td class="btnNum">8</td> <td class="btnNum">9</td> <td class="btnMem" id="memClear">MC</td> </tr> <tr> <td class="btnNum">4</td> <td class="btnNum">5</td> <td class="btnNum">6</td> <td class="btnMem" id="memPlus">M+</td> </tr> <tr> <td class="btnNum">1</td> <td class="btnNum">2</td> <td class="btnNum">3</td> <td class="btnMem" id="memRecall">MR</td> </tr> <tr> <td class="btnNum">0</td> <td class="btnNum">.</td> <td class="btnFunc" id="clear">C</td> <td class="btnEq" id="equals">=</td> </tr> <tr><td colspan="4"><div id="tape"></div></td></tr> </table> </body> </html>

Here's the CSS:

body {
text-align: center;
font-family: Verdana, Helvetica, sans-serif;
font-size: 1em;
}
h1 {
font-size: 100%;
}
table {
border: 1px solid black;
}
#display {
border: 1px solid black;
height: 30px;
color: blue;
font: Courier New, Courier, fixed;
text-align: right;
}
.button {
font-weight: bold;
height: 30px;
width: 30px;
text-align: center;
vertical-align: center;
}
.btnNum {
background-color: silver;
}
.btnFunc {
background-color: blue;
color: white;
}
.btnMem {
background-color: blue;
color: white;
}
.btnMemFull {
background-color: green;
}
.btnOp {
background-color: blue;
color: white;
}
.btnEq {
background-color: maroon;
color: white;
}
#tape {
width: 200px;
height: auto;
border: 1px solid black;
color: black;
font: Courier New, Courier, fixed;
text-align: right;
}

And here's the JavaScript:

var buffer = "0"; var expr = "0"; var mem = 0; function appendBuffer(s){ buffer = buffer == "0" ? "" : buffer; buffer += s; } function clearBuffer(){ buffer = "0"; } function appendExpression(s){ expr += s; } function clearExpression(){ expr = ""; } function showBuffer(){ $("#display").html(buffer); } function appendToTape(s){ $("#tape").append(s); $("#tape").append("
");
} $(document).ready(function(){ $(".btnNum,.btnFunc,.btnMem,.btnMemFull,.btnOp,.btnEq").addClass("button"); $("#calculator").attr("align","center"); showBuffer(); $(".btnNum").click(function(){ appendBuffer($(this).text()); appendExpression($(this).text()); showBuffer(); }); $("#clear").click(function(){ clearBuffer(); clearExpression(); showBuffer(); }); $(".btnOp").click(function(){ if(expr > ""){ $("#equals").click(); } appendExpression($(this).text()); clearBuffer(); }); $("#equals").click(function(){ appendToTape(expr); buffer = eval(expr); expr = buffer; showBuffer(); }); $("#memPlus").click(function(){ mem += buffer * 1; $(".btnMem").addClass("btnMemFull"); }); $("#memRecall").click(function(){ buffer = mem; appendExpression(mem); showBuffer(); }); $("#memClear").click(function(){ mem = 0; $(".btnMem").removeClass("btnMemFull"); }); });

Next time: the deconstruction.