So, I have a question. Inside a function I have a createElement() function that creates a li. I also use the setAttribute() function to add a onclick(). But here's my question, can i store like an array inside the onclick(), so i can get the array in a new functions argument? What should i put inside 'changeClass("' + '");') ? ("names" is a global variable that contains an array. "numberOfCookies" is nothing to worry about.)
function getClassCookie() {
var cookiesArray = document.cookie.split(';');
numberOfCookies = cookiesArray.length;
for (var i = 0; i < cookiesArray.length; i++) {
var nameValueArray = cookiesArray[i].split('=');
var split = nameValueArray[1].split(',');
var className = split[0];
var copySplit = split.slice();
copySplit.splice(0, 1);
names = copySplit;
var newLi = document.createElement('li');
var textNode = document.createTextNode(className);
newLi.setAttribute('onclick', 'changeClass("' + '");');
newLi.setAttribute('onclick', 'hideDropdown();');
newLi.appendChild(textNode);
var parent = document.getElementById('classes');
parent.appendChild(newLi);
}
}
function changeClass(c) {
names = c;
}
You can use the array in the handler with a closure, if you attach the handler properly with Javascript, such as with addEventListener. (Inline handlers are generally difficult to manage and aren't considered good practice, anyway). For example, once you've declared newLi, simply reference the array you want to call changeClass with later:
newLi.addEventListener('click', function() {
changeClass(copySplit);
});
The above code will allow the copySplit that was created in that iteration of the for loop to be the one that's referenced when the newLi is clicked later. (This technique isn't possible when you use inline handlers)
One more thing - variables declared with var are hoisted and have unintuitive function scope, rather than block scope. Make sure to use const or let instead, which have block scope (that way each iteration of the loop has separate bindings for variables, rather than all iterations sharing a single set of variables):
for (let i = 0; i < cookiesArray.length; i++) {
const nameValueArray = cookiesArray[i].split('=');
const split = nameValueArray[1].split(',');
const className = split[0];
const copySplit = split.slice();
// etc
Using string concatenation for the function name while attaching it to an attribute isn't really a good approach. Just use the function hook. If you need to pass parameters into it, then you can do that as well in an anonymous call.
In the example below, note that the demo array maintains its state on subsequent clicks because of its scope.
function handler(arr){
arr.push(arr.length);
console.log(arr);
}
(function demo(){
var newButton = document.createElement('button');
newButton.textContent = "Demo";
var someArray = [];
newButton.onclick = function(){
handler(someArray);
}
document.body.appendChild(newButton);
})()
Related
I need to achieve following in my Node.js program.
How create a function array from their names provided in strings?
How to execute those functions one after another. Not asynchronously.
How to pass parameters for those functions.
Suggest me a better approach to achieve this or code snippet somewhere already implemented this.
You can do like this;
function foo(v){console.log("foo: ",v)};
function bar(v){console.log("bar: ",v)};
var funs = ["foo", "bar"],
i = 0;
while (i < funs.length) this[funs[i]](i++);
Well of course your functions definitions might reside in a particular scope and you may need to invoke them from within whatnot..! In that case you can of course wrap the above code in a function and bind it to whatever scope your function definitions are made in. Such as
var obj = {
foo: function(v){console.log("foo: ",v)},
bar: function(v){console.log("bar: ",v)}
},
funs = ["foo", "bar"],
runner = function(a){
var i = 0;
while (i < a.length) this[a[i]](i++);
}.bind(obj);
runner(funs);
Step 1. Define the list
var functions = ["foo", "bar", "baz"];
Step 2. Iterate the list
We iterate over the function list and call the __call function.
functions.forEach(function(f){
__call(f);
});
Step 3. Call the function
This step is dependent on framework. If you're using node, you have to use the global object. If you're using a browser (HTML) you have to use the window object.
For the former, use:
__call = function(f) {
var args = [];
for(var i = 1; i<arguments.length; i++){
args.push(arguments[i])
};
console.log(args);
global[f].apply(null, args);
};
For the latter, use:
__call = function(f) {
var args = [];
for(var i = 1; i<arguments.length; i++){
args.push(arguments[i])
};
console.log(args);
window[f].apply(null, args);
};
The only difference is we're using the window or global dictionary to access the function.
EDIT: Added passing parameters.
You can call a function by its name like this:
const functions = ['index', 'push'...];
functions[0]() // write your parameters inside. this will call 'index'
If those function are not async, just write the callings one after another. If they are async, use a helper like async.waterfall or async.series.
see 1.
I have a problem when trying to give an array (or variable) a new value. Inside a function, I want to give a new value to a variable pointed out by the parameters when executing the function.
var a = 0;
var b = 0;
function editvariable (target, newValue) {
var target = newValue;
}
editvariable(a, 4);
In this example, I want to change the value of a to 4, but it will only make a new function called target.
a is defined out of the function scope, so you can simply set a = newValue
But why would you want to make a function for what a simple assignment statement can do?
If you really want to have target re-assign a, you'll have to return the result.
var a=0;
var b=0;
function editvariable(target,newValue){
target=newValue;
return target; //return the assignment
}
a = editvariable(a,4)
var target=newValue;
Just remove var.
I am new to programming and I am stuck. Here is my code:
function Subtraction() {
var a = document.getElementById('inputA').value;
var b = document.getElementById('inputB').value;
var c = (parseFloat(a) - parseFloat(b)).toFixed(5);
alert(c);
}
This works fine to me, but I have many functions that waits for onclick event to be executed. And every function have the same a and b variables. How can I get them in global scope so I don't need to wait them over and over again? I tried to put them outside of the function, but what event can trigger their declaration? There is what I tried:
var a = document.getElementById('inputA').value;
var b = document.getElementById('inputB').value;
parseFloat(a).toFixed(5);
parseFloat(b).toFixed(5);
function Subtraction() {
var c = a - b;
alert(c);
}
I see two options at least:
One is to declare them after window has loaded.
Other is to pass the elements as function parameters:
1
var a,b;
window.onload = function(){
a = document.getElementById('inputA').value;
b = document.getElementById('inputB').value;
}
2
element.onclick = function(){
var a = document.getElementById('inputA').value;
var b = document.getElementById('inputB').value;
Subtraction(a, b);
};
Btw, capital letters is used for Classes, if its a normal function better to use small "s".
You can try to declare the vars in a different javascript source file or put them in an upper block the environment of the variables holds through the entire execution from the moment you declare them so if you do this:
<script src="declare_vars.js"></script>
<script src="use_vars.js"></script>
In declare_vars.js you can try doing:
var a;
var b;
and in the other scripts use them as you want and give them the values you need, they will always be available.
The value of an input is a primitive (specifically a string) and is therefore passed by value. This means that if you do this:
var oldvalue = document.getElementById('someinput').value;
document.getElementById('someinput').value = "derp";
alert(oldvalue); // it has not changed
What you can do, if you want, is cache the result of getElementById:
var inputA = document.getElementById('inputA');
var inputB = document.getElementById('inputB');
// in your function {
var c = parseFloat(inputA.value)-parseFloat(inputB.value);
// }
This works because the result of getElementById is the input element itself. You then retrieve the desired property of this element (.value) at the specific time you need it.
That said, avoid global variables. Put variables in a scope if you want, but don't make them global.
Disclaimer: this solution makes no attempt to avoid using global variables. The usage of global variables may introduce all sorts of problems, though for an application as simple as the one described by the OP the potential pitfalls are limited.
You can add the initialization in the change event handler of each input to make sure it is always up to date:
HTML
a<input id="inputA"/>
b<input id="inputB"/>
<button id="sum">sum</button>
JAVASCRIPT
var a,b;
document.getElementById('inputA').addEventListener('change',function(evt){
a = +evt.target.value;
});
document.getElementById('inputB').addEventListener('change',function(evt){
b = +evt.target.value;
});
document.getElementById('sum').addEventListener('click', function(evt){
console.log('Sum is ' + (a+b));
});
DEMO: http://jsbin.com/EZECEraR/2/edit
I have the following code:
for(var i = 0; i < nodelist.length; i++) {
var x = functionThatCreatesADivElement();
someElement.appendChild(x.getDiv()); // this works fine
nodelist[i].onclick = function() {
x.someFunction(); // this always refer to the last 'x' object
}
}
function functionThatCreatesADivElement() {
var div = document.createElement("div");
this.someFunction = function() {}
this.getDiv = function() {
return div;
}
return this;
}
the problem is that the execution of nodelist[0].onclick is exactly the same as nodelist[4].onclick (assuming that i = 4 is the last node).
I believe the references of the previously iterated are changing to point to the currently iterated element.
What is the proper way of doing this?
EDIT: Added some more code and changed the name of the function cause it was too confusing
You have two problems. The first problem is that JavaScript variables don't have block scopes.
From MDN:
When you declare a variable outside of any function, it is called a global variable, because it is available to any other code in the current document. When you declare a variable
within a function, it is called a local variable, because it is available only within that
function.
JavaScript does not have block statement scope;
You aren't enclosing a the x variable in a function, so all of your onclick callbacks are using the same x variable, which point to whatever element is last in the loop since that will be the last one to have overwritten x.
Doing this for your loop should work:
nodelist.forEach(function (nodeitem) {
var x = functionThatCreatesADivElement();
someElement.appendChild(x.getDiv());
nodeitem.onclick = function() {
x.someFunction();
}
});
The second problem is that your functionThatCreatesADivElement() constructor function is not being called correctly. Use new functionThatCreatesADivElement() since you are invoking a constructor function.
Solved. I had to use
var x = new functionThatCreatesADivElement();
function functionThatCreatesADivElement() {
var div = document.createElement("div");
this.someFunction = function() {}
this.getDiv = function() {
return div;
}
//return this; //Using new instead of returning this
}
I had a "class" defined and was making only one instance of it. The instance possessed a member function that would end up being passed around (it's a mouse handler, but that's not important). Since I would only ever make one instance of my "class", I decided to rewrite it as a singleton by using an object literal.
So I have
var mySingleton = {
theObjects : [];
}
mySingleton.mouseHandler = (function() {
var that = this;
return function (e) {
for (var indx = 0; indx < that.theObjects.length; indx++) {
// do something to that.theObjects[indx];
}
}
}());
mySingleton.addObject = function(newObj) {
this.theObjects.push(newObj);
}
However, when I try to use this code (after adding a few objects), I keep getting an that.theObjects is undefined error. It's referring to the line in the for loop.
Update for 2015 – Use Function.bind() to specify the value of this within the function. Then, instead of using that, you can use this.
mySingleton.mouseHandler = function (e) {
for (var indx = 0; indx < this.theObjects.length; indx++) {
// do something to this.theObjects[indx];
}
}.bind(mySingleton);
This doesn't work if you want mouseHandler to have the context of the 'moused' element. For that, use my original answer below.
If you need to support IE8 or (heaven forbid) earlier, you'll need to use a polyfill.
Since you are calling the function that creates mouseHandler immediately, it is run in the context of window, not mySingleton. So that refers to window. Instead of calling it immediately, just change it to a method so that it runs in the context of mySingleton:
mySingleton.getMouseHandler = function() {
var that = this;
return function() { ... };
};
myElement.onclick = mySingleton.getMouseHandler();
Of course, since you are already using a singleton, you can just reference it directly. In your click handler, instead of checking that.theObjects, check mySingleton.theObjects. Or, in mouseHandler change var that = this to var that = mySingleton.
Edit: Or, pass the context to your anonymous function when you call it:
mySingleton.mouseHandler = (function() {
var that = this;
return function (e) {
for (var indx = 0; indx < that.theObjects.length; indx++) {
// do something to that.theObjects[indx];
}
}
}).call(mySingleton);
There are a few popular ways to do this. First, super-simple solution is just reference mySingleton directly and bypass the confusion associated with this. Instead of that.theObjects just do mySingleton.theObjects and move on with your life and things will work fine.
However, there is a common pattern to do this binding. Here's how underscore.js does it
Check out the annoted source to underscore, where you will find this
_.bind = function(func, obj) {
if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));
var args = slice.call(arguments, 2);
return function() {
return func.apply(obj, args.concat(slice.call(arguments)));
};
};
The other answers here so far are also correct. Providing my viewpoint here in case it helps.
The key to understanding why the code doesn't behave as you expect requires understanding how this works in JavaScript. The problem is that this depends on how the function is called.
First, if you call the function in the method style, this is what you'd expect:
mySingleton.mouseHandler(); // this === mySingleton
If you attach the function to something esle, that works too.
var anotherSingleton = {};
anotherSingleton.foo = mySingleton.mouseHandler;
anotherSingleton.foo(); // this === anotherSingleton
If you detach the function, this becomes the global scope object (window)
var foo = mySingleton.mouseHandler;
foo(); // this === window
And finally, you can force this to be something else using call or apply:
var randomThingy = {};
mySingleton.mouseHandler.call(randomThingy); // this === randomThingy
The takeaway is that this is determined at runtime based on the context of how the function was called. Often, frameworks that allow you to make "classes" abstract these details from you by implicitly applying the bind pattern on your behalf. This is why it used to work, and no longer does.
As others have mentioned, you can change your handler to reference the variable by its scoped name (mySingleton) or otherwise bind it as discussed.
Here's an article I wrote on the subject a few years ago that goes into more detail: http://trephine.org/t/index.php?title=Understanding_JavaScript%27s_this_keyword
Hope this helps!