It turns out, Safari has an interesting quirk. Simply executing this Javascript:
is enough to make the "[[DOMElement.prototype]]" property come into existence. This appears to be true regardless of how early in the page load cycle you execute the Javascript, so perhaps the whole polling script I suggest below is unnecessary after all.
For the longest time, I thought it was just completely impossible to access the prototype of the native DOMElement
(or HTMLElement
in most other browsers) in Safari 2.
As an alternative, I'd see people extend the Object
object's prototype in order to add functionality to DOM elements (heck, I've done this myself -- shame on me). This is problematic for a couple of reasons:
- Programmers expect
var myObj = {}
to be an empty object (i.e. it should have no properties). ExtendingObject
's prototype breaks this. - Iterating an object's properties via
for .. in
now requires the use of.hasOwnProperty(..)
to ensure you're not accessing properties you don't intend to access.
I searched high and low, and I experimented (numerous times) trying to find some way of accessing the native DOMElement
in Safari 2, but always to no avail... until now.
{
alert('foo');
};
// assume elem is a reference to some valid DOM element
elem.foo(); // alerts "foo"
At long last, a solution for Safari 2
Credit totally goes to my co-worker, Derrick Quan, as he is the one who discovered the initial solution.
It turns out there there is a property on the window
object in Safari 2 which is identified by the string "[[DOMElement.prototype]]"
(case sensitive and double brackets on both sides required).
Now you can't access this directly because [[DOMElement.prototype]]
is not a valid variable name. It doesn't have to be though, since it lives as a property of some other object (the window
object in this case).
This means that we can access "[[DOMElement.prototype]]" by treating it like what it is (a property on the window
object).
DOMElemProto.foo = function()
{
alert('foo');
};
// assume elem is a reference to a valid DOM element
elem.foo(); // alerts "foo"
There is "gotcha" however (maybe not -- see the UPDATE note at the beginning of this post).
It seems that the [[DOMElement.prototype]]
property is not available until the DOM is fully loaded, which means if you want to be confident in accessing the property, you'll need to do it, either in a window "load" listener, or by using a DOMContentLoaded-type script (see Hedger Wang's post: An alternative for DOMContentLoaded on Internet Explorer and/or Stuart Langridge's: DOMContentLoaded for IE, Safari, everything, without document.write).
Alternatively, you could poll for the existence of the [[DOMElement.prototype]]
property, and take action once it finally "comes alive."
{
this.retries = this.retries || _retries;
if ((this.count = (this.count || 0) + 1) > _retries)
{
// abort
return;
}
var DOMElemProto = window['[[DOMElement.prototype]]'];
if (typeof DOMElemProto === 'undefined')
{
setTimeout(getDOMElemProto, 100);
}
else
{
extendDOMElements(DOMElemProto);
}
}
function extendDOMElements(_proto)
{
// Extend the DOMElement.prototype as necessary here
_proto.foo = function()
{
alert('foo');
};
_proto.bar = function()
{
alert('bar');
}
}
getDOMProto(50);
After a few retries, Safari2 should be able to access [[DOMElement.prototype]]
, while other browsers will give up after 50 tries.
Now I haven't done extensive testing on this, but initial tests seem to indicate that this works quite well. And it doesn't resort to extending the Object
object's prototype at all, so users of this script should not have to worry about object literals containing unexpected properties.
I think this is pretty cool.
Comments, questions, suggestions, complaints... all welcome.