I'm new to both javascript and jquery, and I'm having a small problem. It is happening with both the latest versions of Chrome and Firefox, so I don't think it's a browser issue.
Relevant code is here (this is a self-invoking function inside script tags on the HTML page).
(function () {
"use strict";
var submitbutton = $('#submitcommand'),
textinput = $('#textinput'),
userinput = textinput.val();
submitbutton.on('click', function() {
alert("textinput is " + textinput.val()); // => works
alert("userinput is " + userinput); // => undefined
});
}());
The first call to alert() works just fine, using 'textinput.val()'. The second call to alert() doesn't return any visible text, because 'userinput' is evaluating to 'undefined'.
I've tried going into the Firebug Console and pasting the four statements (the two assignments and the two calls to alert()) in, one after the other. That works, and gets me the result I expect.
So the question is: what changes about the 'userinput' variable inside the on('click') function? What am I not seeing here?
Thanks in advance!
As the function is self invoking, the variable will be set at page load. It is not reassessed every time the button is clicked. During page load you will probably find that:
The input is emtpy
The input is lower down the page than the script (More probable I think)
So at the time of setting there is no value. You probably want to make the variable scoped to the button click:
(function () {
"use strict";
var submitbutton = $('#submitcommand'),
textinput = $('#textinput')
submitbutton.on('click', function() {
var userinput = textinput.val();
alert("textinput is " + textinput.val()); // => works
alert("userinput is " + userinput); // => undefined
});
}());
Also, you will want to make sure the script block is at the bottom of the page, as then all the UI elements will be loaded in the browser when the function is invoked.
Your value is undefined inside the handler.
You should perhaps use it this way:
(function() {
"use strict";
var submitbutton = $('#submitcommand'),
textinput = $('#textinput')
userinput = textinput.val();
submitbutton.on('click', function() {
userinput = textinput.val();// sets the value in the handler
alert("textinput is " + textinput.val());
alert("userinput is " + userinput);
});
}());
working example:http://jsfiddle.net/avCay/
Note that this works this way due to the scope of the variable and the "reset" of the values inside the event handler. The first pass parses the script, the second executes it, and the subsequent event handler fires on the click event of the submitbutton element, setting the value at that point during that event.
EDIT: NOTE: regarding the jQuery read/load wrap (as in my example fiddle) if you wish to NOT have that, you can do:
$('#submitcommand').on('click', function() {
instead of the variable and jQuery will properly hook the event handler to the element.
EDIT2: or perhaps this will provide more insight here. If you do:
You will still need to re-access the variable however if you wish to have the "current" value of the input, not that in the "undefined" state during the initial execution due to the parse/execute cycle of the Javascript. The "re-access" of the .val(); vs the value during the initial script execution is the key to that "undefined" value. and setting it "inside" the handler gets the current value.
When
var userinput = textinput.val()
is evaluated:
textinput may not exist yet - you must be either in a document.ready() handler or the <script> block must be after #textinput in the DOM.
if it does exist, userinput gets its current value, not any future value it may happen to have.
This is because the value of #textinput is empty on page load (unless you set the value to something when the page loads). So the stored value in userinput is empty. When you read the value from textinput inside the event handler, it looks up the value of the input-field.
Makes sense? :)
Related
When dynamically creating an element of type select, there are two problems when setting the onclick method:
It is impossible to simply set the onclick with element.onclick="updateInput(this.articleIndex)";
This results in a final HTML tag where no onclick is shown at all.
When set by e.setAttribute("onclick","updateInput(this.articleIndex)");, it does appear in the final HTML. And the updateInput method does get called.
However the functionality seems to be broken, as the argument always evaluates to undefined
Here a simple example of my problems:
var selectElem = document.createElement("select");
selElem.id="articleSelector_"+this.articleIndex;
console.log("the index of the article is " + this.articleIndex);
selElem.setAttribute("onclick","updateInput(this.articleIndex);");
//selElem.onclick="updateInput(this.articleIndex)"; //this does not work
The log shows the correct number. Inside the updateInput method, the argument is of value undefined instead of the number previously shown in the log.
Try attaching handlers with pure Javascript, and not with HTML, without onclick = "... (which is as bad as eval).
The this in your script refers to the calling context of the function - what is it?
You might want:
element.addEventListener('click', () => {
updateInput(this.articleIndex);
});
(arrow functions retain the this of their surrounding scope)
it is impossible to simply set the onclick with element.onclick="updateInput(this.articleIndex)";
What that code does is it assigns the string "updateInput(this.articleIndex)" to the onclick which makes no sense and certainly not what you want.
Even if you remove the quotes:
element.onclick = updateInput(this.articleIndex);
It is still incorrect because it assigns the result of the updateInput() function to the onclick which is again not what you want.
You need to assign a function name to the onclick like this:
element.onclick = updateInput;
However, this doesn't allow you to pass a parameter as you wish. To do so, you need to use an anonymous function:
element.onclick = function() {
updateInput(this.articleIndex)
};
When set by e.setAttribute("onclick","updateInput(this.articleIndex)");, it does appear in the final HTML. And the updateInput method does get called.
This works because it sets the attribute onclick and it is a string type, so everything is correct. It is equivalent to using the anonymous function above. The only difference is this, which in this case refers to the element itself, while in the above code it depends on the context that the code appears in. That's why in this case the argument always evaluates to undefined because the select element doesn't have an articleIndex property.
The problem is the value of the context this when that element is clicked, the context this is not available anymore at that moment.
You have two ways to solve this problem:
You can use the function addEventListener to bind the event click, and bind the function/handler with the desired context this:
The function bind binds a specific context to a function.
selElem.addEventListener('click', updateInput.bind(this));
function updateInput() {
console.log(this.articleIndex);
}
As you need a specific value, you can use data attributes. That way, you don't need to worry about the context this.
selElem.dataset.articleIndex = this.articleIndex;
selElem.addEventListener('click', function() {
updateInput(this.dataset.articleIndex); // Here you can get that value.
});
I have been making a videogame with javascript. However, there is just one thing that I don't understand.
if (!user.hasOwnProperty('firstName')) {
$('#inputSubmit').click(function () {
user.firstName = getInput();
addText_1("Good, now type your character's last name");
});
};
this statement will keep executing. Basically the condition is that the user does't have a first name property and the function will add the first name on the click of submit. However, you can keep pressing the submit button and it will keep adding the text.
$('#inputSubmit').click(function() {
if(!user.hasOwnProperty('firstName')) {
user.firstName = getInput();
addText_1('hello');
};
});
However, this works. It only does it once. Could someone explain the principle that I am not understanding?
Thank you very much!
You attach a click event to #inputSubmit. This callback ignores the surrounding if statement. In your second sample code, if is inside the callback function.
in first code "hasOwnProperty" checked in outer scope of function() but in second code the condition checked in scope of function
I am hoping to create a web application, this means that the HTML elements will have to be pretty dynamic as they will be being created and moved around being handled by different other elements a lot.
I therefore decided to use classes - with a lot of success, until the point of handling events..This is the class in suspicion that Chrome tells me confuses it with a variable not being defined.
function SpanInputPair(element) {
span = document.createElement("span");
this.Span = getNextInputID();
span.style.display = "none";
span.id = this.Span;
element.appendChild(span);
input = document.createElement("input");
input.type = "text";
this.Input = getNextInputID(); // request a new ID for this element and save into this class
input.id = this.Input; // Attach it onto the element for future finding :/ (slow)
element.appendChild(input);
input.focus();
span.onclick = function() {
var input = getInput(this.Input); // this.Input cannot be seen?
var span = getInput(this.Span);
input.value = span.innerHTML;
toggleDisplay(input);
toggleDisplay(span);
input.focus();
}
input.onblur...
The line "var input = getInput(this.Input);" is the issue as Chrome is telling me it doesn't see "this.Input" anymore. I know this because I can run this line and replace "this.Input" with the real value and it returns the element fine.
As you can see I have it creating a text box and span dynamically and setting their IDs for future use, this is because I previously tried to save the element itself in the class and use that inside the event - same issue, so I tried to find it in the document each event instead by using document.getElementByID().
Weirdly also it does not seem to focus the text box it just created (if that may be part of the issue too I don't know).
This project was created entirely from nothing and uses no libraries.
So my question is, can you please explain why the element's event cannot see any variables in the class that created it? And how could I fix this?
I have not yet seen this posted as many people are using a library such as JQuery and/or not using classes, is question is specific to event handling with help from internal variables of a class.
Although it may not make too much sense, it is normal in JavaScript because the value of this has changed within the local function you have declared. Therefore, you must understand properly how to declare and use functions in JavaScript:
Functions in JavaScript has something called context, which just means that every function is bounded to a specific object. If we declare a function in your script, it will be bounded to the global object (which is window in browsers).
However, if we try to declare a method (a function that belongs to an object),
this will be a magic variable that represents the object itself. Everytime you invoke this.my_property, you would get the value of the property of your object, either an "attribute" or a "method".
Therefore, the function's context can be changed, as it happens with event handlers. In your scenario, event handlers are functions whose context has changed, so everytime you access to this, its value will be the HTMLElement object that receives the event when you click on it.
To solve this problem, you can declare an auxiliary variable and use it inside of the handler. The common practice followed by the community is:
var that = this; // Also, the people use 'self' instead of 'that'
span.onclick = function() {
var input = that.input;
// ...
};
Using this workaround, you will not have any problem.
The value of 'this' inside the click function is the element clicked. Here that would be the <span> you just created.
You are wanting the value of the this back when you defined the click function. (The question might also be: What is the value of 'this' when the SpanInputPair function is called? You may want to consider that.)
You can do that by something like this which adds it to the closure:
var thisInput = this.Input;
...
span.onclick = function() {
var input = getInput(thisInput);
You are going to have the same problem with this.Span on the next line.
function gResults () {
var TestVar = myform.inputbox.value;
var url = TestVar;
document.myform.reset();
window.location=TestVar;
}
Assume a text box with a url in it.
Will that function always grab the form's input? Are the function's list of things always done in order and saved in memory? Or will the window.location always go to null?
Strings are pass by value. That script would correctly set window.location to equal myForm.inputbox.value from before it was reset.
url will get the value of TestVar at the time the line was executed, i.e. before the call to reset(), see this example.
But why would you reset the form if you're redirecting the page anyway?
I am trying to change the value of the onblur attribute of a text input after the page has finished loading.
When I do the following, it simply fires the function:
ip.onblur = stopCalcUpInt(this,10);
When I do the following, I have success:
ip.onblur = function onblur(event){stopCalcUpInt(this,10);}
Unfortunately, the whole point of this is to be able to dynamically set the second parameter for stopCalcUpInt(). If I hard code a value for it... it works fine... but any attempts to pass varibles to this fails... instead of putting the value of the variable as the second param it just puts the plain text of the variable name itself. Here is ideally what I am TRYING to do:
ip.onblur = function onblur(event){stopCalcUpInt(this,this.value);}
In this example, when I alert the ip.onblur I get:
It depends what this is intended to refer to. In an event handler this refers to the element on which the event is being handled. If that's what you want then your code looks good as written; this will point to ip.
If you intend this to refer to the this from outside the event handler and not ip then try this:
var self = this;
ip.onblur = function(event) { stopCalcUpInt(self, self.value); };
The answer to getting this to work was super easy, yet not overly obvious. Instead of:
ip.onblur = function onblur(event){stopCalcUpInt(this,this.value);}
I did this:
ip.setAttribute('onblur','stopCalcUpInt(this,\'' + ip.value + '\');');
Works perfectly... no more banging my head against the wall! Yay!
ip.onblur = function() {stopCalcUpInt(this,this.value);}
ip.onblur is an event handler... i.e. it's a function
Now, when you alert a function, FF will show you the source code for that function (if it's user defined).
That is why you're seeing the plain text of the variable name.
For an event handler, this is the element that is currently handling the event. So, if you're setting the onblur handler of an input box, you will have access to the contents of that input box.
The code sample that you provided:
ip.onblur = function onblur(event){stopCalcUpInt(this,this.value);}
should work correctly. Try
ip.onblur = function onblur(event){alert(this.value); stopCalcUpInt(this,this.value);}
if you want to be sure
Is stopCalcUpInt expecting a number in the second parameter? The value attribute will return a String, while in your hardcoded example you're passing a number type. Try this:
ip.onblur = function onblur(event){stopCalcUpInt(this,this.value * 1);}
As explained in QuirksMode:
Since multiplying assumes numbers,
JavaScript makes the string a number,
if possible.