# Element Upgrades

[Web Components](..) ⟩ [Custom Elements]() ⟩

{% tabs %}
{% tab title="👉 相關" %}
* [\<time-formatted>](../examples/less-than-time-formatted-greater-than) - example
* [tagName vs. nodeName](../../browser/dom/type/node/tagname-vs.-nodename)
{% endtab %}

{% tab title="📘 手冊" %}
* Google ⟩ [Custom Element Upgrades](https://developers.google.com/web/fundamentals/web-components/customelements#upgrades)
* MDN ⟩&#x20;
  * Element ⟩&#x20;
    * [tagName](https://developer.mozilla.org/en-US/docs/Web/API/Element/tagName): string
    * [localName](https://developer.mozilla.org/en-US/docs/Web/API/Element/localName): [DOMString](https://developer.mozilla.org/en-US/docs/Web/API/DOMString)
  * Node ⟩&#x20;
    * [nodeName](https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeName): [DOMString](https://developer.mozilla.org/en-US/docs/Web/API/DOMString)
{% endtab %}

{% tab title="🗣 討論" %}
* StackOverflow ⟩ [What is the best way to get an element's tag name in JS?](https://stackoverflow.com/questions/2751556/what-is-the-best-way-to-get-an-elements-tag-name-in-js)
{% endtab %}
{% endtabs %}

{% hint style="info" %}
* Custom elements can be used **before** their definition is **registered**.
* The process of calling **customElements.define()** and endowing an **existing element** with a **class definition** is called "**element upgrades**".\
  📘 Google ⟩ [Element Upgrades](https://developers.google.com/web/fundamentals/web-components/customelements#upgrades)
{% endhint %}

If browser encounters **\<custom-element>** before **customElements.define()**, the element is yet [**unknown**](https://developers.google.com/web/fundamentals/web-components/customelements#unknown), just like any **non-standard tag**. [**Undefined elements**](https://developers.google.com/web/fundamentals/web-components/customelements#unknown) can be styled with CSS selector[**`:not(:defined)`**](../../css/selectors/pseudo-class).

When **customElement.define()** is called, they are “**upgraded**”: a new instance of **CustomElement** is created for each, and **`connectedCallback`** is called. They become **`:defined`**. \
:point\_right: [pseudo-class](../../css/selectors/pseudo-class)

## .whenDefined()

{% hint style="info" %}
To know when a **tag name** becomes **defined**, you can use **customElements.whenDefined()**. It returns a **Promise** that resolves when the element becomes defined.
{% endhint %}

{% tabs %}
{% tab title="whenDefined()" %}
```javascript
customElements.whenDefined('custom-element').then(() => {
  console.log('custom-element defined');
});
```
{% endtab %}

{% tab title="Promise" %}
```javascript
<share-buttons>
  <social-button type="twitter"><a href="...">Twitter</a></social-button>
  <social-button type="fb"><a href="...">Facebook</a></social-button>
  <social-button type="plus"><a href="...">G+</a></social-button>
</share-buttons>


<script>
// Fetch all the children of <share-buttons> that are not defined yet.
let undefinedButtons = buttons.querySelectorAll(':not(:defined)');

let promises = [...undefinedButtons].map(socialButton => {
  return customElements.whenDefined(socialButton.localName);
});

// Wait for all the social-buttons to be upgraded.
Promise.all(promises).then(() => {
  // All social-button children are ready.
});
</script>
```
{% endtab %}
{% endtabs %}
