19

I am making a DOM based game for the first time. I would like to extend HTMLDivElement, however in TypeScript, HTMLDivElement is an Interface.

I would like to do this pseudo class:

class QuizElement extends HTMLDivElement{

}

Sorry if this question is crazy. I am somewhat new to the DOM, and just thought, I can extend any visual class in any other environment so I guess it is do-able here!

Clark
  • 2,598
  • 3
  • 27
  • 41
  • 1
    It's not crazy at all. It's actually quite possible, and much better than using document.getElementById to associate a div with a class! You can try it by extending HTMLElement. It works! See more docs here: https://developer.mozilla.org/en-US/docs/Web/Web_Components/Custom_Elements/Custom_Elements_with_Classes – Kokodoko Jul 18 '17 at 14:00

5 Answers5

15

You can't extend HTMLDivElement because it isn't declared as a class. This makes sense, because the underlying native type doesn't make sense to extend.

You have two alternative options.

Option 1: Implements!

Because HTMLDivElement is an interface, you can implement it...

class QuizElement implements HTMLDivElement {

You would have to implement all of the properties and methods of the interface. You probably don't want to do this.

Option 2: Delegation.

You can expose the specific properties and methods you want to make available on your QuizElement class and then delegate to an actual HTMLDivElement instance. Quick example below:

class QuizElement {
    private element: HTMLDivElement;

    constructor(id: string) {
        this.element = <HTMLDivElement>document.getElementById(id);
    }

    set innerHTML(content: string) {
        this.element.innerHTML = content;
    }
}

var quizElement = new QuizElement('quiz');

quizElement.innerHTML = 'Example';
Fenton
  • 241,084
  • 71
  • 387
  • 401
  • 1
    I will go for the second option as it seems much less hassle and cleaner! Thanks for taking the time to explain it to me! :) – Clark Jul 18 '14 at 14:06
  • why this answer is at the bottom even if it is accepted as the right answer and it has the most upvotes? – canbax Sep 27 '21 at 08:49
9

Also you can 'extend' the HTMLDivElement interface with data members if you wish, not by using extends since it is not a class, but by adding it via the interface. TypeScript interfaces are 'open ended', see page 85 of the spec under 'declaration merging'.

http://www.typescriptlang.org/Content/TypeScript%20Language%20Specification.pdf

for example, the below adds the member 'mydata' of type string to the HTMLDivElement interface.

interface HTMLDivElement {

    mydata : string;

}

// now we can assign a value

 var div = <HTMLDivElement>document.getElementById("myDiv"); 

 div.mydata = "test";
Matt
  • 74,352
  • 26
  • 153
  • 180
KnarfaLingus
  • 456
  • 2
  • 10
  • `Import declaration conflicts with local declaration of 'ReactElement'.ts(2440)` what? can you explain more? the link is broken also – OZZIE Jan 17 '23 at 12:48
5

Really late to the show, but these days yet another approach is possible. Rather than adding fields to the interface HTMLDivElement one can create a new interface like

interface QuizElement extends HTMLDivElement {
  quizScore: number;
  ...
}

and then create QuizElement objects like

const quizzi: QuizElement = Object.assign(document.createElement("div"), {
  quizScore: 0,
  ...
});

Compared to other proposed approaches, this does not "pollute" HTMLDivElement with additional fields in the scope of QuizElement.

Harald
  • 4,575
  • 5
  • 33
  • 72
3

A bit late, but apparently it's still not possible to extend HTMLDivElement. A simple way to solve extending a DIV: just use CSS to make a generic HTMLElement behave like a div.

CSS

 my-element {
    display:block;
 } 

Javascript

class MyElement extends HTMLElement{
  constructor(){
      super()
      this.innerHTML = "I behave exactly like a div"
  }
}

window.customElements.define('my-element', MyElement);
Kokodoko
  • 26,167
  • 33
  • 120
  • 197
  • Having done this, I get an error: "Constructors for derived classes must contain a 'super' call." After adding super() in the constructor, I get the following in the Chrome browser dev tools: "Uncaught TypeError: Illegal constructor". – McCrockett Aug 07 '20 at 18:05
  • Maybe that's a browser setting or a compiler setting? It won't work if you compile to ES5. If I test this on codesandbox it works: https://codesandbox.io/s/custom-element-example-s8gxh I did have to add the super() call. – Kokodoko Aug 14 '20 at 12:19
0

It is not the best way but it works.

function MyElement(){
  var element = document.createElement("div");

  element.viewInner = function(){
    console.log(this, this.innerHTML)
  };

  return element;
}

var test = new MyElement();
test.innerHTML = "Hi";
test.viewInner(); // <div> Hi
Antonio Torres
  • 342
  • 1
  • 8
  • This just creates a `div` element, it doesn't extend the native div class to create a custom element. – Kokodoko Sep 17 '21 at 09:39