Search code examples
javascriptoopinheritanceelementwrapper

How could I create an element wrapper in Javascript?


For pure educational and curiosity purposes, I am trying to create an element-wrapper object that allows me to tack-on my own properties and methods to an element. The behavior I'm trying to simulate is basically this:

// get a button element to wrap
const button = document.querySelector('button');

// some function that wraps new properties/methods around a given element
function wrap(element) {
    this.customName = 'John';
    this.customAge = 100;

    this.printName = function() {
        console.log(this.customName);
    }

    // ...
    // ...somehow inherit element fields...
    // ...
}

// wrap the button element
const customElement = new wrap(button);

// custom behavior:
console.log(customElement.customAge) // output => 100
customElement.printName() // output => 'John'

// legacy behavior
console.log(customElement.clientHeight) // output => client height
customElement.remove() // => should still call 'remove' on the element

So, here I should be able to add my own methods/properties but still access the original fields normally. Is this even possible?

I'm using a constructor function here as an example just to demonstrate the intended behavior, but I don't actually know if this would be relevant for the solution. I'm new to Javascript and I have done a ton of research on prototypes and classes, but I'm still confused on what approach I would take here.

Edit: As Brad pointed out in the comments, I also tried this implementation using classes:

class MyButton extends HTMLButtonElement {
    constructor() {
        super();
        this.customName = 'John';
        this.customAge = 100;
    }

    printName() {
        console.log(this.customName);
    }
}

const myBtn = new MyButton();

But this resulted in the error: Uncaught TypeError: Illegal constructor


Solution

  • I haven't test this, but maybe something like this:

    // get a button element to wrap
    const button = document.querySelector('button');
    
    // some function that wraps new properties/methods around a given element
    function wrap(element) {
        Object.defineProperties(element, {
            customName: {value:"John"},
            customAge: {value:100},
            printName:{value: () => console.log(element.customName)}
        })
        return element
    }
    
    // wrap the button element
    const customElement = wrap(button);
    
    // custom behavior:
    console.log(customElement.customAge) // output => 100
    customElement.printName() // output => 'John'
    
    // legacy behavior
    console.log(customElement.clientHeight) // output => client height
    customElement.remove() // => should still call 'remove' on the element
    <button>Hello world!</button>