{"id":13451754,"url":"https://github.com/kenchris/lit-element","last_synced_at":"2025-03-09T21:24:02.518Z","repository":{"id":57143917,"uuid":"107547495","full_name":"kenchris/lit-element","owner":"kenchris","description":"A base class for creating web components using lit-html","archived":false,"fork":false,"pushed_at":"2018-09-02T08:58:10.000Z","size":114,"stargazers_count":81,"open_issues_count":13,"forks_count":10,"subscribers_count":8,"default_branch":"master","last_synced_at":"2024-04-26T16:21:00.604Z","etag":null,"topics":["components","dom","lit-html","web"],"latest_commit_sha":null,"homepage":null,"language":"HTML","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kenchris.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-10-19T13:08:05.000Z","updated_at":"2022-02-21T16:15:12.000Z","dependencies_parsed_at":"2022-09-06T00:11:56.324Z","dependency_job_id":null,"html_url":"https://github.com/kenchris/lit-element","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenchris%2Flit-element","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenchris%2Flit-element/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenchris%2Flit-element/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kenchris%2Flit-element/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kenchris","download_url":"https://codeload.github.com/kenchris/lit-element/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242753068,"owners_count":20179626,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["components","dom","lit-html","web"],"created_at":"2024-07-31T07:01:01.386Z","updated_at":"2025-03-09T21:24:02.487Z","avatar_url":"https://github.com/kenchris.png","language":"HTML","readme":"# lit-element\nA base class for creating web components using [lit-html](https://travis-ci.org/PolymerLabs/lit-html)\n\n`lit-element` can be installed via the [lit-html-element](https://www.npmjs.com/package/lit-html-element) NPM package.\n\n## Overview\n\n`lit-element` lets you create web components with [HTML templates](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/template) expressed with JavaScript [template literals](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals), and efficiently render and _re-render_ those templates to DOM.\n\n`lit-element` accomplishes this by integrating [lit-html](https://github.com/PolymerLabs/lit-html) and has the following features:\n* Depends on ES modules and Web Components (polyfills can also be used)\n* Quite small (around 1kB compressed), with only a dependency on [lit-html](https://github.com/PolymerLabs/lit-html)\n* Works great with TypeScript with additional features such as decorators.\n* Good test coverage\n* Easy rendering by implementing ```render()``` methods\n  * DOM updates are batched and rendered asynchronously\n  * Pre/post render hooks possible via ```renderCallback```\n  * Manually trigger re-rendering by calling ```invalidate()```\n  * Access properties and methods using ```this``` or destructuring\n* Allows defining properties with additional powers\n  * Content is invalidated as properties change\n  * Properties can define types used for conversion\n  * Properties can have default values\n  * Properties/attributes can auto-reflect\n    * Mapping name is up to user, no automatical case-conversion happens\n    * Default values of auto-reflected properties depend on presence of attributes\n  * Properties can be automatically calculated from other properties\n* Easy querying of element by `id` in the shadow root using `this.$(...)`\n\n### Demos\n\nDemos can be found [here](https://kenchris.github.io/lit-element/).\n\n### Basic example\n\nSimple write your HTML code using ```lit-html``` by creating a ```render()``` method.\n\n```javascript\nimport { LitElement, html } from '/src/lit-element.js';\n\nclass HelloWorld extends LitElement {\n  render() {\n    return html`\n      \u003cdiv style=\"font-weight: bold\"\u003eHello World\u003c/div\u003e\n    `;\n  }\n}\ncustomElements.define('hello-world', HelloWorld)\n```\n```html\n\u003chello-world\u003e\u003c/hello-world\u003e\n```\n\n### Example: Querying elements by `id`\n\nAfter contents has been rendered the first time (ie. after ```connectedCallback()``` fires), then you can access elements in the shadow root by ```id``` using ```this.$(...)```.\n\nIn the below example, we call ```this.changeColor()``` whenever the button is pressed, which in result accesses the div using ```this.$(\"wrapper\")``` and modifies its background color.\n\n```javascript\nclass ColorMarker extends LitElement {\n  changeColor() {\n    const color = Math.random().toString(16).substr(2, 6);\n    // Easily query the element by id:\n    this.$(\"wrapper\").style.backgroundColor = `#${color}`;\n  }\n\n  render() {\n    return html`\n      \u003cstyle\u003e\n        div {\n          background-color: yellow;\n        }\n      \u003c/style\u003e\n      \u003cbutton on-click=${() =\u003e this.changeColor()}\u003e\n        Change background color\n      \u003c/button\u003e\n      \u003cdiv id=\"wrapper\"\u003e\u003cslot\u003e\u003c/slot\u003e\u003c/div\u003e\n    `;\n  }\n}\ncustomElements.define('color-marker', ColorMarker);\n```\n```html\n\u003ccolor-marker\u003eHorse\u003c/color-marker\u003e\n```\n\n### Example: using properties\n\nIn this example we will use properties. Every property defined in the static getter ```properties()``` will make sure the content is re-rendered at the right time when modified.\n\nProperties can have default values and can even be reflected via attributes (changes go both ways). Instead of doing magic and converting cases after special rules like ```upper-case``` vs ```upperCase```, you instead define example which attribute name the property should reflect to, and thus avoid any ambiguity.\n\nNOTE, when using properties, you MUST call ```this.withProperties``` before using the elements. As the method returns the class itself, this can be done as part of ```customElements.define(...)```\n\nNOTE, attributes default values are set from the element attributes themselves (present or missing) and thus default values set via 'value' are ignored.\n\n```javascript\nimport { LitElement, html } from '/src/lit-element.js';\n\nclass HelloWorld extends LitElement {\n  static get properties() {\n    return {\n      uppercase: {\n        type: Boolean,\n        attrName: \"uppercase\"\n      }\n    }\n  }\n\n  render() {\n    return html`\n      \u003cstyle\u003e\n        .uppercase {\n          text-transform: uppercase;\n        }\n      \u003c/style\u003e\n      \u003cdiv id=\"box\" class$=\"${this.uppercase ? 'uppercase' : ''}\"\u003e\n        \u003cslot\u003eHello World\u003c/slot\u003e\n      \u003c/div\u003e\n    `;\n  }\n}\ncustomElements.define('hello-world', HelloWorld.withProperties());\n```\n```html\n\u003chello-world\u003e\u003c/hello-world\u003e\n\u003chello-world uppercase\u003e¡Hola, mundo!\u003c/hello-world\u003e\n```\n\n## Attribute reflection\n\nWhen creating custom elements, a good pattern is to use attributes instead of methods or properties. This allows using the element declaratively like ```\u003cmy-dialog opened\u003e```.\n\nFor custom elements only consumed internally in other custom elements, it is often faster just relying on properties. This is also the case if you need to pass along complex data such as arrays or objects.\n\nIn order to make it easy to work with attributes, ```lit-html-element``` supports mapping between attributes and properties automatically, just by defining the name of the attribute the property should map with via ```attrName:```.\n\nThe presence of attributes or not (on elements) results in *actual values*, ie. a missing attribute for a boolean property, means the property will be ```false``` and for all other property types, ```undefined```. This means that when mapping properties to attributes, there is no such thing as a default value as values are always defined depending on the presence, or not, of attributes. This means that setting ```value:``` is ignored when ```attrName:``` is present.\n\nValues are converted using their type constructors, ie ```String(attributeValue)``` for ```String```, ```Number(attributeValue)``` for ```Number```, etc.\n\n```Boolean``` has special handling in order to follow the patterns of the Web Platform.\n\nFrom the HTML standard:\n\n\u003e The presence of a boolean attribute on an element represents the true value, and the absence of the attribute represents the false value.\n\u003e\n\u003e If the attribute is present, its value must either be the empty string or a value that is an ASCII case-insensitive match for the attribute's canonical name, with no leading or trailing whitespace.\n\n```Array``` and ```Object``` are disencouraged for attributes and have no special handling, thus values are converted using their constructors as any other value types, except boolean.\n\n## Access element properties and methods from Destructuring\n\n```this``` is passed to render() for you, which is cleaner. particularly when destructuring. You can still reference them manually, though.\n\n```javascript\nclass RenderShorthand extends LitElement {\n  static get properties() {\n    return {\n      greeting: {\n        type: String,\n        value: \"Hello\"\n      }\n    }\n  }\n\n  render({ greeting }) {\n    return html`${greeting} World!`;\n  }\n}\ncustomElements.define('render-shorthand', RenderShorthand.withProperties());\n```\n\n## Advanced\n\n### Automatical re-rendering\n\nWhen any of the properties in ```properties()``` change, `lit-element` will automatically re-render. The same goes for attributes which are mapped to properties via ```attrName```.\n\nIf you need to re-render manually, you can trigger a re-render via a call to ```invalidate()```. This will schedule a microtask which will render the content just before next ```requestAnimationFrame```.\n\n### Element upgrading\n\nCustom elements need to be upgraded before they work. This happens automatically by the browser when it has all the resources it needs.\n\nThis mean that if you do a custom element which depends on other custom elements and use properties for data flow, then setting those properties before the element is upgraded, mean that you will end up shadowing the ```lit-html-element``` properties, meaning that the property updates and attribute reflection won't work as expected.\n\nThere is an API ```whenAllDefined(result, container)``` for working around this issue, by allowing to wait until all of the dependencies have been upgraded. One way to use it is overwriting the ```renderCallback()```:\n\n```javascript\nrenderCallback() {\n  if (\"resolved\" in this) {\n    super.renderCallback();\n  } else {\n    whenAllDefined(this.render(this)).then(() =\u003e {\n      this.resolved = true;\n      this.renderCallback();\n    });\n  }\n}\n```\n\nBut you might still manage to shadow properties if you manual set values before upgraded like\n\n```javascript\ndocument.getElementById('ninja').firstName = \"Ninja\";\n```\n\nSo guard these the following way:\n\n```javascript\ncustomElements.whenDefined('computed-world').then(() =\u003e {\n  document.getElementById('ninja').firstName = \"Ninja\";\n});\n```\n\n### Computed properties\n\nIf you need some properties that are calculated and updates depending on other properties, that is possible using the 'computed' value, which defined an object method with arguments as a string.\n\nComputed properties *only* update when *all dependent properties are defined*. Default value can be set using ```value:```\n\nNOTE, computed properties can not be reflected to attributes.\n\nEg.\n\n```javascript\nimport { LitElement, html } from '/node_modules/lit-html-element/lit-element.js';\n\nclass ComputedWorld extends LitElement {\n  static get properties() {\n    return {\n      firstName: {\n        type: String,\n        attrName: \"first-name\"\n      },\n      doubleMessage: {\n        type: String,\n        computed: 'computeDoubleMessage(message)'\n      },\n      message: {\n        type: String,\n        computed: 'computeMessage(firstName)',\n        value: 'Hej Verden'\n      }\n    }\n  }\n  computeDoubleMessage(message) {\n    return message + \" \" + message;\n  }\n  computeMessage(firstName) {\n    return `Konichiwa ${firstName}`;\n  }\n  render() {\n    return html`\n      \u003cdiv style=\"font-weight: bold\"\u003e${this.doubleMessage}\u003c/div\u003e\n    `;\n  }\n}\ncustomElements.define('computed-world', ComputedWorld.withProperties())\n```\n```html\n\u003ccomputed-world\u003e\u003c/computed-world\u003e\n\u003ccomputed-world first-name=\"Kenneth\"\u003e\u003c/computed-world\u003e\n```\n\n## Extensions for TypeScript\n\nIt is possible to use ```lit-html-element``` from TypeScript instead of JavaScript. When using TypeScript, you can opt into using decorators instead of defining the static properties accessor ```static get properties()```.\n\nWhen using property decorators any such static property accessor will be ignored, and you don't need to call ```.withProperties()``` either.\n\n```typescript\nimport {\n  LitElement,\n  html,\n  TemplateResult,\n  customElement,\n  property,\n  attribute,\n  computed\n} from '../../src/lit-element.js';\n\n@customElement('test-element')\nexport class TestElement extends LitElement {\n  @computed('firstName', 'lastName')\n  get fullName(): string {\n    return `${this.firstName} ${this.lastName}`;\n  }\n\n  @property() firstName: string = 'John';\n  @property() lastName: string = 'Doe';\n\n  @property() human: boolean = true;\n  @property() favorite: any = { fruit: 'pineapple'};\n  @property() kids: Array\u003cstring\u003e = ['Peter', 'Anna'];\n\n  @attribute('mother') mother: string;\n  @attribute('super-star') superStar: boolean;\n\n  render(): TemplateResult {\n    return html`\n      \u003ch2\u003eName: ${this.fullName}\u003c/h2\u003e\n      \u003ch2\u003eIs human?: ${human ? \"yup\" : \"nope\"}\u003c/h2\u003e\n      \u003ch2\u003eFavorites: ${JSON.stringify(this.favorite)}\u003c/h2\u003e\n      \u003ch2\u003eKids: ${JSON.stringify(this.kids)}\u003c/h2\u003e\n      \u003ch2\u003eMother: '${this.mother}'\u003c/h2\u003e\n      \u003ch2\u003eSuperstar?: '${this.superStar}'\u003c/h2\u003e\n    `;\n  }\n}\n\n```\n\n```html\n\u003ctest-element super-star mother=\"Jennifer\"\u003e\u003c/test-element\u003e\n```\n\n### How to enable\n\nIn order to use decorators from TypeScript you need to enabled the ```experimentalDecorators``` compiler setting in your ```tsconfig.json``` or use the ```--experimentalDecorators``` flag.\n\n```json\n{\n  \"compilerOptions\": {\n    \"experimentalDecorators\": true\n  }\n}\n```\n\nWith the above enabled, you can start using decorators but MUST specify the type information manually:\n\n```typescript\n@property({type: String})\nmyProperty: string;\n```\n\nAs the type often can be derives from the property, especially in TypeScript where you define the type, this feels like a bit of double work. Luckily there is a new specification proposal called [Metadata Reflection](https://rbuckton.github.io/reflect-metadata/) which aims at solving this problem. This proposal has yet to be formally proposed to the TC39 working group (defines the JavaScript standard) but there is already a working polyfill available and experimental support in TypeScript.\n\nWith Metadata Reflection enabled it is possible to define property types more concisely:\n\n```typescript\n@property() myProperty: string;\n```\n\nIn order to use decorators from TypeScript follow the following steps.\n\n1. You need to enabled the ```emitDecoratorMetadata``` compiler setting in your ```tsconfig.json``` or use the ```--emitDecoratorMetadata``` flag.\n\n```json\n{\n  \"compilerOptions\": {\n    \"emitDecoratorMetadata\": true\n  }\n}\n```\n\n2. Install the Metadata Reflection API runtime polyfill from [rbuckton/reflect-metadata](https://github.com/rbuckton/reflect-metadata):\n\n```bash\n$ npm install --save-dev rbuckton/reflect-metadata\n```\n\n3. Load the polyfill at the top-level of your application:\n\n```html\n\u003cscript src=\"/node_modules/reflect-metadata/Reflect.js\"\u003e\u003c/script\u003e\n```\n\n# API documentation\n\nThe following API documentation uses Web IDL.\n\n### Static property accessor and `PropertyOptions`\n\nPropertyOptions are used for configuring the properties for the custom element. In JavaScript you need to implement a static property accessor called `properties`, which returns an object where each property of that object has an associated `PropertyOptions`:\n\n```javascript\nclass {\n  static get properties() {\n    return { selfDefinedObjectProperty: ... }\n  }\n}\n```\n\nThe `PropertyOptions` dictionary has 4 optional properties, shown below in Web IDL format.\n\n```idl\ntypedef (BooleanConstructor or DateConstructor or NumberConstructor or StringConstructor or ArrayConstructor or ObjectConstructor) PropertyType;\n\ndictionary PropertyOptions {\n  attribute PropertyType type;\n  attribute any value;\n  attribute USVString attrName;\n  attribute USVString computed;\n}\n```\n\n#### The `type` property\nThe `type` property is only optional when using decorators and Metadata Reflection.\n\n#### The `value` property\n\nThe `value` property defines a default value for the property. In case of attribute / property mapping via `attrName` (see below), `value` is ignored. When using decorators, the value is taking from the property definition itself:\n\n```typescript\n@property() myProperty: string = \"Hello World\";\n```\n\n#### The `attrName` property\n\nThe `attrName` defines the name of the attribute which should be reflected with the property and the other way around. With `attrName`, default values are ignored and determined from the custom element instead, ie. depending on the presence or not of the attributes.\n\nThe attribute name, much be in Latin letters (a-z) including '-' (hyphen). All attributes on HTML elements in HTML documents get ASCII-lowercased automatically, and initial hyphen ('-') gets ignored.\n\nBe aware that data attributes, ie. attributes starting with `data-` are accessible as properties automatically via `element.dataset`.\n\n##### Mapping from property to attribute\n\nWhen mapped properties get set on the element, the attribute gets updated with the string representation of the new value, unless the new value is `undefined` in which the attribute gets removed.\n\nThere is one exception to this, as boolean properties as reflected differently. Setting the property to `true` and the attribute (say `attr`) is set to the empty string `''` (meaning attribute is present, ie. `\u003cdiv attr\u003e`). Setting the property to `false` and the attribute is removed, ie. `\u003cdiv\u003e`.\n\n##### Mapping from attribute to property\n\nWhen the attributes are set, the values are converted using their type constructors, ie ```String(attributeValue)``` for ```String```, ```Number(attributeValue)``` for ```Number```, etc.\n\n```Boolean``` has special handling in order to follow the patterns of the Web Platform.\n\nFrom the HTML standard:\n\n\u003e The presence of a boolean attribute on an element represents the true value, and the absence of the attribute represents the false value.\n\u003e\n\u003e If the attribute is present, its value must either be the empty string or a value that is an ASCII case-insensitive match for the attribute's canonical name, with no leading or trailing whitespace.\n\nRead more in the [Attribute reflection](#attribute-reflection) section above.\n\n#### The `computed` property\n\nProperties can be calculated from other properties using ```computed```, it takes a string like `'methodName(property1, property2)'`, where `methodName` is a method on the element and `property1` and `property2` are defined.\n\nComputed properties *only* update when *all dependent properties are defined*. Default value can be set using ```value:```\n\nNOTE, computed properties can not be reflected to attributes.\n\n### `renderCallback`\n\nThe `renderCallback` allows for custom hooks before and after rendering.\n\nIf you need to do extra work before rendering, like setting a property based on another property, a subclass can override ```renderCallback()``` to do work before or after the base class calls ```render()```, including setting the dependent property before ```render()```.\n\n### `withProperties()`\n\nTODO:\n\n### `render(HTMLElement this)`\n\nTODO: Move docs here\n\n### `async invalidate()`\n\nTODO: Move docs here\n\n### `$(DOMString id)`\n\nTODO: Move docs here\n\n### `whenAllDefined(TemplateResult result)`\n\nTODO: Move docs here\n\n## Decorators\n\n### `@customElement(USVString tagname)`\n\nA class decorator for registering the custom element\n\n```typescript\n@customElement('my-element')\nclass extends HTMLElement {\n   ...\n}\n```\n\n### `@property(optional PropertyOptions options)`\n\nA property decorator for hooking into the `lit-html-element` property system.\n\nWhen using the property decorator you don't need to define the static properties accessor ```static get properties()```.\n\nWhen using property decorators any such static property accessor will be ignored, and you don't need to call ```.withProperties()``` either.\n\n```typescript\n@property({type: String})\nmyProperty: string;\n```\n\nCheck [Extensions for TypeScript](#extensions-for-typescript) for more info.\n\n### `@attribute(USVString attrName)`\n\nA property decorator for hooking into the `lit-html-element` property system and associating a property with a custom element attribute.\n\nCheck [The `attrName` property](#the-attrname-property) for more info.\n\n### `@computed(any dependency1, any dependency2, ...)`\n\nA property decorator for hooking into the `lit-html-element` property system and create a property auto-computed from other properties.\n\nCheck [The `computed` property](#the-computed-property) for more info.\n\n### `@listen(USVString eventName, (USVString or EventTarget) target)`\n\nA method decorator for adding an event listener. You can use a string for target and it will search for an element in the shadowRoot with that `id`.\n\nEvent listeners are added after the first rendering, which creates the shadow DOM.","funding_links":[],"categories":["HTML"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkenchris%2Flit-element","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkenchris%2Flit-element","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkenchris%2Flit-element/lists"}