How to pass parameters to a function declared like left = function() - javascript

How can I pass parameters to a function declared like something = function(){};
window.prototype.initInterface = function(){
this.mainPane = document.createElement('div');
this.mainPane.style.border="5px solid grey";
this.mainPane.style.margin="0px";
this.mainPane.style.width="420px";
this.mainPane.style.height="600px";
this.exitButton = document.createElement('input');
this.exitButton.setAttribute("type", "button");
this.exitButton.setAttribute("value", "exit");
this.exitButton.onclick = function(){
document.body.removeChild(this.mainPane);
};
this.mainPane.appendChild(this.exitButton);
document.body.appendChild(this.mainPane);
}
When the user presses the exit button I want to remove the mainPane from the body of the html page.
this.exitButton.onclick = function(this.mainPage){
document.body.removeChild(this.mainPane);
};
Does not work
How can I do this?

For your exitButton.onclick function to have access to variables you create in the enveloping initInterface function you want a to create a closure in the exitButton.onclick function by returning a function that performs the action you want and passing that the variable.
exitButton.onclick = function () {
return (function() {
document.body.removeChild(mainPane);
})(mainPane);
};
Read more on how closures work here and here and see a working example fiddle.
Alternatively, you forget about closures and walk up the DOM from the button which triggers the event to your mainPane
exitButton.onclick = function() {
// in here "this" is the object that triggered the event, exitButton
document.body.removeChild(this.parentNode);
}
As an aside, window.prototype does not exist if you are doing this in a browser; window is the object at the top of prototype chain in browser scripting. You want just window.initInterface = function () {} which is the exact same thing as function initInterface() {} because everything you do in javascript in the browser becomes a property of window.

This function is the function w/o function name. It could only be used once and you may not easy to find out what parameters should be passed.
You can create another function like :
function go(a1){}
And call it like window.prototype.initInterface = go(a1);
Or you can get some DOM parameters in this unnamed function by using functions like getDocumentById("DOM ID") etc.

Related

jQuery $(this) not working when inside a function

I have this simple function that copies some html, and places it in another div.
If I put the code for the function in the click event it works fine, but when I move it into a function (to be used in multiple places) it no longer works.
Do you know why this is?
If I console.log($(this)); in the function it returns the window element.
function addHTMLtoComponent () {
var wrapper = $(this).closest(".wrapper");
var component = $(wrapper).find(".component");
var componentCodeHolder = $(wrapper).find('.target');
$(componentCodeHolder).text(component.html())
//console.log($(this));
}
$(".js_show_html").click(function () {
addHTMLtoComponent();
});
codepen here - http://codepen.io/ashconnolly/pen/ebe7a5a45f2c5bbe58734411b03e180e
Should i be referencing $(this) in a different way?
Regarding other answers, i need to put the easiest one:
$(".js_show_html").click(addHTMLtoComponent);
since you called the function manually the function doesn't know the "this" context, therefore it reverted back to use the window object.
$(".js_show_html").click(function () {
addHTMLtoComponent();
});
// Change to this
$(".js_show_html").click(function () {
// the call function allows you to call the function with the specific context
addHTMLtoComponent.call(this);
});
Ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call
this in the context of the click() event is the element clicked. In the context of the function addHTMLtoComponent this no longer is a reference to the element clicked.
Try passing the clicked object to the function to maintain the object reference.
function addHTMLtoComponent ($obj) {
var $wrapper = $obj.closest(".wrapper");
var $component = $wrapper.find(".component");
var $componentCodeHolder = $wrapper.find('.target');
$componentCodeHolder.text($component.html());
}
$(".js_show_html").click(function () {
addHTMLtoComponent($(this));
});
The special keyword this, when you call a function by itself, is the window object (which is what you observed). For the behavior you need, just add a parameter to the function that loads the appropriate context:
function addHTMLtoComponent(context) {
var wrapper = $(context).closest(".wrapper");
var component = $(wrapper).find(".component");
var componentCodeHolder = $(wrapper).find('.target');
$(componentCodeHolder).text(component.html())
//console.log($(context));
}
$(".js_show_html").click(function() {
addHTMLtoComponent(this);
});
One thing you could consider is that addHTMLtoComponent() could be made into a jQuery function itself:
$.fn.addHTMLtoComponent = function() {
return this.each(function() {
var wrapper = $(this).closest(".wrapper");
var component = $(wrapper).find(".component");
var componentCodeHolder = $(wrapper).find('.target');
componentCodeHolder.text(component.html())
});
}
Now you can call it like any other jQuery method:
$(".js_show_html").click(function () {
$(this).addHTMLtoComponent();
});
The value of this in a jQuery method will be the jQuery object itself, so you don't need to re-wrap it with $(). By convention (and when it makes sense), jQuery methods operate on all elements referred to by the root object, and they return that object for further chained operations. That's what the outer return this.each() construction does.
Inside the .each() callback, you've got a typical jQuery callback situation, with this being set successively to each member of the outer jQuery object.
You have to pass the element as parameter to this function.
eg:
<div onclick="addHTMLtoComponent ($(this))"></div>

need help understanding closures usage in this code

Here is a simplified snippet from some code I wrote for managing tablet gestures on canvas elements
first a function that accepts an element and a dictionary of callbacks and register the events plus adding other features like 'hold' gestures:
function registerStageGestures(stage, callbacks, recieverArg) {
stage.inhold = false;
stage.timer = null;
var touchduration = 1000;
var reciever = recieverArg || window;
stage.onLongTouch = function(e) {
if (stage.timer) clearTimeout(stage.timer);
stage.inhold = true;
if (callbacks.touchholdstart) callbacks.touchholdstart.call(reciever, e);
};
stage.getContent().addEventListener('touchstart', function(e) {
e.preventDefault();
calcTouchEventData(e);
stage.timer = setTimeout(function() {
stage.onLongTouch(e);
}, touchduration);
if (callbacks.touchstart) callbacks.touchholdstart.call(reciever, e);
});
stage.getContent().addEventListener('touchmove', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdmove) callbacks.touchholdmove.call(reciever, e);
} else {
if (callbacks.touchmove) callbacks.touchmove.call(reciever, e);
}
});
stage.getContent().addEventListener('touchend', function(e) {
e.preventDefault();
if (stage.timer) clearTimeout(stage.timer);
if (stage.inhold) {
if (callbacks.touchholdend) callbacks.touchholdend.call(reciever, e);
} else {
if (callbacks.touchend) callbacks.touchend.call(reciever, e);
}
stage.inhold = false;
});
}
later I call registerStageGestures on a few elements (represented by 'View' objects) in the same page. Something like:
function View() {
var self=this;
..
function InitView() {
...
registerStageGestures(kineticStage, {
touchstart: function(e) {
// do something
},
touchmove: function(e) {
// do something
},
touchendunction(e) {
// do something
},
touchholdstart: function(e) {
// do something
},
touchholdmove: function(e) {
// do something
},
touchholdend: function(e) {
// do something
},
}, self);
Everything works fine, however I'm left wondering about two things in the implementation of registerStageGestures:
First, is it necessary to make inhold, timer and onLongTouch members of the stage ? or will closures make everything works well if they are local vars in registerStageGestures ?
Second, is it necessary to call the callbacks with '.call(receiver,' syntax ? I'm doing this to make sure the callback code will run in the context of the View but I'm not sure if it's needed ?
any input is much appreciated
Thanks!
First, is it necessary to make inhold, timer and onLongTouch members
of the stage ? or will closures make everything works well if they are
local vars in registerStageGestures ?
As far as registerStageGestures() is concerned, var inhold, var timer and function onLongTouch(e) {...}. would suffice. The mechanism by which an inner function has automatic access to its outer function's members is known as "closure". You would only need to set stage.inhold, stage.timer and stage.onLongTouch if some other piece of code needs access to these settings as properties of stage.
Second, is it necessary to call the callbacks with '.call(receiver,'
syntax ? I'm doing this to make sure the callback code will run in the
context of the View but I'm not sure if it's needed ?
Possibly, depending on how those callbacks are written. .call() and .apply() are sometimes used when calling functions that use this internally. In both cases, the first parameter passed defines the object to be interpreted as this. Thus, javascript gives you the means of defining general purpose methods with no a priori assumption about the object to which those methods will apply when called. Similarly, you can call a method of an object in such a way that it acts on another object.
EDIT:
For completeness, please note that even in the absence of this in a function, .apply() can be very useful as it allows multiple parameters to be specified as elements of a single array, eg the ubiquitous jQuery.when.apply(null, arrayOfPromises)...
There are some simple answers, here.
First, closure:
Closure basically says that whatever is defined inside of a function, has access to the rest of that function's contents.
And all of those contents are guaranteed to stay alive (out of the trash), until there are no more objects left, which ere created inside.
A simple test:
var testClosure = function () {
var name = "Bob",
recallName = function () { return name; };
return { getName : recallName };
};
var test = testClosure();
console.log(test.getName()); // Bob
So anything that was created inside can be accessed by any function which was also created inside (or created inside of a function created in a function[, ...], inside).
var closure_2x = function () {
var name = "Bob",
innerScope = function () {
console.log(name);
return function () {
console.log("Still " + name);
}
};
return innerScope;
};
var inner_func = closure_2x();
var even_deeper = inner_func(); // "Bob"
even_deeper(); // "Still Bob"
This applies not only to variables/objects/functions created inside, but also to function arguments passed inside.
The arguments have no access to the inner-workings(unless passed to methods/callbacks), but the inner-workings will remember the arguments.
So as long as your functions are being created in the same scope as your values (or a child-scope), there's access.
.call is trickier.
You know what it does (replaces this inside of the function with the object you pass it)...
...but why and when, in this case are harder.
var Person = function (name, age) {
this.age = age;
this.getAge = function () {
return this.age;
};
};
var bob = new Person("Bob", 32);
This looks pretty normal.
Honestly, this could look a lot like Java or C# with a couple of tweaks.
bob.getAge(); // 32
Works like Java or C#, too.
doSomething.then(bob.getAge);
? Buh ?
We've now passed Bob's method into a function, as a function, all by itself.
var doug = { age : 28 };
doug.getAge = bob.getAge;
Now we've given doug a reference to directly use bobs methid -- not a copy, but a pointer to the actual method.
doug.getAge(); // 28
Well, that's odd.
What about what came out of passing it in as a callback?
var test = bob.getAge;
test(); // undefined
The reason for this, is, as you said, about context...
But the specific reason is because this inside of a function in JS isn't pre-compiled, or stored...
this is worked out on the fly, every time the function is called.
If you call
obj.method();
this === obj;
If you call
a.b.c.d();
this === a.b.c;
If you call
var test = bob.getAge;
test();
...?
this is equal to window.
In "strict mode" this doesn't happen (you get errors really quickly).
test.call(bob); //32
Balance restored!
Mostly...
There are still a few catches.
var outerScope = function () {
console.log(this.age);
var inner = function () {
console.log("Still " + this.age);
};
inner();
};
outerScope.call(bob);
// "32"
// "Still undefined"
This makes sense, when you think about it...
We know that if a function figures out this at the moment it's called -- scope has nothing to do with it...
...and we didn't add inner to an object...
this.inner = inner;
this.inner();
would have worked just fine (but now you just messed with an external object)...
So inner saw this as window.
The solution would either be to use .call, or .apply, or to use function-scoping and/or closure
var person = this,
inner = function () { console.log(person.age); };
The rabbit hole goes deeper, but my phone is dying...

javascript prototype class, this in jquery click

I made a javascript prototype class.
Inside a method I create an jquery click.
But inside this click I want to execute my build function.
When I try to execute a prototype function inside a jquery click it fails because jquery uses this for something else.
I tried some different things, but I couldnt get it working.
Game.prototype.clicks = function(){
$('.flip').click(function(){
if(cardsPlayed.length < 2) //minder dan 2 kaarten gespeeld
{
$(this).find('.card').addClass('flipped');
cardsPlayed.push($(this).find('.card').attr('arrayKey'));
console.log(cardsPlayed[cardsPlayed.length - 1]);
console.log(playingCards[cardsPlayed[cardsPlayed.length - 1]][0]);
if(cardsPlayed.length == 2)// two cards played
{
if(playingCards[cardsPlayed[0]][0] == playingCards[cardsPlayed[1]][0])
{ // same cards played
console.log('zelfde kaarten');
playingCards[cardsPlayed[0]][0] = 0; //hide card one
playingCards[cardsPlayed[1]][0] = 0; //hide card two
//rebuild the playfield
this.build(); //error here
}
else
{
//differend cards
}
}
}
return false;
}).bind(this);
}
The problem is that you're trying to have this reference the clicked .flip element in $(this).find('.card') as well as the Game object in this.build(). this can't have a dual personality, so one of those references needs to change.
The simplest solution, as already suggested by Licson, is to keep a variable pointing to the Game object in the scope of the click handler. Then, just use this inside the handler for the clicked element (as usual in a jQuery handler) and use self for the Game object.
Game.prototype.clicks = function() {
// Keep a reference to the Game in the scope
var self = this;
$('.flip').click(function() {
if(cardsPlayed.length < 2) //minder dan 2 kaarten gespeeld
{
// Use this to refer to the clicked element
$(this).find('.card').addClass('flipped');
// Stuff goes here...
// Use self to refer to the Game object
self.build();
}
}); // Note: no bind, we let jQuery bind this to the clicked element
};
I think you want something like this:
function class(){
var self = this;
this.build = function(){};
$('#element').click(function(){
self.build();
});
};
If I understand correctly, in modern browsers you can simply use bind:
function MyClass() {
this.foo = 'foo';
$('selector').each(function() {
alert(this.foo); //=> 'foo'
}.bind(this));
}
Otherwise just cache this in a variable, typically self and use that where necessary.

create a javascript function programmatically

Need this for the youtube api // the onStateChange callback functions!
I want to programmatically create functions which will listen to the "onStateChange" event emitted by several youtube player. Adding the listener works already:
function onYouTubePlayerReady(playerId) {
var ytpStateManager = playerId +"_StateManager";
document.getElementById(playerId).addEventListener("onStateChange", ytpStateManager );
...
The function I need to create based on the playerId variable ("ytp_1", "ytp_2", ...) is
function ytpStateManager(newState) {
ytpStateHelper(playerId , newState);
}
So the result for the playerId "ytp_1" would look like this:
function ytp_1_StateManager(newState) {
ytpStateHelper("ytp_1", newState);
}
Works also but right now I need to add them manually for each player, which is not what I need. I want to create them automatically when a new player sends a readyState event.
My problem is that it seems like these functions need to be a global functions to work properly. I tried several options for days now. My problem is that I do not know how (if there is a way) to define a global function, incl. the function name, programmatically, based on another variable.
Its a bummer that the ytp does not emit an event which includes the state AND the player/target. Would make things much easier. All this is basically the workaround as I need all to do stuff on all stateChanges.
If there is a better/simpler way, PLEASE let me know :) Otherwise a solution for this question is highly welcome.
Maybe there is a way to rerout the event, to make it more "accessible"?
I read in the spec that .addEventListener also takes a object, so I tried to bind the event to a dedicated object. But again, it did not get triggered. Feels like I tested everything ...
UPDATE
I am now switching to the iframe player (from swfobject) because that one provides an event which includes playerId and state :D Yeahhh!! After spending week with the wrong ytplayer this feels like a great advancement. Also seems like yt wants us to use the iframe player which can dynamically use html5 when supported.
You create a function that returns a function:
function createStateManager(playerId) {
return function (newState) {
ytpStateHelper(playerId , newState);
}
}
Then you call your function factory when setting up the event listener:
var player = document.getElementById(playerId);
player.addEventListener("onStateChange", createStateManager(playerId));
DEBUGGING
I'm not sure why that's not working, but here is a debugging suggestion. I suspect you may not be getting the playerId on your onYouTubePlayerReady handler.
function onYouTubePlayerReady(playerId) {
console.log('Player ready. The player id is: ' + playerId);
var ytpStateManager = playerId +"_StateManager";
var player = document.getElementById(playerId);
player.addEventListener("onStateChange", createStateManager(playerId));
}
function createStateManager(playerId) {
return function (newState) {
console.log('State changed for player ' + playerId + '. New state is ' + newState);
ytpStateHelper(playerId , newState);
}
}
Could you try that, and post what you get from both console.log calls?
1)You can create Function object new Function([params], "BODY")
So you can combine body of your function as string variable and put into as BODY
Example:
var twoNumAverage = new Function("x", "y", "return (x + y)/2")
console.log(twoNumAverage(3,7))
2)And new can create dynamically name and BODY
Example
var globalObject ={};
var nameFn ='MyNewFunction';
var createFn = function(object,functionName, Body){
object[functionName]= new Function(Body);
}
createFn(globalObject,nameFn,"return (arguments[0] + arguments[1])/2");
You can call your new function:
globalObject[nameFn](10,20);
Result: 15
Please note that in body your function you can get params via collection arguments
window["foo"+"bar"] = function(){ console.log("foobar is called"); }
Here's a way to create a named proxy function that executes another function with the context you supply.
function createNamedProxy(name, fn, context) {
var template = [
'(function #name() {',
' #name.fn.apply(#name.context || window, arguments);',
'})'
].join('').replace(/#name/g, name),
result = eval(template);
result.fn = fn;
result.context = context;
return result;
}
// Example Usage
var anonymous = function() { alert( document === this ); },
named = createNamedProxy('Named', anonymous, document);
// Will alert 'true'
named();
The solution above creates a function that can create and return a named function that executed whatever you'd like. If you don't supply context, it will assume the window object just like a normal anonymous function would. To create the solution you wanted you would do:
var varName = 'ytp_1';
window[varName + '_StateManager'] =
createNamedProxy(varName + '_StateManager', function(newState) {
ytpStateHelper(varName, newState);
});
Where varName could be any programmatic prefix you'd like. When invoking ytp_1_StateManager() you would pass in your newState value and the code would call ytpStateHelper with your variable name and the newState.
Hope this helps.

Calling an object function from onload event makes it lose the context

I wanted to call a function when all required images are loaded. The number of images is known in advance, so I tried attaching a function call to the onload event of each image and count the number of times it was called.
<html>
<head>
<script>
var tractor;
function Tractor()
{
this.init_graphics();
}
Tractor.prototype.init_graphics = function()
{
this.gr_max = 3;
this.load_count = 0;
this.loading_complete(); // #1 test call, works OK
this.img1 = new Image();
this.img1.onload = this.loading_complete; // #2 gets called, but gr_max = undefined, load_count = NaN
this.img1.src = "http://dl.dropbox.com/u/217824/tmp/rearwheel.gif"; //just a test image
}
Tractor.prototype.loading_complete = function()
{
this.load_count += 1;
alert("this.loading_complete, load_count = " + this.load_count + ", gr_max = " + this.gr_max);
if(this.load_count >= this.gr_max) {this.proceed();}
};
function start()
{
tractor = new Tractor();
}
</script>
</head>
<body onload="start();">
</body>
</html>
When it's just called from another function of the object (see #1), it works just as I expected. When, however, it's called from onload event (see #2), the variables become "undefined" or "NaN" or something. What's happening? What am I doing wrong? How do I make it work?
I don't remember ever creating my own objects in Javascript before, so I certainly deeply apologize for this "what's wrong with my code" kind of question. I used this article as a reference, section 1.2, mainly.
Just in case, I put the same code on http://jsfiddle.net/ffJLn/
bind the context to the callback:
this.img1.onload = this.loading_complete.bind(this);
See: http://jsfiddle.net/ffJLn/1/ (same as yours but with this addition)
Here's an explanation of how bind works in detail: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/bind
The basic idea is that it makes this in the bound function equal to whatever you pass as the parameter to bind.
Another option is to create a closure:
var self = this;
this.img1.onload = function() { self.loading_complete() };
Closures are functions that keep references to their context (in fact, all functions in javascript work this way). So here you are creating an anonymous function that keeps a reference to self. So this is another way to maintain context and for loading_complete to have the right this.
See: http://jsfiddle.net/ffJLn/2/ (same as yours but with the second possibility)
When #2 gets called, your this has changed. this now refers to the new Image() rather than the Tractor object.
Try changing...
this.img1.onload = this.loading_complete;
to
var that = this;
this.img1.onload = function() { that.loading_complete(); };
You can now use es6 arrow functions which provide lexical binding:
this.img1.onload = () => { this.loading_complete(); };

Categories

Resources