Simple Javascript Function Problem - javascript

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();
};
})());

Related

Variable doesn't update after having event listener

I cannot update the value for userPickColor for some reason, it is always undefined. But I try to console.log inside the functions and my values are actually changing. But for some reasons, once I call it outside the function, it doesn't update at all.
I'm still new to Javascript so Please Help me
Here is my code:
var white = document.getElementById("white");
var black = document.getElementById("black");
var userPickColor;
white.addEventListener("click", whiteshirt);
black.addEventListener("click", blackshirt);
function whiteshirt(){
userPickColor= "white";
}
function blackshirt(){
userPickColor= "black";
}
ShirtDescrp.innerHTML = userPickColor;
As others have said, you need to place the line that updates the .innerHTML inside of the callback functions so that after the variable has been updated, you can update the page with the most current variable value as well.
But, taking this one step further... There is a common coding methodology called DRY (Don't Repeat Yourself) and you've got two callback functions that largely do the same thing. The only difference is the actual text that gets set. Those two functions run when one of two elements on your page get clicked and those two elements have the text you want to use as their ids. We could easily combine the two callbacks into just one like this:
var ShirtDescrp = document.getElementById("des");
// There's nothing wrong with variables if they help you read the code
// more easily, but if you won't be using the value they store more than
// once, they don't really add much.
document.getElementById("white").addEventListener("click", changeColor);
document.getElementById("black").addEventListener("click", changeColor);
function changeColor() {
// No variable needed. Just set the text to the id of the element that got clicked
// "this" here refers to the object that initiated the call for the current function
// which will be one of the two buttons.
ShirtDescrp.innerHTML = this.id;
}
<button id="white">white</button>
<button id="black">black</button>
<p id="des"></p>
you have to put ShirtDescrp.innerHTML = userPickColor; inside your event listener as well since you change the variable but you didn't tell the dom to update the content
You should update the DOM same as the variable:
var white = document.getElementById("white");
var black = document.getElementById("black");
var ShirtDescrp = document.getElementById("des");
var userPickColor = null;
white.addEventListener("click", whiteshirt);
black.addEventListener("click", blackshirt);
function whiteshirt() {
userPickColor = "white";
ShirtDescrp.innerHTML = userPickColor;
}
function blackshirt() {
userPickColor = "black";
ShirtDescrp.innerHTML = userPickColor;
}
<button id="white">white</button>
<button id="black">black</button>
<p id="des"></p>
You have already got your answer... Change doesn't affect as update is not happening inside event listener. This is just a recommendation how I would do it (especially if i had more color options):
<p id="color-name">none</p>
<div class="color-buttons">
<button id="white">White</button>
<button id="black">Black</button>
<button id="orange">Orange</button>
<button id="blue">Blue</button>
<button id="green">Green</button>
<button id="red">Red</button>
</div>
JavaScript like:
var buttons = document.querySelectorAll(".color-buttons button");
var color_name = document.getElementById("color-name");
buttons.forEach(function(btn){
btn.addEventListener("click", function(){
color_name.style.background=this.id;
color_name.innerHTML=this.id;
});
});
Here is the fiddle to play around.
I would also probably generate those buttons dynamically as well. And also wouldn't use id to store color but i would use something else like data-color='red' for example.

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...

How to use JavaScript function both globally and in Element prototype?

What's going on should be pretty basic, I've "dumbed down" everything to a simple example to hopefully be better understandable here.
I declare a "global" function in one of my scripts:
function byClass(cl)
{
return arguments[1]?
arguments[1].GetElementsByClassName(cl):
document.getElementsByClassName(cl);
}
What it does is: you call byClass() with a string argument (=cl) and it returns a document.getElementsByClassName result. If you also specify an Element of the page as the optional 2nd argument, then the function will perform the .getElementsByClassName only "inside" of the specified element.
Example:
<span class="inner">Outside</span>
<p id="outer">
<span class="inner">Inside</span>
</p>
var both=byClass('inner'); ==> [both 1st and 2nd span]
var out=document.getElementById('outer');
var in =byClass('inner',out); ==> [only 2nd span from inside P element]
What I want to happen now is "attach" that function to the HTMLElement Prototype like so:
HTMLElement.prototype.byClass=function(cl){byClass(cl,this);}
So when .byClass() is added to an element in the code, it will perform the byClass function just "inside" the element it was attached to. Applied as code, this will work something like so:
var out=document.getElementById('outer');
var in =out.byClass('inner'); ==> [only 2nd span from inside P]
This all works fine, having no trouble so far.
However, when adding a simple byClass() call to an element's "onClick" event, it doesn't perform the global byClass() but basically a this.byClass() call "inside" the element that triggered it.
var out=document.getElementById('outer');
var in1=byClass('inner'); ==> [works as expected]
var in2=out.byClass('inner'); ==> [works as expected]
but
<input type="button" onclick="console.debug(byClass('inner'));">
will perform an element-specific this.byClass() instead of the global byClass() when clicked.
How can I avoid that...? I'm lost.
I know I could call it as window.byClass() from the onClick event, but I want to keep things simple and leave nothing up to my luck with "maybe I won't forget adding window. before it"...
Grateful for any help or ideas!
No jQuery please. No "don't extend the DOM" comments please.
Thank you. :)
How can I avoid that...?
By not using event handler attributes in HTML. There's not really a way around that.
Of course, you can also avoid putting .byClass properties on your elements in the first place, but the problem persists for all names of the elements and documents properties.
Inline handler first try find function in HTMLElement.prototype and then in global scope.
You can use an ugly workaround with non-standard Function.caller function, something like in snippet below
function bl() {
console.log('blur');
}
function byClass(cl) {
console.log(arguments.length > 1 ? 'proto' : 'global', this, arguments, arguments.length);
return arguments[1] ?
arguments[1].getElementsByClassName(cl) :
document.getElementsByClassName(cl);
}
HTMLElement.prototype.byClass = function byClassEl(cl) {
console.log('proto', this, arguments, byClassEl.caller);
if (!this.onclick || byClassEl.caller !== this.onclick) { //if called not from onclick -> run with two parameters
byClass(cl, this);
} else { //otherwise run with one
byClass(cl);
}
}
console.log('first, should be global'), byClass('inner');
var x = document.getElementById('outer');
console.log('second, should be proto'), x.byClass('inner');
<span class="inner">Outside</span>
<p id="outer">
<span class="inner">Inside</span>
</p>
<input type="button" onclick="byClass('inner');" value="button" />
<br/>
<br/>Sample for standart onblur:
<input type="button" onclick="onblur()" onblur="bl();" value="button2" />
UPDATE: a bit more generic solution
function bl() {
console.log('blur');
}
function byClass(cl) {
console.log(arguments.length > 1 ? 'proto' : 'global', this, arguments, arguments.length);
return arguments[1] ?
arguments[1].getElementsByClassName(cl) :
document.getElementsByClassName(cl);
}
HTMLElement.prototype.byClass = function byClassEl(cl) {
console.log('proto', this, arguments, byClassEl.caller);
if (checkProp(this, byClassEl.caller)) { //if called not from onclick -> run with two parameters
byClass(cl, this);
} else { //otherwise run with one
byClass(cl);
}
}
function checkProp(source, caller) {
for (var i in source) {
if (i.startsWith('on')) {
var prop = source[i];
if (prop && typeof prop === "function" && prop === caller) {
return false;
}
}
}
return true;
}
console.log('first, should be global'), byClass('inner');
var x = document.getElementById('outer');
console.log('second, should be proto'), x.byClass('inner');
<span class="inner">Outside</span>
<p id="outer">
<span class="inner">Inside</span>
</p>
<input type="button" onclick="byClass('inner');" value="button" />

javascript for the variant of calculator

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.

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

Categories

Resources