I am getting Fib.inputValidate is not a function
I am wanting to run the inputValidate
method so that on keyup
the input validates both as an integer
and as a Fibonacci
number:
HTML looks like this:
<form id="fibonacci-form" action="" method="post">
<input id="fibonacci" type="text" name="fibonacci"/>
</form>
Javascript ES6:
class Fibonacci {
constructor() {
const isPerfectSquare = '';
const isFibonacci = '';
const isInt = '';
const inputValidate = '';
this.isPerfectSquare = isPerfectSquare;
this.isFibonacci = isFibonacci;
this.isInt = isInt;
this.inputValidate = inputValidate;
} // constructor
inputValidate(valueParsed, isInt) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
field.addEventListener("keyup", function(e) {
if (this.isInt(valueParsed) === false && field.value !== '') {
alert('Please enter a valid integer.');
}
if(this.isFibonacci(valueParsed)) {
alert(valueParsed + ' is a Fibonacci Number.');
} else {
alert(valueParsed + ' is not a Fibonacci Number.');
}
});
}
isInt(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
}
isPerfectSquare(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
if (field.value !== '') {
return (squaredValue * squaredValue == valueParsed);
}
}
isFibonacci(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4);
}
} // class
let Fib = new Fibonacci();
console.log(Fib.inputValidate());
I am getting Fib.inputValidate is not a function
I am wanting to run the inputValidate
method so that on keyup
the input validates both as an integer
and as a Fibonacci
number:
HTML looks like this:
<form id="fibonacci-form" action="" method="post">
<input id="fibonacci" type="text" name="fibonacci"/>
</form>
Javascript ES6:
class Fibonacci {
constructor() {
const isPerfectSquare = '';
const isFibonacci = '';
const isInt = '';
const inputValidate = '';
this.isPerfectSquare = isPerfectSquare;
this.isFibonacci = isFibonacci;
this.isInt = isInt;
this.inputValidate = inputValidate;
} // constructor
inputValidate(valueParsed, isInt) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
field.addEventListener("keyup", function(e) {
if (this.isInt(valueParsed) === false && field.value !== '') {
alert('Please enter a valid integer.');
}
if(this.isFibonacci(valueParsed)) {
alert(valueParsed + ' is a Fibonacci Number.');
} else {
alert(valueParsed + ' is not a Fibonacci Number.');
}
});
}
isInt(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
}
isPerfectSquare(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
if (field.value !== '') {
return (squaredValue * squaredValue == valueParsed);
}
}
isFibonacci(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4);
}
} // class
let Fib = new Fibonacci();
console.log(Fib.inputValidate());
Share
Improve this question
edited Jan 14, 2019 at 23:59
Jack Bashford
44.2k11 gold badges55 silver badges82 bronze badges
asked Jan 14, 2019 at 23:38
Rob MyrickRob Myrick
85911 silver badges28 bronze badges
4
-
1
Just remove all the
this.[function] = [function]
from the constructor. – ibrahim mahrir Commented Jan 14, 2019 at 23:41 -
I just did this, but then on
keyup
I getthis.isInt
is not a function. – Rob Myrick Commented Jan 14, 2019 at 23:42 - 1 Your constructor is overwriting all its functions with an empty string. – user5734311 Commented Jan 14, 2019 at 23:42
- @RobMyrick that's a different problem – ibrahim mahrir Commented Jan 14, 2019 at 23:44
5 Answers
Reset to default 3The real problem is that the this
inside event handlers is not what you think it is. The this
inside the event handler will be the (DOM) element that fired the event, not the instance of your class.
Now while you were trying to fix the real problem, you ended up with another problem, which is that you are shadowing the class methods with properties that have as values the empty string ''
.
To fix this, just remove the constructor all together as it doesn't do anything, and fix the issue with the this
inside the event listener. To do that you have plenty of ways:
- Use a variable outside the event listener scope called, for example,
that
, assignthis
to it and usethat
instead ofthis
inside the event listener.
Like so:
var that = this;
field.addEventListener("keyup", function(e) {
// use 'that' instead of 'this'
if(that.isInt(valueParsed) ...
});
- Use an arrow function because arrow functions use their surrounding
this
value:
Like so:
// notice the arrow function passed to addEventListener
field.addEventListener("keyup", e => {
// you can now use 'this' here with no problems
if(this.isInt(valueParsed) ...
});
bind
your event handler to the instance of your class.bind
ing a function will create a new function that always have itsthis
value set to whatever you set it to.
Like so:
field.addEventListener("keyup", function(e) {
// you can now use 'this' here with no problems
if(this.isInt(valueParsed) ...
}.bind(this)); // bind the function to its surronding 'this' value so 'this' inside it will be the same as 'this' outside it
Working code: Using an arrow function
class Fibonacci {
inputValidate(valueParsed, isInt) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
field.addEventListener("keyup", e => {
if (this.isInt(valueParsed) === false && field.value !== '') {
alert('Please enter a valid integer.');
}
if (this.isFibonacci(valueParsed)) {
alert(valueParsed + ' is a Fibonacci Number.');
} else {
alert(valueParsed + ' is not a Fibonacci Number.');
}
});
}
isInt(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
}
isPerfectSquare(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
if (field.value !== '') {
return (squaredValue * squaredValue == valueParsed);
}
}
isFibonacci(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4);
}
} // class
let Fib = new Fibonacci();
<form id="fibonacci-form" action="" method="post">
<input id="fibonacci" type="text" name="fibonacci" />
</form>
Enhanced working code:
There still are some issues with your code that are related to the functionality rather than errors:
- You declare functions as having arguments but you are not using them. For instance, the
valueParsed
argument is not used at all by even though it is an argument in all functions, instead you are fetching it from the DOM element every time. Use the argument. - The
valueParsed
(now used as argument) will be initialized insideinputValidate
. It should be fetched from within the event listener not from outside it (every time the event fires we should get a new value forvalueParsed
). - For validation use
Number
instead ofparseInt
if you want to exclude float numbers (usingparseInt
will let float numbers pass validation as it takes only the integer bit from them). Also if validation fail,return
to stop further code from executing. It (validation) still not very good though, I'll leave that to you. - Suggestion: You may want to use a button and listen for clicks on it instead of listening for
keydown
input on the field which is annoying. Create a button, and when the user clicks the button then check if the number they entered in the field is a Fibbonacci one or not. You would only change a line or two of code to achieve that.
class Fibonacci {
inputValidate() {
var field = document.getElementById('fibonacci');
field.addEventListener("keyup", e => {
var valueParsed = Number(field.value);
if (this.isInt(valueParsed) === false) {
alert('Please enter a valid integer.');
return;
}
if (this.isFibonacci(valueParsed)) {
alert(valueParsed + ' is a Fibonacci Number.');
} else {
alert(valueParsed + ' is not a Fibonacci Number.');
}
});
}
isInt(valueParsed) {
return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
}
isPerfectSquare(valueParsed) {
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
return (squaredValue * squaredValue == valueParsed);
}
isFibonacci(valueParsed) {
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4);
}
} // class
let Fib = new Fibonacci();
<form id="fibonacci-form" action="" method="post">
<input id="fibonacci" type="text" name="fibonacci" />
</form>
Remove (or empty) your constructor. Class methods are inherited by instances of the class automatically, and as it is your constructor is simply overriding them with properties whose values are empty strings.
Remove the this.inputValidate
and const inputValidate
from your constructor. And write your method this way...
inputValidate = (valueParsed, isInt) => {
// do stuff here
};
Problem
Your constructor is overwriting every single function in your class. Here's what actually happens to each method (I'm using isInt()
as an example, but it's exactly the same for each one):
You set isInt
to ''
(empty string) in the constructor:
const isInt = '';
Then, you create a property named isInt
, and set it to isInt
the string:
this.isInt = isInt;
So isInt
ends up being an empty string. Here's a minified example:
class Fibonacci {
constructor() {
const isInt = '';
this.isInt = isInt;
} // constructor
isInt(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
}
} // class
let Fib = new Fibonacci();
console.log(Fib);
As you can see, the property isInt
is equal to ""
(empty string), which is why you can't call it like a function - it's a string.
Solution
Place the function declarations within the constructor:
class Fibonacci {
constructor() {
this.inputValidate = function(valueParsed, isInt) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
field.addEventListener("keyup", function(e) {
if (this.isInt(valueParsed) === false && field.value !== '') {
alert('Please enter a valid integer.');
}
if (this.isFibonacci(valueParsed)) {
alert(valueParsed + ' is a Fibonacci Number.');
} else {
alert(valueParsed + ' is not a Fibonacci Number.');
}
});
}
this.isInt = function(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
return !isNaN(valueParsed) && valueParsed == valueParsed.toFixed();
}
this.isPerfectSquare = function(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
if (field.value !== '') {
return (squaredValue * squaredValue == valueParsed);
}
}
this.isFibonacci = function(valueParsed) {
var field = document.getElementById('fibonacci');
var valueParsed = parseInt(field.value);
var squaredValue = parseInt(Math.sqrt(valueParsed).toFixed(0));
return this.isPerfectSquare(5 * valueParsed * valueParsed + 4) || this.isPerfectSquare(5 * valueParsed * valueParsed - 4);
}
}
} // class
let Fib = new Fibonacci();
console.log(Fib);
Problem raised for below lines in your code.
const inputValidate = '';
this.inputValidate = inputValidate;
what does it mean, it means const variable inputValidate assigned to this.inputValidate, So this.inputValidate is not a function.
In contrast function inputValidate(valueParsed, isInt) added into prototype of the created object for the class by nature.
So, when you called below lines
let Fib = new Fibonacci();
console.log(Fib.inputValidate());
then find Fib.inputValidate in the class/constructor functions first, if not found then find Fib.inputValidate in prototype.
so, when you called Fib.inputValidate() then it finds the function in its constructor function, but it founds Fib.inputValidate is a property like variable but not function.
Thats why showing Uncaught TypeError: Function is not a function
For clear conception you can read the article enter link description here
Actually there is no class in javascript but ES6 introduce a class keyword, Actually this class keyword simply syntactic sugar.
So everyone should keep in mind actual scenario of Class.
Finally some modification of your code:
constructor() {
const isPerfectSquare = '';
const isFibonacci = '';
const isInt = '';
const inputValidate = '';
} // constructor
Now Fib.inputValidate() will be reachable.
And finally into the keypress/keyup or any other event this always point the Dom element so if you use arrow function for keypress/keyup or any other event then this will point the class object.