Reputation: 23
I am trying to create a simple JavaScript Calculator. However, when I run my code and make a change to any of the three fields (value box or sign) I get an error stating
this.calculate is not a function
The error comes up under the this.calculate for whichever prototype function is being called depending on if you change your numerical value or the formula sign.
I've read up on closures, callbacks and the "this" keyword and I believe the issue lies somewhere there with how I am calling my prototype functions. Would someone help explain where in my code I am making a mistake and how to resolve it?
Here is the JavaScript
var Formula=function() {
this.value1 = null;
this.value2 = null;
this.sign = null;
this.result = null;
};
Formula.prototype.calculate = function() {
switch (this.sign) {
case '+':
this.result = this.value1 + this.value2;
break;
case '-':
this.result = this.value1 - this.value2;
break;
case '/':
this.result = this.value1 / this.value2;
break;
case '*':
this.result = this.value1 * this.value2;
break;
default:
break;
}
document.querySelector('#result').innerHTML = this.result;
};
Formula.prototype.updateValue = function(event) {
if (event.currentTarget.id === '#value1')
this.value1 = parseFloat( event.currentTarget.value );
else this.value2 = parseFloat( event.currentTarget.value );
this.calculate();
};
Formula.prototype.updateSign = function(event) {
this.sign = event.currentTarget.value;
this.calculate();
};
document.addEventListener('DOMContentLoaded', function () {
(function() {
var equation = new Formula();
document.querySelector('#sign').addEventListener('change', equation.updateSign);
var values = document.querySelectorAll('.value');
for (var i = 0, numValues = values.length; i < numValues; i++) {
values[i].addEventListener('change', equation.updateValue);
}
})();
});
And here is the HTML
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="calcJS.js"></script>
</head>
<body>
<input type="number" class="value" id="value1"/>
<select id="sign">
<option value="+">+</option>
<option value="-">-</option>
<option value="*">*</option>
<option value="/">/</option>
</select>
<input type="number" class="value" id="value2"/>
=
<span id="result"/>
</body>
</html>
Upvotes: 2
Views: 242
Reputation: 318182
The this
value in a function depends on how the function is called.
Generally when you do
var equation = new Formula();
equation.updateSign();
this
would be the "Formula object" inside updateSign
However, when you do this
var equation = new Formula();
document.querySelector('#sign').addEventListener('change', equation.updateSign);
you're referencing the function, not calling it, the event handler eventually calls it, setting the this
value to the changed element, not the equation
object
If you want this
to be the object instead, you'd have to do something like
var equation = new Formula();
document.querySelector('#sign').addEventListener('change', function() {
equation.updateSign(arguments);
});
or using bind to return a new function with a set this
value
var equation = new Formula();
document.querySelector('#sign').addEventListener('change', equation.updateSign.bind(equation));
You also had a logical flaw
if (event.currentTarget.id === '#value1')
The ID is always returned without the hash
if (event.currentTarget.id === 'value1')
Upvotes: 2
Reputation: 7745
The issue that you're seeing is due to the way that event handling works. When an event handler is called, this
is implicitly passed to the handler function as the element that is the target of the event.
In order to do keep the original this
scope, you'll need to wrap your function and invoke it directly. For example:
var equation = new Formula();
document.querySelector('#sign').addEventListener('change', function(event)
{
equation.updateSign(event);
});
This Fiddle helps to illustrate. Hope that helps.
Upvotes: 0