Quantcast
Viewing latest article 4
Browse Latest Browse All 10

Accessing the Native DOMElement's prototype in Safari 2

UPDATE:

It turns out, Safari has an interesting quirk. Simply executing this Javascript:

document.getElementsByTagName('html')[0];

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:

  1. Programmers expect var myObj = {} to be an empty object (i.e. it should have no properties). Extending Object's prototype breaks this.
  2. 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.

It's worth noting that this is not an issue in Safari 3. Extending DOM elements in that browser is easy:
HTMLElement.prototype.foo = function()
{
   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).

var DOMElemProto = window['[[DOMElement.prototype]]'];
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."

function getDOMElemProto(_retries)
{
   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.


Viewing latest article 4
Browse Latest Browse All 10

Trending Articles