javascript for the variant of calculator - javascript

I am working on the project to create one variant of calculator. Basically, I am storing all the user clicks (which may be number and operator like +,-) in an array and handing the array to the another function when user clicks "=". I am using javascript for this. The below is my code:
var arr=[]; //array of every input from the user interface stored
var i=0; //number of input clicks from the user
var opPos=[]; //position of the operator as given by the user
var operAnd=[];//operands as given by the user
var disp='';
function clearval() {
// This function clears all the values stored in related array when C or CE is pressed
document.getElementById("op").value=0;
arr=[];
i=0;
opPos=[];
operAnd=[];
disp='';
}
//This Function get the value and supplies the array to calculating function
function getval(inp){
if(inp!="=")
{
arr[i]=inp;
disp=disp+inp;//for display in the screen of the output screen
document.getElementById("op").value=disp;
if (typeof inp!="number"){
opPos.push(i);
operAnd.push(inp);
}
i++;
}
else
{
var newInput=assembler(arr,opPos,operAnd);
clearval();
getval(parseFloat(newInput,10));
}
}
//<!------This Function calculates based on array, operator position and operands------!>
function assembler(num_array,opPos,operAnd){
var num='';
var numCollect=[];
var posCount=0;
for(var j=0;j<num_array.length;j++){
if (j==opPos[posCount]) {
numCollect.push(parseFloat(num,10));
num='';
j++;
posCount++;
}
else if (j>posCount) {
}
num=num+num_array[j];
}
num=parseFloat(num,10);
numCollect.push(num);
//document.getElementById("op").value= numCollect;
var newInput=calculator(numCollect,operAnd);
return newInput;
}
function calculator(target_num,operAnd) {
// Not the nice solution but straightforward nonetheless
var result='';
for (var l=0;l<operAnd.length;l++) {
result=result+target_num[l]+operAnd[l];
}
result=result+target_num[l];
document.getElementById("op").value=result + '=' + eval(result) ;
return eval(result);
}
I have html which has buttons like this:
<button class="num" onclick="getval(0)">0</button>
<button class="num" onclick="getval(1)">1</button>
<button class="num" onclick="getval(2)" >2</button>
<button class="num" onclick="getval(3)" >3</button>
......................... and so on
For the basic math, this code works fine and is not a problem. However, here is my problem from where I have hard time on thinking how to implement this. Say for example, I have a following button like this.
<button class='extra' onclick="Regression()"> Find Regression </button>
Now, I will have regression function which will ask user to input the regression type (1-linear, 2-quadratic and so on...this is just an example).
function regression(){
clearval();
document.getElementById("op").value=" Enter the degree of regression:";
which is basically asking user to enter the number and click '=' to enter into the program.
Now you can see my dilemma. Anything user inputs will be firstly processed by getval() which will pass the array to another function when user clicks '=', which is not what I will want in this case. To be clear, for this kind of case which I will have many such as std. dev or some kind of functions like this, I want the keypad to behave as normal keypad and pass the value to another function without doing normal calculator stuff as it was supposed to do.
Am I thinking this straight? Any suggestions?

I think you could solve this by adding another function called passval(), which will contain much of the same logic as getval() in terms of parsing the input into a float, etc., but which doesn't ever push values onto your operand stack or call the assembler function. It simply returns the button pushed as a nice float value to the .extra function that called it.
Then, as part of the logic in your .extra functions like Regression(), you would initially swap the onclick function for all of your buttons from getval() to passval(). When the regression or other special function is complete, swap the buttons back to their default behavior.

Well, this is what I would do(if I undestood right):
Change you button layout to this:
<input type="button" class="num" value="0" /> Removing the onclick event
Always better to use input type="button" than <button>.
Create a function to bind events to the buttons:
function bindButtonEvent(func) {
var buttons; // Here some way to get the button in a collection from the DOM tree
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = function() { func(buttons[i]); }
}
}
With this you will got a function to change the click event of all your buttons. This will make your engine more flexible.
You will have to change your getval function a little to get the value by itself:
function getval(el){
var inp = el.value;
if(inp!="=")
So on the calculator load, you set the click function:
bindButtonEvent(getval())
When you want to call a custom behaviour function, you call the binding again:
function regression() {
bindButtonEvent(function(el) {
value = el.value;
// Do things
// When done, take bindings back.
bindButtonEvent(getval());
});
clearval();
document.getElementById("op").value=" Enter the degree of regression:";
}
NOTE: That is an ideia. I didn't tested the code. If you're interested on this and have errors on implementation, let me know, and we'll going fixing them.

Related

Comparison bewteen types in JavaScript

I found this code (on HTML5), and there is nothing wrong with it, it work just fine, but i dont get the whole process it make(the code create a virtual keyboard that writes on a textfield the letters you click)
the part i dont get it:
var i,c;
function init(){
i=document.getElementById('keyboard').getElementsByTagName('input');
for(c=0;c<i.length;c++) {
if(i[c].type==='button') {
i[c].addEventListener('onclick',makeClickHandler(c));
}
}
My mains doubt are the i[c]
Thanks in advance
var i,c; // global var declaration usually not clever
function init(){ // he creates a js function
var keyboardelement=document.getElementById('keyboard');
var inputfield=keyboardelement.getElementsByTagName('input');
for( var c=0;c<inputfield.length;c++) {
if(inputfield[c].type==='button') { // he runs over the dom tree
// under keyboard to find his buttons...
// now he adds on each of the buttons a OWN click handler which
//calls the
// routine "makeClickHandler" -wrong wording should be known as
// ClickHandler or maybee not s below
inputfield[c].addEventListener('onclick',makeClickHandler(c));
}
}
that is what he made and what i call not that clever cause now he has to implement or make for each button own routine to handle this. Maybee that is why he chose the wording makeclickhanler cause h creates them dynamically.
This here do it all including the evnet handler a "nice" display and a bit action. It will display the text you click together.
<html><body>
<form name="keyboard">
//could also be dynamically created but...
<div id="out"><div>
<input type="button" value="a" class="kbdbutton">
<input type="button" value="b" class="kbdbutton">
<input type="button" value="c" class="kbdbutton">
<input type="button" value="d" class="kbdbutton">
</form>
usually i would add also a ID attribute but
<script>
// get all buttons in one call
var kbd_btn=documentGetElementsByClassName("kbdbutton")
for (var i=0; I< kbd_btn.length;i++){
kbd_btn[i].id = kbd_btn[i].value;
}
// NOW every key has its own *corresponding* id free of charge
// which also makes the button interesting for a global event handler
// act like a man and use ONE handler
document.addEventListener("click", function(event) {}
var id=event.target.id;
if (id){ // not all click events have a id
console.log(id);
display=document.getElementById("out")
switch(id) {
case "a":
alert("a clicked")
case default:
display.innerHTML=display.innerHTML+id;
}
}
)
</script>
</body></html>
if i would a just few lines more and use the evil eval function i would have a nice complete pocket calculator...

Do I need to create multiple functions for multiple actions or can they all be housed in the same function?

I'm working on a script to simulate a page change in a Questionnaire I'm building. I figured maybe I could use a bunch of "if" statements to house all the logic but it's not working right, before I go and create separate functions I'd like to know if it's possible to put them all in one single function.
So far this is the script
function pageChange(){
var chng1 = document.getElementById("p1next");
var chng2a = document.getElementById("p2back");
var chng2b = document.getElementById("p2next");
var chng3a = document.getElementById("p3back");
var chng3b = document.getElementById("p3next");
var pg1 = document.getElementById("page01");
var pg2 = document.getElementById("page02");
var pg3 = document.getElementById("page03");
if (chng1.click){
pg1.style.display="none";
pg2.style.display="block";
}
if (chng2a.click){
pg1.style.display="block";
pg2.style.display="none";
}
the "p1next, p2back, p2next etc." are IDs I gave the buttons on the pages, which I have in DIVs that I respectively named "page01, page02, page03 etc."
Without the 2nd if statement the script works exactly how I want it, it changes the display for "page01" to none and the div for "page02" to block. When I add the second if statement it doesn't work.
The reason I want to do it like this rather than making actual pages is because I don't want the data to get lost when they load another page. Am I on the right track or do I need to create a new function for each page?
Not exactly on the right track, you should use onclick events, instead of if (x.click) like this:
var chng1 = document.getElementById("p1next");
var pg1 = document.getElementById("page01");
var pg2 = document.getElementById("page02");
// Events
chng1.onclick = function(){
pg1.style.display="none";
pg2.style.display="block";
};
This will save your function until the element is clicked and then execute that function. In your case, it is executed on page load, and at that moment the user is not clicking anything.
Why not try something like this:
HTML:
<div class="page" data-pg="1">...</div>
<div class="page" data-pg="2">...</div>
<div class="page" data-pg="3">...</div>
<input id="btnPrev" type="button" value="Prev" />
<input id="btnNext" type="button" value="Next" />
jQuery:
var pageNum = 1;
$(document).ready(function () {
$("#btnPrev").on("click", function () { ChangePage(-1); });
$("#btnNext").on("click", function () { ChangePage(1); });
ChangePage(0);
});
function ChangePage(p) {
$(".page").hide();
pageNum += p;
$(".page[data-pg='" + p + "']").show();
$("#btnPrev").removeAttr("disabled");
$("#btnNext").removeAttr("disabled");
if (pageNum === 1) $("#btnPrev").attr("disabled", "disabled");
if (pageNum === $(".page").length) $("#btnNext").attr("disabled", "disabled");
}
That way you can easily grow your number of pages without changing the script. My apologies by the way for doing this in jQuery.
Update:
Have a lot of time on my hands today and have not coded for while using vanilla Javascript. Here's the version of the code using plain js: https://jsfiddle.net/hhnbz9p2/

Optimizing code to define variables only once, code only works when the vars are in change function and for the code outside change I redefine?

Pretty sure I know the solution... would write .on('change','load', function(){}
correct? <-- Tested didn't work? so I am up to your solutions :)
Sushanth -- && adeneo both came up with great solutions, this is a good lesson in optimizing code... It's gonna be hard to choose which answer to go with, but I know this is going to help me rethink how I write... I dont know what I do without this forum, id have to learn this stuff in college.
This is purely a question out of curiosity and bettering my skills, as well as giving you guys a chance to display your knowledge on jQuery. Also to prevent any sloppy writing.
I have a radio based switch box, the markup looks like this, the id's and on/off values are generated by the values in my array with PHP...
<span class="toggle-bg">//This color is the background of the toggle, I need jQuery to change this color based on the state on/off
<input type="radio" value="on" id="_moon_page_header_area1" name="_moon_page_header_area">//this is my on value generated by the array
<input type="hidden" value="_moon_page_header_area" class="switch-id-value">// I create this input because I have multiple checkboxes that have the ID _moon_ARRAYVALUE_area1
<input type="radio" value="off" id="_moon_page_header_area2" name="_moon_page_header_area">// off value
<input type="hidden" value="_moon_page_header_area" class="switch-id-value">//_moon_ARRAYVALUE_area2
<span class="switch"></span>// the switch button that changes
</span>
Hope that makes sense and the comments are clear
Here is the jQuery
var value = $('.toggle-bg input.switch-id-value').val()
var moon1 = $('#'+value+'1').is(':checked');
var moon2 = $('#'+value+'2').is(':checked');
var static_slide = $('._moon_staticarea_height');
var toggle = $('.toggle-bg');
if(moon1){
toggle.css({'background-color': '#46b692'});
static_slide.hide()
} else
if (moon2){
toggle.css({'background-color': '#333'});
static_slide.show()
}
$('.toggle-bg').change(function () {
var value = $('.toggle-bg input.switch-id-value').val()
var moon1 = $('#'+value+'1').is(':checked');
var moon2 = $('#'+value+'2').is(':checked');
var static_slide = $('._moon_staticarea_height');
var toggle = $('.toggle-bg');
if(moon1){
toggle.css({'background-color': '#46b692'});
static_slide.slideUp()
} else
if (moon2){
toggle.css({'background-color': '#333'});
static_slide.slideDown()
}
});
it looks longer than it really is, its just repeating it self, one is on load so that it gives the correct color on load of the page, and then inside the change function we need to change colors..
How do I write it so I only have to use variables one time (so its cleaner) is there a better way to optimize it... Just NOW thinking after writing this I could put it in one function .on('load', 'change', function() {}
I just now thought of that, but I wrote all this so I am going to see what others think...
You'd do that by having the function in the change event handler, and on the end you chain on a trigger('change') to make it work on pageload :
$('.toggle-bg').on('change', function () {
var value = $('.toggle-bg input.switch-id-value').val(),
moon1 = $('#' + value + '1').is(':checked'),
slider = $('._moon_staticarea_height'),
toggle = $('.toggle-bg');
toggle.css('background-color', (moon1 ? '#46b692' : '#333'));
slider[moon1?'slideUp':'slideDown']();
}).trigger('change');
As radiobuttons can't be unchecked, it's either moon1 or moon2, which means checking one of them should be enough.
.on('change','load',
supposed to be
// Remove the comma separator if you want to bind the same handler to
// multiple events.
.on('change load',
And you can remove the one separately written out and enclose it in a function (if multiple instances of the class toggle-bg)
or just trigger the change event.(If there is a single instance of a class)
This will just run the same functionality when the page loads.
var toggle = $('.toggle-bg');
toggle.change(function () {
var value = $('input.switch-id-value', this).val(),
moon1 = $('#' + value + '1').is(':checked'),
moon2 = $('#' + value + '2').is(':checked'),
static_slide = $('._moon_staticarea_height');
if (moon1) {
toggle.css({
'background-color': '#46b692'
});
static_slide.slideUp()
} else if (moon2) {
toggle.css({
'background-color': '#333'
});
static_slide.slideDown()
}
}).change();

Javascript Dynamic Event Assignment acting strange

I am having problems with Javascript event handler functions created dynamically.
In my HTML I have the following:
<input id="nvctrlfont1" type=number min=18 max=144 step=1 value=36 maxlength="3" size="8">
<input id="nvctrlfont2" class="color" value="000000" style="background-color:black;width:70px;">
<input id="nvctrlfont4" class="color" value="000000" style="background-color:black;width:70px;">
.....
The class color is just a javascript color picker.
The element ID's have 1,2,4 ... etc as Boolean values. That is to say a JS function receives a Boolean that is used to hide or display the HTML input elements. For elements that are hidden no events are to be assigned, for those that are not hidden, events are to be assigned.
The Events functions would reside in the nvFontFuncs.
The problem I cannot seem to solve:
When the function assignment takes place to the nvEL.onchange it appears to replace it each time with the same function.
For example, given the Boolean nvFontBool comes in with say "6" then what should happen is the Input Elements nvcontrolfont2 and nvcontrolfont4 should be enabled and have the functions in nvFontFuncs nvctrlfont2 and 4 respective be assigned to the onchange events accordingly.
What happens however is nvctrlfont4 function ends up getting assigned to BOTH elements.
In other words, no matter which element that has had its onchange event set, when clicked it calls the LAST one assigned. In my example, nvctrlfont4() gets called no matter if I change INPUT nvctrlfont2 or nvctrlfont4 elements.
If I for example change the nvFontBool parameter to "7" thus enabling all three input elements. Then no matter which one is changed, it calls nvctrlfont4() (which happens to be the last one assigned in the loop).
I tried using addEvent and setAttributes as well, same results. Its as the onchange assignment of the function is static when bound. So which ever is the most "recent" one bound ends up replacing the prior bindings across all elements respective.
Help?
Heres the JS.
var nvFontFuncs = {
nvctrlfont1: function () { alert('Function 1'); },
nvctrlfont2: function () { alert('Function 2'); },
nvctrlfont4: function () { alert('Function 4'); }
};
Here is the Function
function nvSetFontCtrls(nvFontBool)
{
var nvEL;
var nvName="nvctrlfont";
var nvTemp;
nvFg=1;
while(nvFontBool != 0) {
//alert("FontBool:"+nvFontBool);
nvTemp=nvName+nvFg.toString();
// nvTemp="func"+nvFg.toString();
nvEL=document.getElementById(nvTemp);
if(nvFontBool & nvFg) {
nvEL.style.display="block";
nvEL.onchange=function() { nvFontFuncs[nvTemp](); }
}
else {
nvEL.style.display="none";
}
nvFontBool=nvFontBool&(~nvFg); //alert("nvfontbool:"+dechex(~nvFg));
nvFg = nvFg << 1;
}
}
You assign the following function:
function() { nvFontFuncs[nvTemp](); }
nvTemp is a variable in the local scope of nvSetFontCtrls. So when the handler is called, then, of course, nvTemp has the last value that it was set to.
You could do somthing like this:
nvEL.onchange=(function(nvTemp) {
return function() { nvFontFuncs[nvTemp](); }
}(nvTemp));
The problem is, that nvTemp is set to the last value of the loop, so once the handler function was called, nvTemp will have 'nvctrlfont4'.
If you use nvEL.onchange=nvFontFuncs[nvTemp] assignment, the problem will be solved

Simple Javascript Function Problem

This works, with a simple button calling the addDamage function:
var damage=0;
function addDamage()
{
damage+=10;
document.getElementById("p1s").innerHTML=damage;
}
This doesn't:
var damage=0;
function addDamage(who)
{
who+=10;
document.getElementById("p1s").innerHTML=who;
}
With this button:
<button type="button" onclick="addDamage(damage)">Add</button>
It's probably obvious. I'm really new. Thanks!
You are adding 10 to who within the function. Via the parameter passed on invocation, who takes the value of damage which is a global variable.
the function uses the updated value of who to set the innerHTML of an element. all that works. Then the function exits. who goes out of scope. The updated value of who is now forgotten.
When you click the button again, it uses the value of damage, which is still its original value, 0 (zero). who gets that value again, then gets 10+, which is 10, and so on.
To update a global variable, return it from the function, and set it in the handler.
var damage=0;
function addDamage(d)
{
d+=10;
document.getElementById("p1s").innerHTML=d;
return d;
}
and
<button type="button" onclick="damage=addDamage(damage);">Add</button>
Cheeso has identified the basic problem, which is that JavaScript parameters are passed by value. To get the behavior you want, you can wrap your counter in an object:
var player1 = { damage: 0 };
function addDamage(who) {
who.damage+=10;
document.getElementById("p1s").innerHTML=who.damage;
}
Then your button would do this:
<button type="button" onclick="addDamage(player1)">Add</button>
Presumably you would have other properties for player1 that you could put in the object as well.
To make the addDamage more flexible, you could also pass a second parameter to tell where you want to display the results:
function addDamage(who, outputId) {
who.damage+=10;
document.getElementById(outputId).innerHTML=who.damage;
}
Then button looks like:
<button type="button" onclick="addDamage(player1, 'p1s')">Add</button>
var who=0; // want to use who not damage
function addDamage(who)
{
who+=10;
document.getElementById("p1s").innerHTML=who;
}
// also change me from damage to who
<button type="button" onclick="addDamage(who)">Add</button>
the nice alternative solution would be this
<button id="addDamage"> Add </button>
<div id="showDamage"> </div>
// add handler to button
document.getElementById("addDamage").addEventListener("click", (function() {
// damage is not stored in global space.
var damage = 0,
// div is only gotten once.
div = document.getElementById("showDamage");
// show functionality is in its own function so its easily changed
function show() {
div.textContent = damage;
}
// call show to show the initial damage
show();
// return event handler
return function() {
// change damage
damage+=10;
// show the new damage
show();
};
})());

Categories

Resources