Sorcerer's Tower

Beyond Tables, Beyond Divs - Simple JavaScript Calculator III

As we (hopefully) all know, using HTML tables for layout is Bad and Wrong.

Unfortunately, the most common way people avoid using tables is to just replace their table, tr and td tags with divs, divs and more divs.

The div tag is a generic container that should be used as a last resort, if there are no other more suitable options.

Whilst the problem of excess divs is not limited to the conversion of table layouts, it is perhaps most obvious here, as it shows the developer is still stuck in the column+row frame of mind, rather than thinking about what they are actually displaying.

The code which spawned this post is a good example of this behaviour. Jonny Shaw created an example of a simple javascript calculator - however it uses tables for its layout, so Mark Ireland created a version without tables for layout, but he fell into the trap mentioned above, and the resultant code has divs strewn throughout it.

One quick semi-tangent before we get on looking at code: Usability. The layout of the initial versions of this calculator, coding aside, was not the best - in particular, it didn't place the buttons into sensible groups. Usability should always be considered, especially for such interface-centric utilities as a calculator.

So, whilst it might seem like I'm picking on Mark and Jonny here, this calculator is just an example of what I see all over the place, and what I actually want to do with this blog entry is to get people thinking more clearly about what it is they are coding, so they can produce simpler, cleaner and more correct code.


Here is the key markup of v2 of the calculator:

<div style="width:22em;">
	<div class="fl"><input class="b1" type="button" name="1" value="1" OnClick="Calc.Display.value += '1'"></div>
	<div class="fl"><input class="b1" type="button" name="2" value="2" OnClick="Calc.Display.value += '2'"></div>
	<div class="fl"><input class="b1" type="button" name="3" value="3" OnClick="Calc.Display.value += '3'"></div>
	<div class="f3"><input class="b1" type="button" name="+" value="+" OnClick="Calc.Display.value += ' + '"></div>
	<div class="f3"><input class="b1" type="button" name="-" value="-" OnClick="Calc.Display.value += ' - '"></div>
</div>
<div style="clear:both;" />
<div style="width:22em;">
	<div class="fl"><input class="b1" type="button" name="4" value="4" OnClick="Calc.Display.value += '4'"></div>
	<div class="fl"><input class="b1" type="button" name="5" value="5" OnClick="Calc.Display.value += '5'"></div>
	<div class="fl"><input class="b1" type="button" name="6" value="6" OnClick="Calc.Display.value += '6'"></div>
	<div class="f3"><input class="b1" type="button" name="*" value="*" OnClick="Calc.Display.value += ' * '"></div>
	<div class="f3"><input class="b1" type="button" name="/" value="/" OnClick="Calc.Display.value += ' / '"></div>
</div>
<div style="clear:both;" />
<div style="width:22em;">
	<div class="fl"><input class="b1" type="button" name="7" value="7" OnClick="Calc.Display.value += '7'"></div>
	<div class="fl"><input class="b1" type="button" name="8" value="8" OnClick="Calc.Display.value += '8'"></div>
	<div class="fl"><input class="b1" type="button" name="9" value="9" OnClick="Calc.Display.value += '9'"></div>
	<div class="f3"><input class="b2" type="button" name="MS" value="MS" OnClick="Calc.store.value = Calc.Display.value "></div>
	<div class="f3"><input class="b2" type="button" name="MR" value="MR" OnClick="Calc.Display.value = Calc.Display.value += Calc.store.value"></div>
<div style="clear:both;" />
<div style="width:22em;">
	<div class="fl"><input class="b1" type="button" name="C" value="C" OnClick="Calc.Display.value = ''"></div>
	<div class="fl"><input class="b1" type="button" name="0" value="0" OnClick="Calc.Display.value += '0'"></div>
	<div class="fl"><input class="b1" type="button" name="=" value="=" OnClick="Calc.Display.value = eval(Calc.Display.value)"></div>
	<div class="f3"> </div>
	<div class="f3"><input class="b2" type="button" name="MC"
value="MC" OnClick="Calc.store.value = ''"></div>
</div>
<div style="clear:both;" />

As you can see, it's a lot of code, containing a lot of divs, a lot of classes, plus both inline styles and inline scripts.

Compare that to the code I did for v3 of the calculator:

<fieldset class="memory pad">
	<button type="button">MC</button>
	<button type="button">MR</button>
	<button type="button">MS</button>
	<button type="button">M+</button>
</fieldset>
<fieldset class="num pad">
	<button type="button">1</button>
	<button type="button">2</button>
	<button type="button">3</button>
	<button type="button">4</button>
	<button type="button">5</button>
	<button type="button">6</button>
	<button type="button">7</button>
	<button type="button">8</button>
	<button type="button">9</button>
	<button type="button">0</button>
	<button type="button">+/-</button>
	<button type="button">.</button>
</fieldset>
<fieldset class="operator pad">
	<button type="button">+</button>
	<button type="button">-</button>
	<button type="button">×</button>
	<button type="button">÷</button>
	<button type="button">=</button>
	<button type="button">C</button>
</fieldset>

Not a div in sight!

In order to group the buttons of similar operation (something the original did not properly do), I used the fieldset tag... this tag is for grouping form elements, so is more apt than using a div tag.

There are no divs surrounding each button like the original, nor are there any rows enforced in the HTML code - rows are entirely presentational, and are therefore handled in the CSS styling.

Note also that there are just the required classes, no inline styles, no inline scripts, and no unnecessary attributes. (Except the type="button", which is only required because some idiot at the W3C decided to specify type="submit" as the default behaviour for the button tag.)


So, first to show what styles apply to the buttons.

Essentially, all we have to do is give them a width and float left, and that's it.

With the parent container set to the appropriate width, they all line up perfectly - no rows or columns needed!

(note, the #calc below is the parent container of the whole calculator, not shown in code extract above - it is good practise to have a top level container with an ID so that you can be sure your styles are neither leaking into other components nor being overridden by other styles)

#calc .memory.pad
{
	width: 3em;
}

#calc .num.pad
{
	width: 9em;
}

#calc .operator.pad
{
	width: 6em;
}

#calc .pad button
{
	width:  3em;
	height: 1.5em;

	float: left; clear: none;
}

(I removed some other styling from that example, to only show the relevant parts)


The next most common question I suspect people will be asking is: how does it work without the onclick attributes!?

This is another common mistake/misunderstanding - whilst people are beginning to understand the concept of separating presentation from their markup, far fewer people seem to realise that interactivity should also be separate from markup, for similar reasons.

All of the onclick operations are now specified in a single javascript function that fires at page load.

The example here uses the jQuery JavaScript library, however the concept is not limited to jQuery - all decent JavaScript libraries can do this. (As can no JS library at all, but requires writing more code and is just spending effort without a good reason.)

$j('.memory button:contains(MC)').click( clearMemory );
$j('.memory button:contains(MR)').click( readMemory );
$j('.memory button:contains(MS)').click( storeMemory );

To briefly explain this for people not familiar with jQuery, what the first line says is:
"inside any element with class 'memory' look for buttons containing the text 'MC', and when the item(s) found is clicked, execute the clearMemory function".

I have done the same thing for all the other buttons, to trigger the relevant functions that each button does.

Note, I could have set an ID on all the buttons and referenced them like so:

$j('#ClearMemoryButton').click( clearMemory );
$j('#ReadMemoryButton').click( readMemory );
$j('#StoreMemoryButton').click( storeMemory );

In more complex examples that can be a better method, but since I wanted to demonstrate completely simplified markup, I opted for the other way.


Now, before I get to giving you the complete example code, I should point out that I didn't simply convert the JavaScript to work with the new markup, but I went a couple of steps further and created a couple of JS objects, which contain the main functionality, and then a series of functions to connect these objects to the interface.


The code is not perfect, but that is (at least partly) deliberate - I'd like to set an open challenge: take what I've done, identify things to improve (there are lots), create a v4, write a blog entry to explain your changes, then invite others to do the same with your version.

Keep in mind that this is intended as a programming exercise, rather than attempting to create a super-powerful calculator - keep it simple and focus on implementation & improvement rather throwing in new features for the sake of it.


So, here is a link to v3 - simply View Source to see the code:
Simple JavaScript Calculator v3

Also, for reference, here also is v2 (copied from Mark's blog into its own page):
Simple JavaScript Calculator v2

And the two related blog posts:

If anyone does accept the challenge and creates a v4, please let me know and I'll add the link.