Can I subclass a DOM-class?

后端 未结 4 1864
Happy的楠姐
Happy的楠姐 2021-01-18 02:23

I was wondering if I can create a subclass of HTMLDivElement. Like this.

MyDivElement.prototype.pickColor = function()
{
    return this.picked;
}
function M         


        
相关标签:
4条回答
  • 2021-01-18 03:18

    In some browsers, you can extend the prototype, in others, no. I'll let you guess the ones where you can't. :-) That's not really the same as extending a DOM element, but it does let you do a certain subset of the things for which you might want that facility. The thing is, DOM elements aren't really JavaScript entities; they're only simulacrums provided by the runtime system. (Maybe someday all the jsdom work will actually come to fruition.)

    Well ok I'll tell you about the problematic browsers: IE doesn't like that at all. However others do. If you've ever looked at the Prototype library, you'll come across a manifestation of that fact all the time via nasty irritating IE-only bugs when you forget to Prototype-ize a DOM element reference.

    (IE9 may be different, but I sort-of doubt it.)

    This is the kind of thing that's dirt simple to test over at jsfiddle.

    0 讨论(0)
  • 2021-01-18 03:25

    new HTMLDivElement(); throws a TypError "Illegal constructor" in Chrome - so it's not possible.

    Update: I've tested in other current browsers, and they throw various types of errors - but they all throw.


    Actually, this would work:

    function MyDivElement() {
        this.picked = 'unknowd';
    }
    
    MyDivElement.prototype = document.createElement('div');
    
    var mydiv = new MyDivElement();
    

    But I'm not sure how you could use this pattern...

    0 讨论(0)
  • 2021-01-18 03:30

    In browsers where __proto__ is exposed and mutable you can sub class DOM elements. It looks like this:

    function CustomEl () {
        var el = document.createElement('div')
        el.__proto__ = CustomEl.prototype
        return el
    }
    CustomEl.prototype.__proto__ = HTMLDivElement.prototype
    

    I also played with it in more detail on jsFiddle. Unfortunately though IE and Opera don't allow __proto__ access and have no plans to in the future as far as I know.

    0 讨论(0)
  • 2021-01-18 03:30

    I'm experimenting with this a little bit. A big difficulty is that you need the context of a document to create an element. You can go with window.document as a default, but that's boring.

    Here's the POC I'm working on:

    function CustomNode(type, parent) { 
        if (type instanceof Node) {
            // Decorate a preexisting node appropriately if called that way.
            if (arguments.length === 2 && type.ownerDocument !== parent) {
                // Import the node if it's not owned by the requested document.
                type = parent.importNode(type, true);
            }
            return Object.assign(type, this.__proto__);
        }
        //Normal flow, e.g., new CustomNode("div");
        var d = document;
        if (parent) {
            // Alt document flow, e.g., new CustomNode("div", xmlDoc);
            if (parent.nodeType === 9) {
                d = parent;
            } else {
                // Support for new CustomNode("div", parentElement);
                //  This doesn't append the element, just makes sure 
                //  the documents match
                d = parent.ownerDocument;
            }
        }
        var inst;
        // Creation flags
        if (type[0] === '#') { //text
            inst = d.createTextNode(type.substr(1));
        } else if (type[0] === '?') { //Processing instruction
            type = type.substr(1).split(' ');
            inst = d.createProcessingInstruction(type.shift(), type.join(' '));
        } else if (type[0] === '[') { // CDATA
            inst = d.createCDATASection(type.substr(1));
        } else if (type[0] === '/') { // Comment
            inst = d.createComment(type.substr(1));
        } else { //Everything else gets an element.
            inst = d.createElement(type);
        }
        // DE-COR-ATE
        Object.assign(inst, this.__proto__);
        return inst;
    }
    // Decorator for customized NodeLists; probably inefficient.  Decorates
    //   contents with CustomNode
    function CustomNodeList(list) {
        var Self = this.constructor,
            CNode = this.Node;
        return Object.assign([].map.call(list, function (node) {
            return new CNode(node);
        }), this.__proto__);
    }
    CustomNodeList.prototype = {
        // so we know how to wrap...
        Node: CustomNode,
        text: function () {
            return this[0].textContent;
        }
    };
    
    
    CustomNode.prototype = {
        // So we know how to decorate NodeLists
        NodeList: CustomNodeList,
        // So we know how to decorate Nodes
        Node: CustomNode,
        // Easy make-and-attach
        new: function (type, attach) {
            var CNode = this.Node;
            var ret = new CNode(type, this.ownerDocument);
            if (attach) {
                this.appendChild(ret);
            }
            return ret;
        },
        // NodeLists with ES5 iterators!
        find: function () {
            var CNodeList = this.NodeList;
            return new CNodeList(this.querySelectorAll.apply(this, arguments));
        },
        kids: function () {
            var CNodeList = this.NodeList;
            return new CNodeList(this.childNodes);
        }
    };
    

    Mind, this is likely a bad idea: everything in the same scope of these functions (including the elements themselves) will never get garbage collected, as the GC in most browsers is dead-stupid when it comes to elements referencing objects. I'll never use it for production for that reason alone: it's a straight-up memory leak.

    0 讨论(0)
提交回复
热议问题