最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Subclassing from (extending) an HTML element? - Stack Overflow

programmeradmin0浏览0评论

I'd love it if I could make my own class that extended an HTML element class, e.g. HTMLDivElement or HTMLImageElement.

Assuming the standard inheritance pattern:

// class A:

function ClassA(foo) {
    this.foo = foo;
}

ClassA.prototype.toString = function() {
    return "My foo is " + this.foo;
};

// class B:

function ClassB(foo, bar) {
    ClassA.call(this, foo);
    this.bar = bar;
}

ClassB.prototype = new ClassA();

ClassB.prototype.toString = function() {
    return "My foo/bar is " + this.foo + "/" + this.bar;
};

I'm not able to figure out what to call in the constructor:

function Image2() {
    // this doesn't work:
    Image2.prototype.constructor.call(this);
    // neither does this (only applies to <img>, no equivalent for <span> etc.):
    Image.call(this);
}

Image2.prototype = new Image(); // or document.createElement("img");

Image2.prototype.toString = function() {
    return "My src is " + this.src;
};

Is this just not possible? Or is there some way? Thanks. =)

I'd love it if I could make my own class that extended an HTML element class, e.g. HTMLDivElement or HTMLImageElement.

Assuming the standard inheritance pattern:

// class A:

function ClassA(foo) {
    this.foo = foo;
}

ClassA.prototype.toString = function() {
    return "My foo is " + this.foo;
};

// class B:

function ClassB(foo, bar) {
    ClassA.call(this, foo);
    this.bar = bar;
}

ClassB.prototype = new ClassA();

ClassB.prototype.toString = function() {
    return "My foo/bar is " + this.foo + "/" + this.bar;
};

I'm not able to figure out what to call in the constructor:

function Image2() {
    // this doesn't work:
    Image2.prototype.constructor.call(this);
    // neither does this (only applies to <img>, no equivalent for <span> etc.):
    Image.call(this);
}

Image2.prototype = new Image(); // or document.createElement("img");

Image2.prototype.toString = function() {
    return "My src is " + this.src;
};

Is this just not possible? Or is there some way? Thanks. =)

Share Improve this question asked Sep 6, 2009 at 0:12 Aseem KishoreAseem Kishore 10.9k11 gold badges53 silver badges59 bronze badges 4
  • Javascripts prototypal inheritance is very different from anything us OO junkies are used to. What exactly is the desired result? :D – Gordon Gustafson Commented Sep 6, 2009 at 0:30
  • It's the same inheritance you're used to (anywhere you can use Image, you should be able to plug in Image2, and Image2 should have all properties/methods of Image plus more); it's just written differently in Javascript. That, and there are no private/protected properties/methods in this style. – Aseem Kishore Commented Sep 6, 2009 at 0:41
  • 1 Does this answer help? stackoverflow.com/questions/1489738/… – kangax Commented Oct 1, 2009 at 15:12
  • @kangax Sorry for the late reply, but unfortunately not. I meant "extend" as in "subclass <foo> from <div>"; that poster is asking "extend" as in "add methods to the <div> prototype". – Aseem Kishore Commented Mar 20, 2011 at 6:51
Add a comment  | 

5 Answers 5

Reset to default 6

Alright, this question needs an update from the current technology.

HTML5 offers ways for you to create custom elements. You need to register your element via document.registerElement and after that you can use it just like any other tags. The feature is already available in the latest Chrome. I'm not sure about other browsers.

More details here:

http://www.html5rocks.com/en/tutorials/webcomponents/customelements/

I'm not able to figure out what to call in the constructor

Image2.prototype.constructor.call(this);

Yeah, JavaScript's objects don't have classes and chainable constructors like that. It has its own model of prototypes, which can be quite confusing, but doesn't actually deliver the proper potential of prototype inheritance because it's still tied to constructor-functions, which look like class constructors but aren't really.

unfortunately this isn't truly creating a subclass; it's just extending instances in disguise

Yes - that's all JavaScript gives you. If you want to, you can certainly create your own object system that abstracts this away so it seems like you're creating classes and instances. I've attached a flavour I often use at the bottom as an example. But there is no standard for creating a subclass of anything in JavaScript.

For example, in your "standard" code you say:

ClassA.call(this, foo);

but this only works because you have written ClassA in such a way that you can get away with calling it twice, once for the prototype ClassA, with 'foo' set to 'undefined', and then again on the object that inherits from that prototype, to mask that 'foo' behind another 'foo' over the top.

This is not anything actually standard that classes might be expected to do for each other, and it's certainly not specified that this will work for non-JS-native browser objects:

function MyImage() {
    Image.call(this); // might do anything. will almost certainly fail
}
MyImage.prototype= new Image();

In any case, setting a prototype to something like an HTMLElement that isn't a native-JavaScript object is not specified by the ECMAScript standard and has varying levels of success; in IE it doesn't work at all, the prototype's properties don't take.

Also,

Image2.prototype.constructor

from your example may actually not be the same as 'Image' as you would expect. 'constructor', apart from being a non-standard property that's not available in all browsers, does something that in a class system would be Not What You Think at all; it is best avoided.

Image2 should have all properties/methods of Image plus more

Currently the only way to do this is with a wrapper of some sort. You can't do this with prototypes for the reasons above; you'd have to add the properties to every instance separately:

function makeImage2() {
    var image= Image();
    image.isBig= function() {
        return this.width>200 || this.height>200;
    };
}

this is just a factory function of course, and not anything like subclassing. You would have to provide your own replacement for instanceof.

...

// An example class system
//
Function.prototype.subclass= function() {
    var c= new Function(
        'if (!(this instanceof arguments.callee)) throw(\'Constructor called without "new"\'); '+
        'if (arguments[0]!==Function.prototype.subclass._FLAG && this._init) this._init.apply(this, arguments); '
    );
    if (this!==Object)
        c.prototype= new this(Function.prototype.subclass._FLAG);
    return c;
};
Function.prototype.subclass._FLAG= {};

// Usage
//
ClassA= Object.subclass();
ClassA.prototype._init= function(foo) {
    this.foo= foo;
};
ClassA.prototype.toString= function() {
    return 'My foo is '+this.foo;
};

ClassB= ClassA.subclass();
ClassB.prototype._init= function(foo, bar) {
    ClassA.prototype._init.call(this, foo);
    this.bar= bar;
};
ClassB.prototype.toString= function() {
    return ClassA.prototype.toString.call(this)+' and my bar is '+this.bar;
};

This might help you. In this Javascript based game, he extended table elements in the DOM. MiniRacer on CodeProject.

And I believe he got his technique from this article by Paul Sowden.

No you can't. (At least not reliably across today's browsers down to IE6; I don't know if the situation has recently changed in cutting-edge browsers.)

The link David posted features a solution involving a factory method. It's not inheritance by any means, but it provides a convenient way to generate instances of DOM elements decorated with the properties and methods you desire. You can chain decorator calls to "build up" specialized instances.

The factory method (you don't have to use Object.defineProperty):

// Create a custom div with a setter and a method.
function newElement(){
    // Create an element to decorate.
    var el = document.createElement('div');
    // A "private" variable.
    var _myVar = null;
    // Create a setter
    Object.defineProperty(el, "myVar", {
        set : function(value){
            _myVar = value;
            this.innerHTML = value;
        }
    });
    // Create a method
    Object.defineProperty(el, "saySomething", {
        value : function(){
            alert(_myVar);
        }
    });
    return el;
}

In use:

var el = newElement();
el.myVar = "Boo!";
el.saySomething(); // alerts "Boo!"
document.body.appendChild(el); // A div with text "Boo!" is added to the body
发布评论

评论列表(0)

  1. 暂无评论