Search code examples
javascripthtmlcustom-element

Create a custom input element


I'm trying to create a custom component that extends the HTMLInputElement component, but nothing renders.

class myInput extends HTMLInputElement {};

customElements.define('my-input', myInput, {
  extends: 'input'
});
<my-input type="text"></my-input>

What am I missing here?


Solution

  • What you are expecting is not happening because that's not the correct way to extend an already built-in element.

    As MDN documentation states, you need to keep the built-in tag in your DOM and affect it a is attribute.

    Look at the snippet below by focusing on the spot input.

    class spotInput extends HTMLInputElement {
      constructor(...args) {
        super(...args);
        
        this.addEventListener('focus', () => {
          console.log('Focus on spotinput');
        });
      }
    };
    
    customElements.define('spot-input', spotInput, {
      extends: 'input',
    });
    <input type="text" placeholder="simple input">
    <input is="spot-input" type="text" placeholder="spot input">

    But I am guessing that you want to be allowed to use a <spot-input> tag. You can do that by attaching a shadow DOM, creating an autonomous element and append it a <input>.

    class spotInput extends HTMLElement {
      constructor(...args) {
        super(...args);
        
        // Attaches a shadow root to your custom element.
        const shadowRoot = this.attachShadow({mode: 'open'});
        
        // Defines the "real" input element.
        let inputElement = document.createElement('input');
        inputElement.setAttribute('type', this.getAttribute('type'));
        
        inputElement.addEventListener('focus', () => {
          console.log('focus on spot input');
        });
        
        // Appends the input into the shadow root.
        shadowRoot.appendChild(inputElement);
      }
    };
    
    customElements.define('spot-input', spotInput);
    <input type="number">
    <spot-input type="number"></spot-input>

    Then, if you check the DOM tree, you should have:

    <input type="number">
    
    <spot-input type="number">
        #shadow-root (open)
            <input type="number">
    </spot-input>