Search code examples
javascriptformsweb-componentcustom-elementhtml5-template

How can I create a web component that acts like a form element?


I am trying to create a web component that is usable in a form element specifically, that has a name, and a value. I recognize that I can create a web component that extendsan HTMLInputElement:

<input is="very-extended">

but I am trying to create an entirely new element.

When creating a regular web component, you can create it from the prototype of a regular HTMLElement (HTMLElement.prototype). Which leads me to assume that I can create a different element with the prototype of an HTMLInputElement (HTMLInputElement.prototype). You actually use that prototype when extending the API of input elements, so why can't I use that prototype to create an entirely new element that works in a form?

If you look at the shadow dom of a regular input field:

enter image description here

you can see that there is a div inside of there. I understand that this HTMLInputElement has methods and attributes, getters/setters, etc. So why is it that when I attempt to create my component, it fails to be part of the name, value pairs found in the form?

Here is an example of how I am trying to create this web component:

Please note that his should be tested in a browser that supports web components.

(function() {

  var iconDoc = (document._currentScript || document.currentScript).ownerDocument;
  var objectPrototype = Object.create(HTMLInputElement.prototype);

  Object.defineProperty(objectPrototype, 'name', {
    writable: true
  });

  Object.defineProperty(objectPrototype, 'value', {
    writable: true
  });

  objectPrototype.createdCallback = function() {
    var shadow = this.createShadowRoot();
    var template = iconDoc.querySelector('#test');
    shadow.appendChild(template.content.cloneNode(true));
  };

  document.registerElement('custom-input', {
    prototype: objectPrototype
  });

})();

console.log(
  $('form').serialize()
)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<template id="test">
  <div>This is a special input</div>
</template>

<form>
  <input name="regular" value="input">
  <custom-input name="foo" value="bar"></custom-input>
</form>

Why is the name, value pair not found among the form, and how can I create a custom form element?


Solution

  • You can create your <custom-input> custom element that will be interpreted by a form, just by adding inside your template a hidden input element with the name and value pair your want.

    <template>
        <input type="hidden" name="foo" value="defaultVal">
    </template>
    

    The default value (and name) can by updated by your custom element internal logic.

    This hidden input must not be inserted inside a Shadow DOM to be detected by the container form.

    (function() {
    
      var iconDoc = (document._currentScript || document.currentScript).ownerDocument;
      var objectPrototype = Object.create(HTMLInputElement.prototype);
    
      objectPrototype.createdCallback = function() {
        //var shadow = this.createShadowRoot();
        var template = iconDoc.querySelector('#test');
        this.appendChild(template.content.cloneNode(true));
      };
    
      document.registerElement('custom-input', {
        prototype: objectPrototype
      });
    
    })();
    
    console.log(
      $('form').serialize()
    )
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <template id="test">
      <input type="hidden" name="foo" value="bar">
    </template>
    
    <form>
      <input name="regular" value="input">
      <custom-input name="foo" value="bar"></custom-input>
    </form>