# \<time-formatted>

{% tabs %}
{% tab title="📗 參考" %}

* :green\_book: [Example: “time-formatted”](https://javascript.info/custom-elements#example-time-formatted) (JS.info)
* :blue\_book: [Intl.DateTimeFormat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat) (MDN)
* :floppy\_disk: [replit](https://replit.com/@pegasusroe/web-component-time-formatted#script.js)
  {% endtab %}

{% tab title="📘 手冊" %}

* [element.getAttribute(name)](https://developer.mozilla.org/en-US/docs/Web/API/Element/getAttribute) (MDN)
* [element.setAttribute(name, value)](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute) (MDN)
* Google ⟩ [Custom Element Upgrades](https://developers.google.com/web/fundamentals/web-components/customelements#upgrades)
  {% endtab %}
  {% endtabs %}

## Custom Element Upgrade

{% hint style="info" %}
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 %}

{% hint style="info" %}
If browser encounter&#x73;**`<time-formatted>`**&#x65;lements befor&#x65;**`customElements.define`**, the element is yet **unknown**, just like any **non-standard tag**.

* **undefined elements** can be styled with CSS selector[**`:not(:defined)`**](https://lochiwei.gitbook.io/web/css/selectors/pseudo-class).

When **`customElement.define`** is called, they are “**upgraded**”: a new instance of **`TimeFormatted`** is created for each, and **`connectedCallback`** is called.&#x20;

* They become **`:defined`**. :point\_right: [pseudo-class](https://lochiwei.gitbook.io/web/css/selectors/pseudo-class)
  {% endhint %}

## code

{% tabs %}
{% tab title="JS" %}

```javascript
// ⭐️ get/set attribute
//    get: elem.attr(name)
//    set: elem.attr(name, value)
Element.prototype.attr = function(name, value=undefined){
    if(value !== undefined){ this.setAttribute(name, value) };
    return this.getAttribute(name) || undefined;
};

// 1. define new element
class TimeFormatted extends HTMLElement {

    /* ---------- life cycle callbacks ---------- */

    // triggers when <time-formatted> element is added to page
    // (or when HTML parser detects it)
    connectedCallback() {
        this.render();
    }

    // observed attributes
    static get observedAttributes() { // (3)
        return [
            'datetime', 'year', 'month', 'day', 
            'hour', 'minute', 'second', 'time-zone-name'
        ];
    }

    attributeChangedCallback(name, oldValue, newValue) { 
        // re-render when attributes changed
        this.render();
    }

  /* ---------- helper methods ---------- */
  
    render() {

        let date = new Date(this.attr('datetime') || Date.now());

        // built-in Intl.DateTimeFormat formatter
        let formatter = new Intl.DateTimeFormat("default", {
        year  : this.attr('year'),
        month : this.attr('month'),
        day   : this.attr('day'),
        hour  : this.attr('hour'),
        minute: this.attr('minute'),
        second: this.attr('second'),
        timeZoneName: this.attr('time-zone-name'),
        });

        // show a nicely formatted time.
        // ⭐️ 注意：這裡設定的東西屬於 light DOM❗️
        this.innerHTML = formatter.format(date);
    }
}

// 2. register new element
customElements.define("time-formatted", TimeFormatted);

// change attribute 'datetime' per second.
setInterval(
    () => elem.attr('datetime', new Date()), 
    1000
);
```

{% endtab %}

{% tab title="HTML" %}

```markup
<time-formatted 
	id="elem"
	datetime="2019-12-01" 
	year="numeric" 
	month="long" 
	day="numeric" 
	hour="numeric" 
	minute="numeric" 
	second="numeric"
	time-zone-name="short">
</time-formatted>
```

{% endtab %}

{% tab title="📗 參考" %}

{% endtab %}
{% endtabs %}
