Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[scoped-custom-element-registry]: Disabled attribute blocks any click on custom elements (all the WebComponents are form-associated) #547

Open
4 of 5 tasks
igomezal opened this issue Jul 24, 2023 · 3 comments

Comments

@igomezal
Copy link

Description

WebComponents defined with the scoped-custom-element-registry polyfill not receive any click event when they have the disabled attribute set.

The polyfill seems to be setting the formAssociated static getter to all the WebComponents registered with the polyfill enabled which cause all the WebComponents to behave like an input, button, textarea, etc.

return class ScopedCustomElementBase {
static get ['formAssociated']() {
return true;
}

As it enables the formAssociated property, it blocks any click from the user when the component is disabled something that should only happen on real form components like button, fieldset , optgroup , option , select, textarea and input as defined in https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/disabled

This is not the only issue detected, other users also seems to find strange behaviours with the polyfill enabled like in the issue #546

Example

<!DOCTYPE html>
 <html>
 <head>
   <script src="https://www.unpkg.com/@webcomponents/[email protected]/scoped-custom-element-registry.min.js"></script>
 </head>
 <body>
   <my-custom-webcomponent disabled></my-custom-webcomponent>

   <script>
     class MyCustomWebComponent extends HTMLElement {
       constructor() {
         super()

         const shadow = this.attachShadow({ mode: 'open' })

         shadow.innerHTML = '<p>Click here!</p>'
       }

       connectedCallback() {
        this.addEventListener('click', this.alertOnClick);
       }

       disconnectedCallback() {
        this.removeEventListener('click', this.alertOnClick);
       }

       alertOnClick() {
        alert('YOU CLICKED!');
       }
     }

     customElements.define('my-custom-webcomponent', MyCustomWebComponent)
   </script>
 </body>
 </html>

Steps to reproduce

  1. Create an element my-element
  2. Create and index.html loading the polyfill
  3. Append my-element to document.body of the previously created index.html
  4. Listen to the click event.
  5. Click on the element and it should work fine
  6. Now add the disabled attribute to my-element attached to the DOM
  7. Click again on the element, now it is not dispatching the click event.

Expected behavior

Any element which is not really associated with a form or a form element should be able to behave as a normal WebComponent and not as a form-associated WebComponent

Actual behavior

All the WebComponents are form-associated when the polyfill is loaded

Version

This is issue has been happening from the version 0.0.4 and onwards of the scoped-custom-element-registry polyfill.

Browsers affected

  • Chrome
  • Firefox
  • Edge
  • Safari
  • IE 11
@igomezal igomezal changed the title [scoped-custom-element-registry]: Disabled attribute blocks any click on custom elements (all the WebComponents behaves like form controls) [scoped-custom-element-registry]: Disabled attribute blocks any click on custom elements (all the WebComponents are form-associated) Jul 24, 2023
@michaelwarren1106
Copy link

The polyfill also prevents setting focus on disabled custom elements that explicitly have a tabindex=0 set, which is not browser default behavior.

Lit.dev playground example

Use case:

I am creating a menu-item component with role="menuitem". The WCAG spec states that disabled menu items should still be focusable even though disabled. My menu/menu-item component manages menu-item selection with roving tabindex, and applies tabindex=0 even to the menu-items with disabled applied.

As shown in the lit.dev playground example above, the polyfill prevents disabled elements from receiving focus at all, even when tabindex is present. Native elements will receive focus when disabled tabindex="0" is applied.

@stefanpearson
Copy link

stefanpearson commented Mar 19, 2024

👋 Hello!

I've also come across some obscure behaviour, which seems to be directly related to this. I'm unsure exactly how to reproduce this as a minimal example, but this polyfill setting formAssociated is breaking the event target in Firefox. When clicking on a native button in an elements shadow DOM, the event.target seems to be some other distant ancestor host within the internal handler. This only happens in Firefox, with the scoped elements polyfill active. When debugging, specifically returning false on the polyfill's formAssociated getter changes the behaviour to be expected.

Unfortunately, I think we're going to have to remove this polyfill entirely because of these unpredictable issues, and specifically go down a route of not scoping anything.… Unless there a chance this issue can be addressed?

@sorvell
Copy link
Collaborator

sorvell commented Apr 25, 2024

Unfortunately, there's not a great fix for this since formAssociated cannot be changed once set. This means the polyfill's approach of having a single natively defined element for a set of scoped elements doesn't allow a given tag to be either formAssociated or not. To support formAssociated elements, the polyfill turns this on for all elements, which causes the problems noted above.

Workaround

playground example

Before loading the polyfill, apply a script like the following. This ensures formAssociated is always false unless the given tag name has been explicitly allowed via window.formAssociatedCustomElementTags .add('form-associated-element');

<script>(function() {
  window.formAssociatedCustomElementTags = new Set();
  const nativeDefine = customElements.define.bind(customElements);
  customElements.define = (name, ctor) => {
    if (!window.formAssociatedCustomElementTags.has(name)) {
      Object.defineProperty(ctor, 'formAssociated', {value: false, configurable: true});
    }
    nativeDefine(name, ctor);
  }
})();</script>
<script src="scoped-custom-element-registry.min.js"></script>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants