25

In Javascript, you can extend existing classes by using its prototype object:

String.prototype.getFirstLetter = function() {
    return this[0];
};

Is it possible to use this method to extend DOM elements?

nickf
  • 537,072
  • 198
  • 649
  • 721
  • 6
    You can. But probably shouldn't :) http://perfectionkills.com/whats-wrong-with-extending-the-dom/ – kangax Apr 06 '10 at 15:56
  • 1
    Actually, things have changed in the last three years: https://github.com/nbubna/mind-hacking/blob/gh-pages/extending-the-dom.md – Nathan Bubna Oct 21 '13 at 16:28

5 Answers5

25

I found the answer just as I was writing the question, but thought I'd post anyway to share the info.

The object you need to extend is Element.prototype.

Element.prototype.getMyId = function() {
    return this.id;
};
nickf
  • 537,072
  • 198
  • 649
  • 721
21

you can extend the DOM by using the Element's prototype. However, this does not work in IE7 and earlier. You will need to extend the specific element, one at a time. The Prototype library does this. I recommend looking through the source to see exactly how it's done.

geowa4
  • 40,390
  • 17
  • 88
  • 107
  • Note that this should only be required for IE6 and IE7, as IE8 supports this. See http://msdn.microsoft.com/en-us/library/dd282900(VS.85).aspx. – Michael Madsen Apr 23 '09 at 01:02
  • yeah, IE8 has made great headway with DOM manipulation, but fully supporting it isn't quite atop my priority list right now – geowa4 Apr 23 '09 at 01:05
7

You shouldn't be directly extending anything (by "anything" I mean native DOM objects) - that will only lead to bad things. Plus re-extending every new element (something you'd have to do to support IE) adds additional overhead.

Why not take the jQuery approach and create a wrapper/constructor and extend that instead:

var myDOM = (function(){
    var myDOM = function(elems){
            return new MyDOMConstruct(elems);
        },
        MyDOMConstruct = function(elems) {
            this.collection = elems[1] ? Array.prototype.slice.call(elems) : [elems];
            return this;
        };
    myDOM.fn = MyDOMConstruct.prototype = {
        forEach : function(fn) {
            var elems = this.collection;
            for (var i = 0, l = elems.length; i < l; i++) {
                fn( elems[i], i );
            }
            return this;
        },
        addStyles : function(styles) {
            var elems = this.collection;
            for (var i = 0, l = elems.length; i < l; i++) {
                for (var prop in styles) {
                    elems[i].style[prop] = styles[prop];
                }
            }
            return this;
        }
    };
    return myDOM;
})();

Then you can add your own methods via myDOM.fn ... And you can use it like this:

myDOM(document.getElementsByTagName('*')).forEach(function(elem){
    myDOM(elem).addStyles({
        color: 'red',
        backgroundColor : 'blue'
    });
});
James
  • 109,676
  • 31
  • 162
  • 175
  • 14
    "You shouldn't be directly extending anything - that will only lead to bad things." It will lead to good things if you have a good reason for doing it and write your code properly. Apart from interoperability issues, there's no reason not to extend Element.prototype; for other default objects, a lot of good can be had (see adding formatting methods to Date.prototype, or escaping to RegExp.prototype). It's silly to forego perfectly useful OO practices when they're available and useful. – eyelidlessness Apr 23 '09 at 08:55
  • 2
    We're not talking about the JavaScript language and design patterns or anything like that. This is about the DOM - extending native DOM objects is not a good idea; not to mention the fact that it doesn't work properly in all browsers... – James Apr 23 '09 at 09:13
  • It used to be a bad idea, not so bad now. https://github.com/nbubna/mind-hacking/blob/gh-pages/extending-the-dom.md – Nathan Bubna Aug 13 '13 at 22:27
  • @James idiomatic usage is not equivalent to good usage. Conversely an anti-idiomatic pattern is not equivalent to bad usage. Whatever CAN be done is worth exploring for use cases. Paradigms are tools for convenient framing, not laws we are beholden to. – That Realty Programmer Guy Jun 01 '22 at 00:25
1

In case someone's looking for this (I know I was), Here's the JavaScript types of all HTML tags:

interface HTMLElementTagNameMap {
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "applet": HTMLAppletElement;
    "area": HTMLAreaElement;
    "article": HTMLElement;
    "aside": HTMLElement;
    "audio": HTMLAudioElement;
    "b": HTMLElement;
    "base": HTMLBaseElement;
    "bdi": HTMLElement;
    "bdo": HTMLElement;
    "blockquote": HTMLQuoteElement;
    "body": HTMLBodyElement;
    "br": HTMLBRElement;
    "button": HTMLButtonElement;
    "canvas": HTMLCanvasElement;
    "caption": HTMLTableCaptionElement;
    "cite": HTMLElement;
    "code": HTMLElement;
    "col": HTMLTableColElement;
    "colgroup": HTMLTableColElement;
    "data": HTMLDataElement;
    "datalist": HTMLDataListElement;
    "dd": HTMLElement;
    "del": HTMLModElement;
    "details": HTMLDetailsElement;
    "dfn": HTMLElement;
    "dialog": HTMLDialogElement;
    "dir": HTMLDirectoryElement;
    "div": HTMLDivElement;
    "dl": HTMLDListElement;
    "dt": HTMLElement;
    "em": HTMLElement;
    "embed": HTMLEmbedElement;
    "fieldset": HTMLFieldSetElement;
    "figcaption": HTMLElement;
    "figure": HTMLElement;
    "font": HTMLFontElement;
    "footer": HTMLElement;
    "form": HTMLFormElement;
    "frame": HTMLFrameElement;
    "frameset": HTMLFrameSetElement;
    "h1": HTMLHeadingElement;
    "h2": HTMLHeadingElement;
    "h3": HTMLHeadingElement;
    "h4": HTMLHeadingElement;
    "h5": HTMLHeadingElement;
    "h6": HTMLHeadingElement;
    "head": HTMLHeadElement;
    "header": HTMLElement;
    "hgroup": HTMLElement;
    "hr": HTMLHRElement;
    "html": HTMLHtmlElement;
    "i": HTMLElement;
    "iframe": HTMLIFrameElement;
    "img": HTMLImageElement;
    "input": HTMLInputElement;
    "ins": HTMLModElement;
    "kbd": HTMLElement;
    "label": HTMLLabelElement;
    "legend": HTMLLegendElement;
    "li": HTMLLIElement;
    "link": HTMLLinkElement;
    "main": HTMLElement;
    "map": HTMLMapElement;
    "mark": HTMLElement;
    "marquee": HTMLMarqueeElement;
    "menu": HTMLMenuElement;
    "meta": HTMLMetaElement;
    "meter": HTMLMeterElement;
    "nav": HTMLElement;
    "noscript": HTMLElement;
    "object": HTMLObjectElement;
    "ol": HTMLOListElement;
    "optgroup": HTMLOptGroupElement;
    "option": HTMLOptionElement;
    "output": HTMLOutputElement;
    "p": HTMLParagraphElement;
    "param": HTMLParamElement;
    "picture": HTMLPictureElement;
    "pre": HTMLPreElement;
    "progress": HTMLProgressElement;
    "q": HTMLQuoteElement;
    "rp": HTMLElement;
    "rt": HTMLElement;
    "ruby": HTMLElement;
    "s": HTMLElement;
    "samp": HTMLElement;
    "script": HTMLScriptElement;
    "section": HTMLElement;
    "select": HTMLSelectElement;
    "slot": HTMLSlotElement;
    "small": HTMLElement;
    "source": HTMLSourceElement;
    "span": HTMLSpanElement;
    "strong": HTMLElement;
    "style": HTMLStyleElement;
    "sub": HTMLElement;
    "summary": HTMLElement;
    "sup": HTMLElement;
    "table": HTMLTableElement;
    "tbody": HTMLTableSectionElement;
    "td": HTMLTableDataCellElement;
    "template": HTMLTemplateElement;
    "textarea": HTMLTextAreaElement;
    "tfoot": HTMLTableSectionElement;
    "th": HTMLTableHeaderCellElement;
    "thead": HTMLTableSectionElement;
    "time": HTMLTimeElement;
    "title": HTMLTitleElement;
    "tr": HTMLTableRowElement;
    "track": HTMLTrackElement;
    "u": HTMLElement;
    "ul": HTMLUListElement;
    "var": HTMLElement;
    "video": HTMLVideoElement;
    "wbr": HTMLElement;
}

Taken from "lib.dom.d.ts" (shipped with vs-code).

1

Yes you can, but it is strongly advised not to.

If you override something another library is expecting to be the original or another library overwrote something you were expecting .. chaos!

It is best practice to keep your code in your own namespace/scope.

Chad Grant
  • 44,326
  • 9
  • 65
  • 80
  • I would agree that you must be aware of interfering with other libraries -- however there are cases where it is desirable to boldly claim names without prefix. This is especially the case if you are producing a module with functionality you wish were included in the standard. For example many situations with custom elements and web components should avoid vendor prefixes when the *goal* is interoperability across various systems, platforms and frameworks. Better to emulate the standard-to-come as if your library is a shiv for future features. – That Realty Programmer Guy Jun 01 '22 at 00:41