I'm wondering why new HTMLElement() and others (for example new HTMLDivElement()) throw illegal constructor error.
I want codes like this:
// create a square with a given length
class Square extends HTMLDivElement {
constructor(len) {
super();
this.style.width = `${len}px`;
this.style.height = `${len}px`;
this.style.border = '1px solid black';
}
}
const square = new Square(5);
document.body.appendChild(square);
However, the above code is no less than just a wish, because the above code throws illegal constructor error.
I think there are two workarounds: (1) using $ref (2) using custom element. Please let me show.
(1) Using $ref
class Square {
constructor(len) {
const $div = document.createElement('div');
$div.style.width = `${len}px`;
$div.style.height = `${len}px`;
$div.style.border = '1px solid black';
this.$ref = $div;
}
}
const square = new Square(5); // returns Square object
document.body.appendChild(square.$ref);
This way, I can make a square, but it is inconvenient since I can't access to the div(=$ref) directly. I need to go via 'square' object first. (What I want to say is the difference between appendChild(square) and appendChild(square.$ref)) Because new HTMLDivElement() is not possible, I have no choice but to use an object which acts like a wrapper.
(2) Using (autonomous) custom element -- also uses $ref too.
class Square extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
const $div = document.createElement('div');
this.shadowRoot.appendChild($div);
this.shadowRoot.$ref = $div;
}
connectedCallback() {
this.shadowRoot.$ref.style.width = `${this.dataset.len}px`;
this.shadowRoot.$ref.style.height = `${this.dataset.len}px`;
this.shadowRoot.$ref.style.border = '1px solid black';
}
}
customElements.define('custom-square', Square);
const square = document.createElement('custom-square');
square.setAttribute('data-len', 5);
document.body.appendChild(square);
In this case, new Square(5) cannot be done because of the illegal constructor error, so I must use data-len custom attribute and connectedCallback() in order to apply data-len to $ref.style when square is appended to DOM.
Of course I can use both ways, but still I wonder why new HTMLElement() is prohibited. Are there any historical reasons for that?
Also, what makes me wonder is that even though new HTMLElement() throws illegal constructor error, how document.createElement() can make a new instance of HTMLElement (internally)? According to the first answer of this question, HTMLElement.constructor() throws an exception, not creating an object. Then how HTMLDivElement is instantiated and returned to me when I call document.createElement('div')?
In addition, I saw an answer that uses Object.create() method to create an instance of HTMLElement. It works and returns HTMLElement instance, but seems the HTMLElement instance doesn't work correctly. For example,
const test = Object.create(HTMLDivElement.prototype, {});
test.textContent = 'test'; // illegal invocation
For some reason I can get illegal invocation error. Why is that?