Options
All
  • Public
  • Public/Protected
  • All
Menu

lit-html

Index

Type aliases

DirectiveFn

DirectiveFn: function

Type declaration

    • (part: Part): void
    • Parameters

      Returns void

ItemTemplate

ItemTemplate: function

Type declaration

    • (item: T, index: number): any
    • Parameters

      • item: T
      • index: number

      Returns any

KeyFn

KeyFn: function

Type declaration

    • (item: T, index: number): any
    • Parameters

      • item: T
      • index: number

      Returns any

TemplateFactory

TemplateFactory: function

A function type that creates a Template from a TemplateResult.

This is a hook into the template-creation process for rendering that requires some modification of templates before they're used, like ShadyCSS, which must add classes to elements and remove styles.

Templates should be cached as aggressively as possible, so that many TemplateResults produced from the same expression only do the work of creating the Template the first time.

Templates are usually cached by TemplateResult.strings and TemplateResult.type, but may be cached by other keys if this function modifies the template.

Note that currently TemplateFactories must not add, remove, or reorder expressions, because there is no way to describe such a modification to render() so that values are interpolated to the correct place in the template instances.

Type declaration

TemplatePart

TemplatePart: object | object

A placeholder for a dynamic expression in an HTML template.

There are two built-in part types: AttributePart and NodePart. NodeParts always represent a single dynamic expression, while AttributeParts may represent as many expressions are contained in the attribute.

A Template's parts are mutable, so parts can be replaced or modified (possibly to implement different template semantics). The contract is that parts can only be replaced, not removed, added or reordered, and parts must always consume the correct number of values in their update() method.

TODO(justinfagnani): That requirement is a little fragile. A TemplateInstance could instead be more careful about which values it gives to Part.update().

templateCache

templateCache: object

The first argument to JS template tags retain identity across multiple calls to a tag for the same literal, so we can cache work done per literal in a Map.

Safari currently has a bug which occasionally breaks this behaviour, so we need to cache the Template at two levels. We first cache the TemplateStringsArray, and if that fails, we cache a key constructed by joining the strings array.

Type declaration

Variables

Const asyncAppend

asyncAppend: (Anonymous function) = directive(<T>(value: AsyncIterable<T>,mapper?: (v: T, index?: number) => any) => async (part: Part) => {if (!(part instanceof NodePart)) {throw new Error('asyncAppend can only be used in text bindings');}// If we've already set up this particular iterable, we don't need// to do anything.if (value === part.value) {return;}part.value = value;// We keep track of item Parts across iterations, so that we can// share marker nodes between consecutive Parts.let itemPart;let i = 0;for await (let v of value) {// Check to make sure that value is the still the current value of// the part, and if not bail because a new value owns this partif (part.value !== value) {break;}// When we get the first value, clear the part. This lets the// previous value display until we can replace it.if (i === 0) {part.clear();}// As a convenience, because functional-programming-style// transforms of iterables and async iterables requires a library,// we accept a mapper function. This is especially convenient for// rendering a template for each item.if (mapper !== undefined) {v = mapper(v, i);}// Like with sync iterables, each item induces a Part, so we need// to keep track of start and end nodes for the Part.// Note: Because these Parts are not updatable like with a sync// iterable (if we render a new value, we always clear), it may// be possible to optimize away the Parts and just re-use the// Part.setValue() logic.let itemStartNode = part.startNode;// Check to see if we have a previous item and Partif (itemPart !== undefined) {// Create a new node to separate the previous and next PartsitemStartNode = createMarker();// itemPart is currently the Part for the previous item. Set// it's endNode to the node we'll use for the next Part's// startNode.itemPart.endNode = itemStartNode;part.endNode.parentNode!.insertBefore(itemStartNode, part.endNode);}itemPart = new NodePart(part.options);itemPart.insertAfterNode(itemStartNode);itemPart.setValue(v);itemPart.commit();i++;}})

A directive that renders the items of an async iterable[1], appending new values after previous values, similar to the built-in support for iterables.

Async iterables are objects with a [Symbol.asyncIterator] method, which returns an iterator who's next() method returns a Promise. When a new value is available, the Promise resolves and the value is appended to the Part controlled by the directive. If another value other than this directive has been set on the Part, the iterable will no longer be listened to and new values won't be written to the Part.

param

An async iterable

param

An optional function that maps from (value, index) to another value. Useful for generating templates for each item in the iterable.

Const asyncReplace

asyncReplace: (Anonymous function) = directive(<T>(value: AsyncIterable<T>, mapper?: (v: T, index?: number) => any) =>async (part: Part) => {if (!(part instanceof NodePart)) {throw new Error('asyncReplace can only be used in text bindings');}// If we've already set up this particular iterable, we don't need// to do anything.if (value === part.value) {return;}// We nest a new part to keep track of previous item values separately// of the iterable as a value itself.const itemPart = new NodePart(part.options);part.value = value;let i = 0;for await (let v of value) {// Check to make sure that value is the still the current value of// the part, and if not bail because a new value owns this partif (part.value !== value) {break;}// When we get the first value, clear the part. This let's the// previous value display until we can replace it.if (i === 0) {part.clear();itemPart.appendIntoPart(part);}// As a convenience, because functional-programming-style// transforms of iterables and async iterables requires a library,// we accept a mapper function. This is especially convenient for// rendering a template for each item.if (mapper !== undefined) {v = mapper(v, i);}itemPart.setValue(v);itemPart.commit();i++;}})

A directive that renders the items of an async iterable[1], replacing previous values with new values, so that only one value is ever rendered at a time.

Async iterables are objects with a [Symbol.asyncIterator] method, which returns an iterator who's next() method returns a Promise. When a new value is available, the Promise resolves and the value is rendered to the Part controlled by the directive. If another value other than this directive has been set on the Part, the iterable will no longer be listened to and new values won't be written to the Part.

param

An async iterable

param

An optional function that maps from (value, index) to another value. Useful for generating templates for each item in the iterable.

Const boundAttributeSuffix

boundAttributeSuffix: "$lit$" = "$lit$"

Suffix appended to all bound attribute names.

Const cache

cache: (Anonymous function) = directive((value: any) => (part: Part) => {if (!(part instanceof NodePart)) {throw new Error('cache can only be used in text bindings');}let templateCache = templateCaches.get(part);if (templateCache === undefined) {templateCache = new WeakMap();templateCaches.set(part, templateCache);}const previousValue = part.value;// First, can we update the current TemplateInstance, or do we need to move// the current nodes into the cache?if (previousValue instanceof TemplateInstance) {if (value instanceof TemplateResult &&previousValue.template === part.options.templateFactory(value)) {// Same Template, just trigger an update of the TemplateInstancepart.setValue(value);return;} else {// Not the same Template, move the nodes from the DOM into the cache.let cachedTemplate = templateCache.get(previousValue.template);if (cachedTemplate === undefined) {cachedTemplate = {instance: previousValue,nodes: document.createDocumentFragment(),};templateCache.set(previousValue.template, cachedTemplate);}reparentNodes(cachedTemplate.nodes, part.startNode.nextSibling, part.endNode);}}// Next, can we reuse nodes from the cache?if (value instanceof TemplateResult) {const template = part.options.templateFactory(value);const cachedTemplate = templateCache.get(template);if (cachedTemplate !== undefined) {// Move nodes out of cachepart.setValue(cachedTemplate.nodes);part.commit();// Set the Part value to the TemplateInstance so it'll update it.part.value = cachedTemplate.instance;}}part.setValue(value);})

Enables fast switching between multiple templates by caching the DOM nodes and TemplateInstances produced by the templates.

Example:

let checked = false;

html`
  ${cache(checked ? html`input is checked` : html`input is not checked`)}
`

Const classMap

classMap: (Anonymous function) = directive((classInfo: ClassInfo) => (part: Part) => {if (!(part instanceof AttributePart) || (part instanceof PropertyPart) ||part.committer.name !== 'class' || part.committer.parts.length > 1) {throw new Error('The `classMap` directive must be used in the `class` attribute ' +'and must be the only part in the attribute.');}// handle static classesif (!classMapStatics.has(part)) {part.committer.element.className = part.committer.strings.join(' ');classMapStatics.set(part, true);}// remove old classes that no longer applyconst oldInfo = classMapCache.get(part);for (const name in oldInfo) {if (!(name in classInfo)) {part.committer.element.classList.remove(name);}}// add new classesfor (const name in classInfo) {if (!oldInfo || (oldInfo[name] !== classInfo[name])) {// We explicitly want a loose truthy check here because// it seems more convenient that '' and 0 are skipped.part.committer.element.classList.toggle(name, Boolean(classInfo[name]));}}classMapCache.set(part, classInfo);})

A directive that applies CSS classes. This must be used in the class attribute and must be the only part used in the attribute. It takes each property in the classInfo argument and adds the property name to the element's classList if the property value is truthy; if the property value is falsey, the property name is removed from the element's classList. For example {foo: bar} applies the class foo if the value of bar is truthy.

param

Const defaultTemplateProcessor

defaultTemplateProcessor: DefaultTemplateProcessor = new DefaultTemplateProcessor()

Const guard

guard: (Anonymous function) = directive((value: unknown, f: () => unknown) => (part: Part): void => {const previousValue = previousValues.get(part);if (Array.isArray(value)) {// Dirty-check arrays by itemif (Array.isArray(previousValue) &&previousValue.length === value.length &&value.every((v, i) => v === previousValue[i])) {return;}} else if (previousValue === value &&(value !== undefined || previousValues.has(part))) {// Dirty-check non-arrays by identityreturn;}part.setValue(f());// Copy the value if it's an array so that if it's mutated we don't forget// what the previous values were.previousValues.set(part, Array.isArray(value) ? Array.from(value) : value);})

Prevents re-render of a template function until a single value or an array of values changes.

Example:

html`
  <div>
    ${guard([user.id, company.id], () => html`...`)}
  </div>

In this case, the template only renders if either user.id or company.id changes.

guard() is useful with immutable data patterns, by preventing expensive work until data updates.

Example:

html`
  <div>
    ${guard([immutableItems], () => immutableItems.map(i => html`${i}`))}
  </div>

In this case, items are mapped over only when the array reference changes.

param

the value to check before re-rendering

param

the template function

Const ifDefined

ifDefined: (Anonymous function) = directive((value: any) => (part: Part) => {if (value === undefined && part instanceof AttributePart) {if (value !== part.value) {const name = part.committer.name;part.committer.element.removeAttribute(name);}} else {part.setValue(value);}})

For AttributeParts, sets the attribute if the value is defined and removes the attribute if the value is undefined.

For other part types, this directive is a no-op.

Const isCEPolyfill

isCEPolyfill: boolean = window.customElements !== undefined &&(window.customElements as any).polyfillWrapFlushCallback !== undefined
license

Copyright (c) 2017 The Polymer Project Authors. All rights reserved. This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code distributed by Google as part of the polymer project is also subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt

Const lastAttributeNameRegex

lastAttributeNameRegex: RegExp = /([ \x09\x0a\x0c\x0d])([^\0-\x1F\x7F-\x9F \x09\x0a\x0c\x0d"'>=/]+)([ \x09\x0a\x0c\x0d]*=[ \x09\x0a\x0c\x0d]*(?:[^ \x09\x0a\x0c\x0d"'`<>=]*|"[^"]*|'[^']*))$/

This regex extracts the attribute name preceding an attribute-position expression. It does this by matching the syntax allowed for attributes against the string literal directly preceding the expression, assuming that the expression is in an attribute-value position.

See attributes in the HTML spec: https://www.w3.org/TR/html5/syntax.html#attributes-0

"\0-\x1F\x7F-\x9F" are Unicode control characters

" \x09\x0a\x0c\x0d" are HTML space characters: https://www.w3.org/TR/html5/infrastructure.html#space-character

So an attribute is:

  • The name: any character except a control character, space character, ('), ("), ">", "=", or "/"
  • Followed by zero or more space characters
  • Followed by "="
  • Followed by zero or more space characters
  • Followed by:
    • Any character except space, ('), ("), "<", ">", "=", (`), or
    • (") then any non-("), or
    • (') then any non-(')

Const marker

marker: string = `{{lit-${String(Math.random()).slice(2)}}}`

An expression marker with embedded unique key to avoid collision with possible text in templates.

Const markerRegex

markerRegex: RegExp = new RegExp(`${marker}|${nodeMarker}`)

Const noChange

noChange: object

A sentinel value that signals that a value was handled by a directive and should not be written to the DOM.

Type declaration

Const nodeMarker

nodeMarker: string = `<!--${marker}-->`

An expression marker used text-positions, multi-binding attributes, and attributes with markup-like text values.

Const parts

parts: WeakMap<Node, NodePart> = new WeakMap<Node, NodePart>()

Const repeat

repeat: (Anonymous function) = directive(<T>(items: Iterable<T>,keyFnOrTemplate: KeyFn<T>|ItemTemplate<T>,template?: ItemTemplate<T>): DirectiveFn => {let keyFn: KeyFn<T>;if (template === undefined) {template = keyFnOrTemplate;} else if (keyFnOrTemplate !== undefined) {keyFn = keyFnOrTemplate as KeyFn<T>;}return (containerPart: Part): void => {if (!(containerPart instanceof NodePart)) {throw new Error('repeat can only be used in text bindings');}// Old part & key lists are retrieved from the last update (associated// with the part for this instance of the directive)const oldParts = partListCache.get(containerPart) || [];const oldKeys = keyListCache.get(containerPart) || [];// New part list will be built up as we go (either reused from old parts// or created for new keys in this update). This is saved in the above// cache at the end of the update.const newParts: NodePart[] = [];// New value list is eagerly generated from items along with a parallel// array indicating its key.const newValues: unknown[] = [];const newKeys: unknown[] = [];let index = 0;for (const item of items) {newKeys[index] = keyFn ? keyFn(item, index) : index;newValues[index] = template !(item, index);index++;}// Maps from key to index for current and previous update; these are// generated lazily only when needed as a performance optimization,// since they are only required for multiple non-contiguous changes in// the list, which are less common.let newKeyToIndexMap!: Map<unknown, number>;let oldKeyToIndexMap!: Map<unknown, number>;// Head and tail pointers to old parts and new valueslet oldHead = 0;let oldTail = oldParts.length - 1;let newHead = 0;let newTail = newValues.length - 1;// Overview of O(n) reconciliation algorithm (general approach based on// ideas found in ivi, vue, snabbdom, etc.)://// * We start with the list of old parts and new values (and arrays of// their respective keys), head/tail pointers into each, and we build// up the new list of parts by updating (and when needed, moving) old// parts or creating new ones. The initial scenario might look like// this (for brevity of the diagrams, the numbers in the array reflect// keys associated with the old parts or new values, although keys and// parts/values are actually stored in parallel arrays indexed using// the same head/tail pointers)://// oldHead v v oldTail// oldKeys: [0, 1, 2, 3, 4, 5, 6]// newParts: [ , , , , , , ]// newKeys: [0, 2, 1, 4, 3, 7, 6] <- reflects the user's new item// order// newHead ^ ^ newTail//// * Iterate old & new lists from both sides, updating, swapping, or// removing parts at the head/tail locations until neither head nor// tail can move.//// * Example below: keys at head pointers match, so update old part 0// in-// place (no need to move it) and record part 0 in the `newParts`// list. The last thing we do is advance the `oldHead` and `newHead`// pointers (will be reflected in the next diagram).//// oldHead v v oldTail// oldKeys: [0, 1, 2, 3, 4, 5, 6]// newParts: [0, , , , , , ] <- heads matched: update 0 and// newKeys: [0, 2, 1, 4, 3, 7, 6] advance both oldHead & newHead// newHead ^ ^ newTail//// * Example below: head pointers don't match, but tail pointers do, so// update part 6 in place (no need to move it), and record part 6 in// the `newParts` list. Last, advance the `oldTail` and `oldHead`// pointers.//// oldHead v v oldTail// oldKeys: [0, 1, 2, 3, 4, 5, 6]// newParts: [0, , , , , , 6] <- tails matched: update 6 and// newKeys: [0, 2, 1, 4, 3, 7, 6] advance both oldTail & newTail// newHead ^ ^ newTail//// * If neither head nor tail match; next check if one of the old// head/tail// items was removed. We first need to generate the reverse map of new// keys to index (`newKeyToIndexMap`), which is done once lazily as a// performance optimization, since we only hit this case if multiple// non-contiguous changes were made. Note that for contiguous removal// anywhere in the list, the head and tails would advance from either// end and pass each other before we get to this case and removals// would be handled in the final while loop without needing to// generate the map.//// * Example below: The key at `oldTail` was removed (no longer in the// `newKeyToIndexMap`), so remove that part from the DOM and advance// just the `oldTail` pointer.//// oldHead v v oldTail// oldKeys: [0, 1, 2, 3, 4, 5, 6]// newParts: [0, , , , , , 6] <- 5 not in new map; remove 5 and// newKeys: [0, 2, 1, 4, 3, 7, 6] advance oldTail// newHead ^ ^ newTail//// * Once head and tail cannot move, any mismatches are due to either// new or// moved items; if a new key is in the previous "old key to old index"// map, move the old part to the new location, otherwise create and// insert a new part. Note that when moving an old part we null its// position in the oldParts array if it lies between the head and tail// so we know to skip it when the pointers get there.//// * Example below: neither head nor tail match, and neither were// removed;// so find the `newHead` key in the `oldKeyToIndexMap`, and move that// old part's DOM into the next head position (before// `oldParts[oldHead]`). Last, null the part in the `oldPart` array// since it was somewhere in the remaining oldParts still to be// scanned (between the head and tail pointers) so that we know to// skip that old part on future iterations.//// oldHead v v oldTail// oldKeys: [0, 1, -, 3, 4, 5, 6]// newParts: [0, 2, , , , , 6] <- stuck; update & move 2 into// place newKeys: [0, 2, 1, 4, 3, 7, 6] and advance newHead// newHead ^ ^ newTail//// * Note that for moves/insertions like the one above, a part inserted// at// the head pointer is inserted before the current// `oldParts[oldHead]`, and a part inserted at the tail pointer is// inserted before `newParts[newTail+1]`. The seeming asymmetry lies// in the fact that new parts are moved into place outside in, so to// the right of the head pointer are old parts, and to the right of// the tail pointer are new parts.//// * We always restart back from the top of the algorithm, allowing// matching// and simple updates in place to continue...//// * Example below: the head pointers once again match, so simply update// part 1 and record it in the `newParts` array. Last, advance both// head pointers.//// oldHead v v oldTail// oldKeys: [0, 1, -, 3, 4, 5, 6]// newParts: [0, 2, 1, , , , 6] <- heads matched; update 1 and// newKeys: [0, 2, 1, 4, 3, 7, 6] advance both oldHead & newHead// newHead ^ ^ newTail//// * As mentioned above, items that were moved as a result of being// stuck// (the final else clause in the code below) are marked with null, so// we always advance old pointers over these so we're comparing the// next actual old value on either end.//// * Example below: `oldHead` is null (already placed in newParts), so// advance `oldHead`.//// oldHead v v oldTail// oldKeys: [0, 1, -, 3, 4, 5, 6] // old head already used; advance// newParts: [0, 2, 1, , , , 6] // oldHead// newKeys: [0, 2, 1, 4, 3, 7, 6]// newHead ^ ^ newTail//// * Note it's not critical to mark old parts as null when they are// moved// from head to tail or tail to head, since they will be outside the// pointer range and never visited again.//// * Example below: Here the old tail key matches the new head key, so// the part at the `oldTail` position and move its DOM to the new// head position (before `oldParts[oldHead]`). Last, advance `oldTail`// and `newHead` pointers.//// oldHead v v oldTail// oldKeys: [0, 1, -, 3, 4, 5, 6]// newParts: [0, 2, 1, 4, , , 6] <- old tail matches new head:// update newKeys: [0, 2, 1, 4, 3, 7, 6] & move 4, advance oldTail// & newHead// newHead ^ ^ newTail//// * Example below: Old and new head keys match, so update the old head// part in place, and advance the `oldHead` and `newHead` pointers.//// oldHead v oldTail// oldKeys: [0, 1, -, 3, 4, 5, 6]// newParts: [0, 2, 1, 4, 3, ,6] <- heads match: update 3 and// advance newKeys: [0, 2, 1, 4, 3, 7, 6] oldHead & newHead// newHead ^ ^ newTail//// * Once the new or old pointers move past each other then all we have// left is additions (if old list exhausted) or removals (if new list// exhausted). Those are handled in the final while loops at the end.//// * Example below: `oldHead` exceeded `oldTail`, so we're done with the// main loop. Create the remaining part and insert it at the new head// position, and the update is complete.//// (oldHead > oldTail)// oldKeys: [0, 1, -, 3, 4, 5, 6]// newParts: [0, 2, 1, 4, 3, 7 ,6] <- create and insert 7// newKeys: [0, 2, 1, 4, 3, 7, 6]// newHead ^ newTail//// * Note that the order of the if/else clauses is not important to the// algorithm, as long as the null checks come first (to ensure we're// always working on valid old parts) and that the final else clause// comes last (since that's where the expensive moves occur). The// order of remaining clauses is is just a simple guess at which cases// will be most common.//// * TODO(kschaaf) Note, we could calculate the longest increasing// subsequence (LIS) of old items in new position, and only move those// not in the LIS set. However that costs O(nlogn) time and adds a bit// more code, and only helps make rare types of mutations require// fewer moves. The above handles removes, adds, reversal, swaps, and// single moves of contiguous items in linear time, in the minimum// number of moves. As the number of multiple moves where LIS might// help approaches a random shuffle, the LIS optimization becomes less// helpful, so it seems not worth the code at this point. Could// reconsider if a compelling case arises.while (oldHead <= oldTail && newHead <= newTail) {if (oldParts[oldHead] === null) {// `null` means old part at head has already been used below; skipoldHead++;} else if (oldParts[oldTail] === null) {// `null` means old part at tail has already been used below; skipoldTail--;} else if (oldKeys[oldHead] === newKeys[newHead]) {// Old head matches new head; update in placenewParts[newHead] =updatePart(oldParts[oldHead]!, newValues[newHead]);oldHead++;newHead++;} else if (oldKeys[oldTail] === newKeys[newTail]) {// Old tail matches new tail; update in placenewParts[newTail] =updatePart(oldParts[oldTail]!, newValues[newTail]);oldTail--;newTail--;} else if (oldKeys[oldHead] === newKeys[newTail]) {// Old head matches new tail; update and move to new tailnewParts[newTail] =updatePart(oldParts[oldHead]!, newValues[newTail]);insertPartBefore(containerPart, oldParts[oldHead]!, newParts[newTail + 1]);oldHead++;newTail--;} else if (oldKeys[oldTail] === newKeys[newHead]) {// Old tail matches new head; update and move to new headnewParts[newHead] =updatePart(oldParts[oldTail]!, newValues[newHead]);insertPartBefore(containerPart, oldParts[oldTail]!, oldParts[oldHead]!);oldTail--;newHead++;} else {if (newKeyToIndexMap === undefined) {// Lazily generate key-to-index maps, used for removals & moves// belownewKeyToIndexMap = generateMap(newKeys, newHead, newTail);oldKeyToIndexMap = generateMap(oldKeys, oldHead, oldTail);}if (!newKeyToIndexMap.has(oldKeys[oldHead])) {// Old head is no longer in new list; removeremovePart(oldParts[oldHead]!);oldHead++;} else if (!newKeyToIndexMap.has(oldKeys[oldTail])) {// Old tail is no longer in new list; removeremovePart(oldParts[oldTail]!);oldTail--;} else {// Any mismatches at this point are due to additions or moves; see// if we have an old part we can reuse and move into placeconst oldIndex = oldKeyToIndexMap.get(newKeys[newHead]);const oldPart =oldIndex !== undefined ? oldParts[oldIndex] : null;if (oldPart === null) {// No old part for this value; create a new one and insert itconst newPart =createAndInsertPart(containerPart, oldParts[oldHead]!);updatePart(newPart, newValues[newHead]);newParts[newHead] = newPart;} else {// Reuse old partnewParts[newHead] = updatePart(oldPart, newValues[newHead]);insertPartBefore(containerPart, oldPart, oldParts[oldHead]!);// This marks the old part as having been used, so that it will// be skipped in the first two checks aboveoldParts[oldIndex as number] = null;}newHead++;}}}// Add parts for any remaining new valueswhile (newHead <= newTail) {// For all remaining additions, we insert before last new tail,// since old pointers are no longer validconst newPart =createAndInsertPart(containerPart, newParts[newTail + 1]!);updatePart(newPart, newValues[newHead]);newParts[newHead++] = newPart;}// Remove any remaining unused old partswhile (oldHead <= oldTail) {const oldPart = oldParts[oldHead++];if (oldPart !== null) {removePart(oldPart);}}// Save order of new parts for next roundpartListCache.set(containerPart, newParts);keyListCache.set(containerPart, newKeys);};})

A directive that repeats a series of values (usually TemplateResults) generated from an iterable, and updates those items efficiently when the iterable changes based on user-provided keys associated with each item.

Note that if a keyFn is provided, strict key-to-DOM mapping is maintained, meaning previous DOM for a given key is moved into the new position if needed, and DOM will never be reused with values for different keys (new DOM will always be created for new keys). This is generally the most efficient way to use repeat since it performs minimum unnecessary work for insertions amd removals.

IMPORTANT: If providing a keyFn, keys must be unique for all items in a given call to repeat. The behavior when two or more items have the same key is undefined.

If no keyFn is provided, this directive will perform similar to mapping items to values, and DOM will be reused against potentially different items.

Const styleMap

styleMap: (Anonymous function) = directive((styleInfo: StyleInfo) => (part: Part) => {if (!(part instanceof AttributePart) || (part instanceof PropertyPart) ||part.committer.name !== 'style' || part.committer.parts.length > 1) {throw new Error('The `styleMap` directive must be used in the style attribute ' +'and must be the only part in the attribute.');}// Handle static styles the first time we see a Partif (!styleMapStatics.has(part)) {(part.committer.element as HTMLElement).style.cssText =part.committer.strings.join(' ');styleMapStatics.set(part, true);}const style = (part.committer.element as HTMLElement).style;// Remove old properties that no longer exist in styleInfoconst oldInfo = styleMapCache.get(part);for (const name in oldInfo) {if (!(name in styleInfo)) {if (name.indexOf('-') === -1) {(style as any)[name] = null;} else {style.removeProperty(name);}}}// Add or update propertiesfor (const name in styleInfo) {if (name.indexOf('-') === -1) {(style as any)[name] = styleInfo[name];} else {style.setProperty(name, styleInfo[name]);}}styleMapCache.set(part, styleInfo);})

A directive that applies CSS properties to an element.

styleMap can only be used in the style attribute and must be the only expression in the attribute. It takes the property names in the styleInfo object and adds the property values as CSS propertes. Property names with dashes (-) are assumed to be valid CSS property names and set on the element's style object using setProperty(). Names without dashes are assumed to be camelCased JavaScript property names and set on the element's style object using property assignment, allowing the style object to translate JavaScript-style names to CSS property names.

For example styleMap({backgroundColor: 'red', 'border-top': '5px', '--size': '0'}) sets the background-color, border-top and --size properties.

param

Const templateCaches

templateCaches: Map<string, object> = new Map<string, templateCache>()

Const unsafeHTML

unsafeHTML: (Anonymous function) = directive((value: any) => (part: Part): void => {if (!(part instanceof NodePart)) {throw new Error('unsafeHTML can only be used in text bindings');}// Dirty check primitive valuesconst previousValue = previousValues.get(part);if (previousValue === value && isPrimitive(value)) {return;}// Use a <template> to parse HTML into Nodesconst tmp = document.createElement('template');tmp.innerHTML = value;part.setValue(document.importNode(tmp.content, true));previousValues.set(part, value);})

Const until

until: (Anonymous function) = directive((...args: any[]) => (part: Part) => {let state = _state.get(part)!;if (state === undefined) {state = {values: [],};_state.set(part, state);}const previousValues = state.values;let changedSinceLastRender = false;state.values = args;for (let i = 0; i < args.length; i++) {const value = args[i];// If we've seen this value before, we've already handled it.if (value === previousValues[i] && !changedSinceLastRender) {continue;}changedSinceLastRender = true;// Render non-Promise values immediatelyif (isPrimitive(value) || typeof value.then !== 'function') {part.setValue(value);state.lastRenderedIndex = i;// Since a lower-priority value will never overwrite a higher-priority// synchronous value, we can stop processsing now.break;}// We have a Promise that we haven't seen before, so priorities may have// changed. Forget what we rendered before.state.lastRenderedIndex = undefined;Promise.resolve(value).then((resolvedValue: unknown) => {const index = state.values.indexOf(value);// If state.values doesn't contain the value, we've re-rendered without// the value, so don't render it. Then, only render if the value is// higher-priority than what's already been rendered.if (index > -1 &&(state.lastRenderedIndex === undefined ||index < state.lastRenderedIndex)) {state.lastRenderedIndex = index;part.setValue(resolvedValue);part.commit();}});}})

Renders one of a series of values, including Promises, to a Part.

Values are rendered in priority order, with the first argument having the highest priority and the last argument having the lowest priority. If a value is a Promise, low-priority values will be rendered until it resolves.

The priority of values can be used to create placeholder content for async data. For example, a Promise with pending content can be the first, highest-priority, argument, and a non_promise loading indicator template can be used as the second, lower-priority, argument. The loading indicator will render immediately, and the primary content will render when the Promise resolves.

Example:

const content = fetch('./content.txt').then(r => r.text());
html`${until(content, html`<span>Loading...</span>`)}`

Functions

Const createMarker

  • createMarker(): Comment

Const directive

  • directive<F>(f: F): F
  • Brands a function as a directive so that lit-html will call the function during template rendering, rather than passing as a value.

    Type parameters

    • F: Function

    Parameters

    • f: F

      The directive factory function. Must be a function that returns a function of the signature (part: Part) => void. The returned function will be called with the part object

    Returns F

Const html

  • html(strings: TemplateStringsArray, ...values: any[]): TemplateResult
  • Interprets a template literal as an HTML template that can efficiently render to and update a container.

    Parameters

    • strings: TemplateStringsArray
    • Rest ...values: any[]

    Returns TemplateResult

insertNodeIntoTemplate

  • insertNodeIntoTemplate(template: Template, node: Node, refNode?: Node | null): void
  • Inserts the given node into the Template, optionally before the given refNode. In addition to inserting the node into the Template, the Template part indices are updated to match the mutated Template DOM.

    Parameters

    • template: Template
    • node: Node
    • Default value refNode: Node | null = null

    Returns void

Const isDirective

  • isDirective(o: any): boolean

Const isPrimitive

  • isPrimitive(value: any): boolean

Const isTemplatePartActive

Const removeNodes

  • removeNodes(container: Node, startNode: Node | null, endNode?: Node | null): void
  • Removes nodes, starting from startNode (inclusive) to endNode (exclusive), from container.

    Parameters

    • container: Node
    • startNode: Node | null
    • Default value endNode: Node | null = null

    Returns void

removeNodesFromTemplate

  • removeNodesFromTemplate(template: Template, nodesToRemove: Set<Node>): void
  • Removes the list of nodes from a Template safely. In addition to removing nodes from the Template, the Template part indices are updated to match the mutated Template DOM.

    As the template is walked the removal state is tracked and part indices are adjusted as needed.

    div div#1 (remove) <-- start removing (removing node is div#1) div div#2 (remove) <-- continue removing (removing node is still div#1) div div <-- stop removing since previous sibling is the removing node (div#1, removed 4 nodes)

    Parameters

    • template: Template
    • nodesToRemove: Set<Node>

    Returns void

Const render

  • Renders a template to a container. Extension to the standard render method which supports rendering to ShadowRoots when the ShadyDOM (https://github.com/webcomponents/shadydom) and ShadyCSS (https://github.com/webcomponents/shadycss) polyfills are used or when the webcomponentsjs (https://github.com/webcomponents/webcomponentsjs) polyfill is used.

    To update a container with new values, reevaluate the template literal and call render with the new result.

    Adds a scopeName option which is used to scope element DOM and stylesheets when native ShadowDOM is unavailable. The scopeName will be added to the class attribute of all rendered DOM. In addition, any style elements will be automatically re-written with this scopeName selector and moved out of the rendered DOM and into the document .

    It is common to use this render method in conjunction with a custom element which renders a shadowRoot. When this is done, typically the element's localName should be used as the scopeName.

    In addition to DOM scoping, ShadyCSS also supports a basic shim for css custom properties (needed only on older browsers like IE11) and a shim for a deprecated feature called @apply that supports applying a set of css custom properties to a given location.

    Usage considerations:

    • Part values in