The function to render my canvas is a prototyped method of a class, like this:
Engine.prototype.renderCameras = function() {
console.log('render ok');
}
When I try to run this code directly, it works fine:
engine.renderCameras()
>>> render ok
When I try to run it using requestAnimationFrame, in either Chrome or Firefox, I get this:
window.requestAnimFrame(engine.renderCameras())
>>> render ok
>>> Error: Component returned failure code: 0x80570009 (NS_ERROR_XPC_BAD_CONVERT_JS) [nsIDOMWindow.mozRequestAnimationFrame]
It runs, but it always throws an error. That's not cool.
When I try to run it like this:
window.requestAnimFrame(engine.renderCameras)
>>> 0
It just does nothing.
I was able to solve this problem by using a closure, but I'd still like to know why I can't pass a function like that to requestAnimationFrame.
window.requestAnimFrame(engine.renderCameras())
is not passing a function to requestAnimFrame, it is passing the return value of engine.renderCameras to requestAnimFrame. The return value is probably not a function and that's why you get this error.
window.requestAnimFrame(engine.renderCameras)
instead correctly passes a function reference, but then this [docs] inside renderCameras won't refer to engine. If you rely on that (which I assume based on the setup), you either have to pass a function calling engine.renderCameras properly:
window.requestAnimFrame(function(){
engine.renderCameras();
});
or use .bind [docs] to set (and bind) this explicitly:
window.requestAnimFrame(engine.renderCameras.bind(engine));
Either way, you have to repeatedly call window.requestAnimFrame to get the next animation frame, which means you typically use a recursive function. For example:
window.requestAnimFrame(function render(){
engine.renderCameras();
window.requestAnimFrame(render);
});
Related
I have a problem with QlikView in the browser: I have a listbox and try to access it using an initialize script.
The script is registered by using the InitWorkbench function, using its BodyOnLoadFunctionNames parameter. So far, this works, and the initializer is run at startup.
Inside the initializer I try to do the following:
var doc = Qv.GetCurrentDocument();
var listbox = doc.GetObject('LB01');
Afterwards, when I have a look at listbox.Type, unfortunately it is undefined. If I delay execution of this query, it correctly says LB, hence apparently the query works - but only when it is executed delayed.
So, obviuosly there's a timing problem, and it seems as if the initializer runs too early (or I am doing something wrong).
Can anybody point out what the solution is (or give me a hint on what I am doing wrong)?
Okay, I've found the solution: The internal update function did not run yet, and all the values are only available once this function ran, so you need to provide a callback to the call to GetObject (that gets called after the update function):
var doc = Qv.GetCurrentDocument();
var listbox = doc.GetObject('LB01', function () {
console.log(listbox.Type); // => 'LB'
});
I would love some insight into the error I am seeing in Safari and Chrome with the following line of code:
setTimeout(window.location.reload, 250);
Chrome reports:
Uncaught TypeError: Illegal invocation
And Safari:
TypeError: Type error
In FireFox, the code runs fine. Also, this code runs fine in each of the three browsers:
setTimeout((function() {
window.location.reload();
}), 250);
Chrome and Safari have no issues with this code:
var say_hello = function () { alert("hello") };
setTimeout(say_hello, 250);
What is special about window.location.reload that causes this error?
(not sure if it's useful or not, but here's a jsfiddle illustrating this)
Because reload() needs window.location as this. In other words - it is a method of window.location. When you say:
var fun = window.location.reload;
fun();
You are calling reload() function without any this reference (or with implicit window reference).
This should work:
setTimeout(window.location.reload.bind(window.location), 250);
The window.location.reload.bind(window.location) part means: take window.location.reload function and return a function that, when called, will use window.location as this reference inside reload().
See also
How can I pass an argument to a function called using setTimeout?
Why doesn't console.log work when passed as a parameter to forEach?
Preserve 'this' reference in javascript prototype event handler
There is another way of doing this, pretty simple, and no need of doing extra steps, bindings or stuff like that. When you use arrow functions instead of common functions in JS, the this context is included. So you could just as easily do the following:
setTimeout(() => window.location.reload(), 250);
Because this must be bound to location when you call reload. It's same as trying:
var reload = window.location.reload;
reload();
this would be window in non-strict mode and undefined in strict mode which are both invalid.
in non-old browsers you can do instead:
reload.call( location )
or in your example:
setTimeout( window.location.reload.bind( window.location ), 1000 )
Older IEs don't support explicit binding on host objects though.
You also get this for some native methods which are not generic such as:
var a = function(){}.toString;
a();
TypeError: Function.prototype.toString is not generic
Some are generic:
var fakeArray = {0:1,1:2,length:2};
fakeArray.join = [].join;
fakeArray.join( " " );
"1 2"
This fails because you're missing the location context (the function's this), when passing it your way.
You would have to bind the context, before you can use it like this, for example with the underscore.js bind method
var boundReload = _.bind(window.location.reload, window.location);
setTimeout(boundReload, 500)
It's the same with any other function that is usually called from it's containing object like console.log
I want to run some JS code when an image's CSS property "display" has been changed by any other JS script/functions. Is there any method to monitor that change and setup a callback function?
$(this).bind.('propertychange', function(){})
cannot do this, and setInterval is also a bad idea.
What else could be done?
This is what you are looking for:
document.documentElement.addEventListener('DOMAttrModified', function(e){
if (e.attrName === 'style') {
console.log('prevValue: ' + e.prevValue, 'newValue: ' + e.newValue);
}
}, false);
This is inside the legacy JavaScript files that you do not want to modify:
// this is your original, unmodified function
function originalFunction(sel) {
alert(sel);
$(sel).css("display","none");
}
This is in your code:
// here is a sample callback function you pass into the extended function below
function myCallback(s) {
alert("The image with src = '" + $(s).attr("src") + "' has been modified!");
}
// here is how you can extend the function to do what you want
// without needing to modify the actual code above
originalFunction = (function(legacyFn, callback) {
// 1 arg function to be returned and reassigned to originalFunction
return function(sel) {
// call "original" originalFunction, with alert and image hide.
legacyFn(sel);
if(callback) callback(sel); // invoke your callback
}
})(originalFunction, myCallback);
The variable originalFunction is assigned a function that takes one argument. The function that takes one argument is returned by an anonymous, self-executing function that takes 2 arguments, the reference to the originalFunction before it is modified, and the reference to the callback function. These two function references become "locked" inside the closure so that when the originalFunction is then assigned a new value by the self-executing function, the legacyFn parameter still contains a reference to the originalFunction prior to it being modified.
In summary, at a higher level, originalFunction and myCallback are passed in as parameters to the self-executing anonymous function and are passed into the variables legacyFn and callback, and a new function is then assigned to originalFunction.
Now, when you call originalFunction('.someClassOnAnImage'), the legacyFn will fire, which will alert the selector and set the display property to none. Afterwards, the callback function, if it exists, will fire, and you'll then see:
The image with src = '.someClassOnAnImage' has been modified!
While this isn't as nice as a hypothetical or platform-specific addEventListener, it does allow you to modify the behavior of the functions in the legacy code without having to physically crack open those files and modify them. This simply extends the functions to perform additional behaviors but without needing to modify the original functions or even the original files for that matter.
You could neatly include all of your extensions in a separate JavaScript file (or whatever JavaScript file you're working in) and if you ever want to go back to the original behavior, you simply remove your extended functions.
The Answer: See this other post >> is there an alternative to DOMAttrModified that will work in webkit
The Rant:
The DOM Mutation events hold the key to your problem. However, in the new wave of browser wars, Wekit and Gecko can't agree on stuff. While Gecko has DOMAttrModified, webkit has something called mutation observer (which breaks the pattern of event handlers being attached to events but hey who cares for consistency when we want to lock users/coders in right? ;)
P.S: Just adding this here for future seekers of the same wisdom.
Building upon Jeff's suggestion, I would recommend writing a single function that modifies the image style property and then using that function as the bottleneck that all other functions must go through to modify that image style property.
function showImage(selector, callback) {
$(selector).css("display","block");
if(callback)
callback();
}
function hideImage(selector, callback) {
$(selector).css("display","none");
if(callback)
callback();
}
Something like the above two functions can be invoked from anywhere in your JavaScript when you must change the image CSS property. The functions also take a function as a parameter, which would be executed afterwards assuming the function was passed in as the 2nd parameter.
You could further simplify this into a single function, but I'll leave that to you as I don't know exactly what your goals are in doing this.
I have a function which makes an AJAX request to a server and returns relevant information after it completes.
I have another function which manipulates some variables in its namespace based on the returned information.
Currently, I am appending a 'callback' argument to the first function, which is called when the request completes. This, however, blurs the purpose of the first function - instead of being a 'getInfo' function, it's become a 'getInfoAndDo' function.
Ideally, I'd like to call the second function (a 'do' function, which calls the first function, a 'get' function) and does its thing.
I have looked around and found jQuery methods such as .ajaxStop and .ajaxComplete, but they seem to only to work when bound to DOM elements. Is there any way to do this entirely in javascript?
e.g.
function _getEventAttendance(uid, callback) {
var attendQuery = FB.Data.query('SELECT eid,rsvp_status,start_time FROM event_member WHERE uid = {0}', uid);
FB.Data.waitOn( [attendQuery],
function (args){
callback(args[0]);
}
);
}
function logAttendance(attendance){
console.log(attendance);
}
Currently, I am doing:
_getEventAttendance(123456789, logAttendance);
which seems ridiculous to me.
Is there a way to write the code such that I can change the code snippet inside _getEventAttendance / remove the callback argument:
FB.Data.waitOn( [attendQuery],
function (args){
return args[0];
}
);
and then make calls that are equivalently as simple as :
logAttendance.ajaxComplete(_getEventAttendance(123456789));
(I'm just making up the syntax for this, I have no idea how it's supposed to be written.)
$.when(<AJAX Request>).then(function(response){...});
Optionally use $.pipe() to filter response first.
When calling a Javascript function, it seems like JS gives priority to functions without parameters first, even if I have the same function name with parameters.
The strange behavior only happens in the following scenario:
I have a an HTML page with embedded Javascript, like this:
//Javascript in the page
function testAbc(){
alert('testAbc no params');
}
//Javascript in common.js
function testAbc(x){
alert('testAbc with param:'+x);
}
function testAbcFunc(x){
testAbc(x);
}
Now from somewhere in the page, im calling testAbcFunc from the common.js expecting it to call testAbc with parameter which is the common function. But strangely, JS calls back the function in the original page without params!!
I have been debugging this bug fore few hours now, and i tried this short code to reproduce the bug, it does happen each time.
NOTE: if all functions are in the same page, the correct function (with params) will be called, but when ther are split between the page and the JS file. JS seems to give priority to the function in the page even though is doesn't have parameter
JavaScript does not support method overloading based on parameters. It simply uses the last-defined function if multiple functions have the same name. The version in the page will override the included version. When it worked for you, I assume that the include version (with the argument signature) was inlined after the original.
JavaScript doesn't have overloaded function. It doesn't care about signatures, it calls functions solely by names and nothing else. It is strange that later function does not completely hide the first one but well, there's no spec about that behaviour.
So just don't do that, check the number of params with arguments.length inside the function and don't try to use overloading which will never work.
function testAbc(){
if (arguments.length == 0) {
alert('testAbc no params');
} else {
var x = arguments[0];
alert('testAbc with param:'+x);
}
}
There is no function overloading in JavaScript. If you are defining a function with two times with diffrent number of parameters the last one to be defined will be called.
Also, you should be namespacing your JavaScript.
Like so:
var common = {
testABC: function () {
//Stuff
}
};
Then call testABC like this
common.testABC();