Overriding DOM Methods

Update: This article is old. You might not even remember that there was a time when you had to support IE6, but that's when it was written. My current recommendation for over-riding DOM Methods (if you need to do it at all) is to use DOM Prototypes. And if a browser doesn't support DOM prototypes then just treat it as though it doesn't support javascript.

This article is an analysis of how to add or override methods on HTML DOM objects.

How can I add my own methods to DOM objects?

All the major browsers allow you to extend HTML DOM objects (documents, elements, events, etc) with your own methods. For this purpose you can treat DOM objects as Javascript objects so, for example, do this:

var form = document.getElementsByTagName("form")[0];
form.alertURL = function() {
  alert(this.action); // same as alert(form.action)
}

In the method this refers to the DOM object the method is attached to (in this case a form element).

Recent versions of Firefox, Safari and Opera also enable you to add methods to DOM prototypes, meaning all DOM objects of a certain type can be extended at once. For example

HTMLFormElement.prototype.alertURL = function() {
  alert(this.action);
}

is the equivalent of

Array.forEach(
  document.getElementsByTagName("form"),
  function(form) {
    form.alertURL = function() {
      alert(this.action);
    }
  }
});

Actually it isn't quite the same - if you dynamically add a <form> element to the page then the first approach (using HTMLFormElement.prototype) will automatically attach the method where the second approach leaves it up to you.

Notes:

What if I want to override built-in DOM methods?

This is exactly the same as extending DOM objects with your own method. If you don't want to allow programmable submission of a form you could use

form.submit = function() {
  alert("Preventing attempted form submission.");
} 
Notes:

What if I want to call the built-in DOM method from my override?

This is the point at which the browsers really diverge.

Firefox, Opera and Safari-3 allow you to call the method on the DOM prototype (if you are overriding a method on the object). You need to call the prototype method with .call(this) or more generically .apply(this, arguments) so that the method is executed in the context of the DOM object. For example, if you want the user to confirm before allowing programmable submission of a form:

form.submit = function() {
  if (!confirm("Automatic submission of this form has been requested. You okay with that?")) return;
  HTMLFormElement.prototype.submit.apply(this, arguments);
}
Notes:

Alternatively you could save a reference to the original DOM method and call that from the overriding method. e.g.

form._submit = form.submit; // saved reference
form.submit = function() {
  if (!confirm("Automatic submission of this form has been requested. You okay with that?")) return;
  this._submit.apply(this, arguments);
}

In this example the reference is saved on the DOM object itself, meaning that it could be called as regular method:

form._submit = form.submit; // saved reference
form.submit = function() {
  if (!confirm("Automatic submission of this form has been requested. You okay with that?")) return;
  this._submit();
}

This technique can also be used on DOM prototypes, e.g.

HTMLFormElement.prototype._submit = HTMLFormElement.prototype.submit; 
HTMLFormElement.prototype.submit = function() {
  if (!confirm("Automatic submission of this form has been requested. You okay with that?")) return;
  this._submit();
}

The down-side to this is that you lose the ability to just pass on the arguments array to the original DOM method.

IE6 (and presumably other versions of IE) implement the methods on DOM objects as something like bound methods - you could store a reference to the DOM method anywhere, even in a variable or on another DOM object, and when the method is called it will still execute in the context of the original DOM object. In fact, IE DOM methods aren't even Javascript functions so, for instance, calling them with .call() or .apply() will throw an error. So the only solution on IE will look something like this:

form._submit = form.submit; // saved reference
form.submit = function() {
  if (!confirm("Automatic submission of this form has been requested. You okay with that?")) return;
  this._submit();
}

Safari-2 (if you need to support it) implements methods on DOM objects differently again. They are neither normal Javascript methods (like Firefox, Safari-3, Opera) or bound-methods (like IE). However, a little experimentation reveals that if you save a reference to a DOM method on the object itself you can still call it as a normal method of the object.

form._submit = form.submit; // saved reference
form.submit = function() {
  if (!confirm("Automatic submission of this form has been requested. You okay with that?")) return;
  this._submit();
}

But is there a simple cross-browser way to override DOM methods while still allowing the original methods to be called?

Yes. Despite differing implementations of DOM methods there is one solution that does work in all the leading browsers.

If you copy the original DOM method on the original DOM object with a different method name then the copy can be called in the same way as the original method, even if the original method is overridden.

form._submit = form.submit; // saved reference
form.submit = function() {
  if (!confirm("Automatic submission of this form has been requested. You okay with that?")) return;
  this._submit();
}

Although this approach works cross-browser, it would be preferrable to use DOM prototypes by default and fallback to modifying DOM objects when the prototypes aren't available.

How do I override methods on XML DOM objects in Internet Explorer?

Ummm... I don't think you can do that.

How do I add properties to DOM objects? How do I override built-in properties?

That's a question for another time.