Course lectures : 1 of 1

Online calculator with jQuery and PHP

26th December 2011

online calculatorjquery calculatorphp calculator

Today we will learn how to build a calculator, which allows us to perform simple calculations such as adding, deducting, multiplying and dividing. To build it we will use jQuery and a tiny bit of PHP.

To start with - download the exercise files and place the entire content of the start folder inside of the root of your project.

Open index.html and in between the opening and closing body tags create the wrapper div and the structure of our calculator:

<section id="wrapper">

	<div id="calculator">

		<div id="calculatorResult">0.00</div>

		<ul id="calculatorKeyboard">

			<li data-value="1">1</li>
			<li data-value="2">2</li>
			<li data-value="3">3</li>
			<li data-value="+" class="operator">+</li>

			<li data-value="4">4</li>
			<li data-value="5">5</li>
			<li data-value="6">6</li>
			<li data-value="-" class="operator">-</li>

			<li data-value="7">7</li>
			<li data-value="8">8</li>
			<li data-value="9">9</li>
			<li data-value="*" class="operator">*</li>

			<li data-value="0" class="double">0</li>
			<li data-value="." class="operator">.</li>
			<li data-value="/" class="operator">/</li>

			<li data-value="c" class="double summary">C</li>
			<li data-value="=" class="double summary">=</li>

		</ul>

	</div>

</section>

As you can see our calculator structure is enclosed within the div with the id calculator. The display is represented by the div with the id calculatorResult and the keyboard is simply an unordered list. The key value is stored within the data-value attribute of the specific key (li tag) - this gives you more flexibility when it comes to a visual representation of the specific key, as values are separate to what you want to display in between the opening and closing li tags.

Our index.html file is now completed so you can save and close it.

Now open the core.js file, which you'll find inside of the js folder. Right at the top start with the definition of the new object called (not surprisingly) calculatorObject:

var calculatorObject = {

};

Inside of the new object create the following properties:

defaultValue : '0.00',
operators : ['+', '-', '*', '/'],
sequence : [ ],
cycleCompleted : false,
url : './mod/calculate.php',
result: $('#calculatorResult'),

The defaultValue is the value which is displayed when the calculator first loads and whenever we reset it. The operators is the array of all possible calculation types represented by its operator i.e. adding is represented by the '+' symbol etc.

The sequence is an empty array, which will be used to store values and operators to create a calculation string, which we'll later send to PHP to get the result. The cycleCompleted is telling us whether the equal key has been pressed or not, url is the url to the PHP file where the calculation takes place and result is the object to which we print the result of the calculation.

The first method we will create is going to determine whether the given value is a number (integer / decimal).
We'll give it a name of isNumber.

Please note that each method will use strict mode by starting with "use strict" statement and some of them will declare a variable called self to refer to its parent object calculatorObject rather than calling the object by its full name.

isNumber : function(value) {

	"use strict";

	return (
		typeof value !== 'undefined' &&
		! isNaN(parseFloat(value)) &&
		isFinite(value)
	);

}

The next method will perform the call to the PHP string sending the calculation string in order to get the result and display it in the display area. This specific method is called calculate and will be triggered whenever someone presses the = key:

calculate : function() {

	"use strict";

	var self = this;

	$.ajax({
		type: "POST",
        dataType: 'json',
		url: self.url,
		data: { sequence : self.sequence },
		success: function(data, textStatus, jqXHR) {

			self.result.html(data);
			self.cycleCompleted = true;
			self.sequence = [ ];

		},
		error: function(jqXHR, textStatus, errorThrown) {

			throw new Error(errorThrown);

		}
	});

}

As you can see we are using jQuery's ajax() method to make a POST call to the url with the sequence containing the values and operators used while clicking the buttons on the calculator's keyboard.

If call was successful we replace the existing value within the div with the id of calculatorResult with the returned one.

After this, we set the cycleCompleted to true and reset our sequence array.

If call was unsuccessful, we simply throw a new Error with the returned error as an argument.

The last method of our calculatorObject is called init:

init : function() {

    "use strict";

    var self = this;

    $(document).on('click', '#calculator ul li', function() {

        $(this).siblings('li').removeClass('active');
        $(this).addClass('active');

        var clickedValue = $(this).data('value'),
			displayValue = self.result.text();


    });

}

We start with binding the click event to each li element inside of the div with the id calculator using jQuery's on() method.

When the click event occurs the first thing that happens is the removal of the active class from all li tags, which are the siblings of the one that has just been clicked. Next we select the clicked li tag by applying the same class to it.

The clickedValue variable stores the value assigned to the data-value attribute of the clicked element and the displayValue one gets the current value from the display.

Next we need to process the event. After the line that reads:

displayValue = self.result.text();

create a switch statement with the clickedValue as the argument:

switch(clickedValue) {

	case '=':
		self.equal(displayValue);
		break;

	case 'c':
		self.clear();
		break;

	case self.operators[0]:
	case self.operators[1]:
	case self.operators[2]:
	case self.operators[3]:
		self.operator(clickedValue, displayValue);
		break;

	default:
		self.default(clickedValue, displayValue);
		break;

}

The first case will be executed when the equal key is clicked:

case '=':
	self.equal(displayValue);
	break;

Second case responds to the c (clear) button:

case 'c':
    self.clear();
    break;

Third case will be executed if any of the operators has been clicked:

case self.operators[0]:
case self.operators[1]:
case self.operators[2]:
case self.operators[3]:
    self.operator(clickedValue, displayValue);
    break;

And the last, default one - will execute when any of the above 3 fails:

default:
    self.default(clickedValue, displayValue);
    break;

Now we need to create the 4 methods we've used within the switch statement - these are equal(), clear(), operator() and default().

We'll start with the equal(), which will push the previously clicked value (now in the display) to the sequence and call the calculate() method to get the result of the expression:

equal: function(displayValue) {

    "use strict";

    this.sequence.push(displayValue);
    this.calculate();

}

Next, clear() method clears the sequence array and resets the value in the display to the default one.

clear: function() {

    "use strict";

    this.sequence = [ ];
    this.result.html(this.defaultValue);

}

The third, operator() method, first resets the cycleCompleted if it was called after the calculate() method returned the result. Next we add the value from the display to the sequence and check if it is a number. If it is - we add the clickedValue to the sequence - otherwise we overwrite it with the clicked value.

Last line of this method puts the clickedValue in display.

operator: function(clickedValue, displayValue) {

    "use strict";

    if (this.cycleCompleted) {

        this.cycleCompleted = false;

    }

    this.sequence.push(displayValue);

    if (this.isNumber(displayValue)) {

        this.sequence.push(clickedValue);

    } else {

        this.sequence[this.sequence.length-1] = clickedValue;

    }

    this.result.html(clickedValue);

}

The last default() method first checks if the value in the display is an operator and if it is - it simply replaces it with the clickedValue. Otherwise it first checks if the value in the display is identical === to the defaultValue set on the object - and if that evaluates to true - it also replaced the value in the display with the clicked one.

The else block of this condition checks if it's the first call after the calculate() method has been called, by checking cycleCompleted property. If it returns true then it sets the very same property to false and again - puts the clickedValue in the display - otherwise the value that's put in the display is a concatenation of the displayValue with clickedValue.

default: function(clickedValue, displayValue) {

    "use strict";

    if ($.inArray(displayValue, this.operators) !== -1) {

        this.result.html(clickedValue);

    } else {

        if (displayValue === this.defaultValue) {

            this.result.html(clickedValue);

        } else {

            if (this.cycleCompleted) {

                this.cycleCompleted = false;
                this.result.html(clickedValue);

            } else {

                this.result.html(displayValue + clickedValue);

            }

        }

    }

}

Our object is now completed and the last thing we need to do is to call its init() method once the document has been fully loaded.

$(function() {

    "use strict";

    calculatorObject.init();

});

You can now save and close the file, navigate to the mod directory and open the calculate.php file for editing.

We will wrap our statement in between the try / catch block, which will handle any thrown exception with the header() function and 402 status.

With 402 status, jQuery's $.ajax() method will call any function associated with its error property rather than success, which would be the case with status 200.

<?php

try {



} catch (Exception $e) {

	header("HTTP/1.0 402 " . $e->getMessage());

}

Now, within the try block type the following:

if ( ! isset($_POST['sequence'])) {

    throw new Exception('Invalid request');

}

$values = array_filter(
    $_POST['sequence'],
    function ($value) {

        return (
            is_numeric($value) ||
            in_array($value, array('+', '-', '/', '*'))
        );

    }
);

if (empty($values)) {

    throw new Exception("Empty request");

}

$calculation = 'return ('.implode(' ', $values).');';
$value = eval($calculation);

echo json_encode([
    number_format($value, 2)
]);

First we check if the request actually contains the sequence post variable, and if it doesn't - we throw an exception.

Next we filter the sequence array to make sure that it only contains numerical values as well as 4 of our operators. Once filtered, we check if the returned array still contains any data and if not - we throw an exception.

The next two lines create an expression as a string and then pass it to the eval() function to evaluate. The result is stored in the $value variable.

Last piece simply echoes the result formatted as number with 2 decimals in the json format using json_encode() function.

Save and close the calculate.php file and test your new calculator in the browser.

COMMENTS

If you'd like to include code example please wrap your code within <pre><code> tags. See example here


<pre>
<code>
function fullName(firstName, lastName)
{
    return firstName + ' ' + lastName;
}
</code>
</pre>