Reputation: 5932
I'm creating a self-contained javascript utility object that detects advanced browser features. Ideally, my object would look something like this:
Support = {
borderRadius : false, // values returned by functions
gradient : false, // i am defining
dataURI : true
};
My current problem deals with some code I'm adapting from Weston Ruter's site which detects dataURI support. It attempts to use javascript to create an image with a dataURI source, and uses onload/onerror callbacks to check the width/height. Since onload is asynchronous, I lose my scope and returning true/false does not assign true/false to my object.
Here is my attempt:
Support = {
...
dataURI : function(prop) {
prop = prop; // keeps in closure for callback
var data = new Image();
data.onload = data.onerror = function(){
if(this.width != 1 || this.height != 1) {
prop = false;
}
prop = true;
}
data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
return -1;
}(this)
};
I'm executing the anonymous function immediately, passing this (which I hoped was a reference to Support.dataURI), but unfortunately references the window object -- so the value is always -1. I can get it to work by using an externally defined function to assign the value after the Support object is created... but I don't think it's very clean that way. Is there a way for it to be self-contained? Can the object literal's function reference the property it's assigned to?
EDIT -----------------------------------
Not exactly the answer to my question (as phrased) so I'm not going to post an additional answer, BUT... I've decided to use a singleton object instead of an object literal. Here is the working code:
Support = new function Support() {
var that = this;
this.dataURI = function() {
var data = new Image();
data.onload = data.onerror = function(){
if(this.width != 1 || this.height != 1) {
that.dataURI = false;
} else {
that.dataURI = true;
}
}
data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
return that.dataURI;
}();
};
Upvotes: 2
Views: 2504
Reputation: 4152
Can the object literal's function reference the property it's assigned to?
Yes, it can. Every function has a local argumentsvariable, which has a callee
property that references the called function. So if you wanted to reference Support.dataURI, you could simply use that:
var Support = {
...
dataURI: function(prop)
{
// Do whatever here with arguments.callee
}
};
However, I don't think that's really what you want. It looks like you're trying to reference the property name, not the value—in which case your answer is no. A JavaScript method has no way of knowing what properties it's being stored in; indeed it can be stored in multiple properties simultaneously. For example:
var Support = {
...
dataURI: function(prop)
{
}
};
Support.somethingElse = Support.dataURI;
Here, Support
has two properties that both point to the same method. The method itself, however, has no way of knowing which reference was called. And since the Support
object has yet to be declared, you have no way of referencing it.
In fact (though you ask if the "object literal's function [can] reference the property it's assigned to"), the function isn't even stored on the object: its result is. So the method itself isn't associated with Support
in any way.
I'm afraid the best you can do it declare the object, then set its dataURI
property.
First of all, you don't need the "new" keyword before "function". Still, I don't see how this could work in any browser. If you simplify it, lines 3-14 of your code have the form `this.dataURI = fn();'. Here's how a statement like this works:
fn()
dataURI
property.At the point that fn()
is being executed, dataURI
hasn't yet been assigned, so there's no way to access its (correct) value to return it.
In addition, you have an (at least potentially) asynchronous process (waiting for the image to load or error), so the value won't even have been set by the callback. Basically, you're trying to wrap an asynchronous process (onload
event) in a synchronous one (function call).
Finally, there's no need for you to create and execute a function here. Just get rid of it:
var Support = function() {
var that = this;
var data = new Image();
data.onload = data.onerror = function(){
if(this.width != 1 || this.height != 1) {
that.dataURI = false;
} else {
that.dataURI = true;
}
}
data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
};
But remember: there's still no guarantee (that I know of, at least) that the onload
and onerror
handlers will fire synchronously (immediately) when you set the source—in fact, there may even be a guarantee that they won't! So you're likely going to add a callback to your Support
object, and execute it once dataURI
is set. Then, though, you'll have to guard against the possibility that onload
or onerror
is executed synchronously. Something like this should work:
var Support = function() {
var that = this;
this.getDataURI = function()
{
var data = new Image();
data.onload = data.onerror = function(){
if(this.width != 1 || this.height != 1) {
that.dataURI = false;
} else {
that.dataURI = true;
}
if (that.onDataURI)
that.onDataURI();
}
data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
}
};
var mySupport = new Support();
mySupport.onDataURI = function() { alert(mySupport.dataURI); }
mySupport.getDataURI();
Upvotes: 3
Reputation: 11683
I guess you should use prop instead of this within your function. (You call the function passing this as a parameter for the argument prop - but within the function you use "this"?).
dataURI : function(prop) {
var data = new Image();
data.onload = data.onerror = function(){
if(prop.width != 1 || prop.height != 1) {
that = false;
}
that = true;
}
data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
return -1;
}(this);
Upvotes: 0
Reputation: 887797
You cannot reference an object while declaring it.
Instead, you need to call your function after declaring the object:
var Support = {
...
};
(function() {
var data = new Image();
data.onload = data.onerror = function(){
Support.dataURI = this.width === 1 && this.height === 1;
};
data.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
})();
Upvotes: 2