Search code examples
javascripthtmlweb-componentcustom-element

Clicking label not focusing custom element (Web Components)


I’m writing a custom <select> element using native DOM (no Polymer).

I’m trying to use my element with a <label> element and correctly trigger click events to my element when the <label> is clicked, i.e.:

<label>
  My Select:
  <my-select placeholder="Please select one">...</my-select>
</label>

or

<label for='mySelect1'>My Select:</label>
<my-select id='mySelect1' placeholder="Please select one">...</my-select>

However, this behavior doesn’t seem to work out of the box, even if I add a tabindex to make it focusable.

Here’s a stripped down version of the code and a JSFiddle with some basic debugging:

var MySelectOptionProto = Object.create(HTMLElement.prototype);
document.registerElement('my-select-option', {
  prototype: MySelectOptionProto
});
var MySelectProto = Object.create(HTMLElement.prototype);

MySelectProto.createdCallback = function() {
  if (!this.getAttribute('tabindex')) {
    this.setAttribute('tabindex', 0);
  }
  
  this.placeholder = document.createElement('span');
  this.placeholder.className = 'my-select-placeholder';
  this.appendChild(this.placeholder);

  var selected = this.querySelector('my-select-option[selected]');
  
  this.placeholder.textContent = selected
    ? selected.textContent
    : (this.getAttribute('placeholder') || '');
};
document.registerElement('my-select', {
  prototype: MySelectProto
});

Solution

  • Only the phrasing content elements can be targeted by <label>.

    So you'll have to manage the focus action by yourself if you want to use a non-standard (autonomous custom) element.

    Instead you can choose to define a Customized built-in element that will extend the <select> element, as in the following example: https://jsfiddle.net/h56692ee/4/

     var MySelectProto = Object.create( HTMLSelectElement.prototype )
     //...
     document.registerElement('my-select', { prototype: MySelectProto, extends: "select" } )
    

    You'll need to use the is attribute notation for HTML:

    <label>
       My Select:
       <select is="my-select" placeholder="Please select one">
           <option>...</option>
       </select>
    </label> 
    

    Update More explanations in these 2 posts: here and there.