webcomponents / webcomponents/polyfills

[Custom Elements] Incorrect `observedAttributes` behavior when using mixed-case attributes

Open
#380 8 comments 0 reactions 0 assignees View on GitHub
Focus Area: Standards & Polyfills Type: Bug
Dominant language
HTML
Stars
1.2k
Forks
168
PR merge metrics
No merged PRs in 30d

Description

### Description

Another issue that is somewhat related, and may be relevant to also see for context: https://github.com/webcomponents/polyfills/issues/111

With native Custom Elements, the attributes declared in the static `observedAttributes` getter _must_ be lower-case in order for the `attributeChangedCallback` to be triggered when they change. This is how the implementation in Chrome, Firefox, and Safari works.

In the following example, the `attributeChangedCallback` will never be called in browsers that natively support Custom Elements:

```js
class A extends HTMLElement {
static get observedAttributes () {
return ["fooBarBaz"];
}

attributeChangedCallback (name, oldValue, newValue) {
console.log("changed!:", {
name, oldValue, newValue
});
}
}
customElements.define("a-el", A);

const a = new A();
// Won't trigger attributeChangedCallback
console.log("setting attribute with name: 'foobarbaz'");
a.setAttribute("foobarbaz", "");

// Wait a bit since MutationObserver operates on the microtask queue
setTimeout(() => {
// *Will* trigger attributeChangedCallback but **only when using the Custom Elements polyfill**
console.log("setting attribute with name: 'fooBarBaz'");
a.setAttribute("fooBarBaz", "");
}, 1000);
```

However, using the Custom Elements polyfill, the `attributeChangedCallback` **will** be called when there is a direct case-sensitive match between the observed attribute name and the attribute named passed on to `setAttribute`. In the example above, the second call (`a.setAttribute("fooBarBaz", "")`) will trigger the `attributeChangedCallback`.

While - according to the HTML spec - attribute names can mix lower- with uppercase letters that are an ASCII case-insensitive match for the attribute's name, the actual behavior of `observedAttributes` does require, at least as evidenced by its implementation in Chromium/WebKit/Firefox, that these names are all lowercased when returned from that getter.

For lit-html/LitElement users and users of other tools and libraries that leverages Custom Elements, this can cause problems when binding to attributes or boolean attributes with mixed-case property names as is very common to do (camelCased property names that reflect to/from attributes) when leveraging the Custom Elements polyfill.

### Example

Here's a repro. Open the page, and check out the console. What is printed there depends on whether or not the polyfill is being used.

https://ce-bug-demo.surge.sh/

### Steps to reproduce

A repro example is provided in the code example above

#### Expected behavior

The behavior of `observedAttributes` aligns with native behavior.

#### Actual behavior

The behavior of `observedAttributes` does not align with native behavior.

### Version

@webcomponents/custom-elements@1.4.2

### Browsers affected

- [x] Chrome
- [x] Firefox
- [x] Edge
- [x] Safari
- [x] IE 11

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.