Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings
This repository was archived by the owner on Jan 9, 2025. It is now read-only.
/jtmlPublic archive

Write HTML in JavaScript, using template-tags.

License

NotificationsYou must be signed in to change notification settings

github/jtml

Repository files navigation

This library is designed as a layer on top of@github/template-parts to provide declarative, JavaScript based HTML template tags.

This library is heavily inspired bylit-html, which GitHub has used in production for a while. This was created independently fromlit-html for the following reasons:

Basic Usage

This library comes with a set of exports, the main two beinghtml andrender.

html is a"tagged template" function. Rather than calling it, you "tag" a template string withhtml and it will return aTemplateResult which can be used to render HTML safely, on the client side.

import{html,render}from'@github/jtml'constgreeting='Hello'render(html`<h1>${greeting} World</h1>`,document.body)

The benefit of this over, say, settinginnerHTML is that the tagged template can be re-used efficiently, causing less mutations in the DOM:

import{html,render}from'@github/jtml'consttheTime=date=>html`<p>The time is${date.toString()}</p>`setInterval(()=>render(theTime(newDate()),document.body),1000)

Expressions

jtml interpolates placeholder expressions in special ways across the template. Depending on where you put a placeholder expression (the${} syntax is a placeholder expression) depends on what it does.Importantly "Attributes" behave differently to "Nodes". Here is a comprehensive list:

Attributes

HTML Attributes can contain placeholder expressions, but thesemust be inside the quoted part of the attribute. The name of an Attribute cannot use placeholder expressions, only the value.

import{html,render}from'@github/jtml'constclassName=`red-box`html`<pclass="${className}"></p>`// This is validhtml`<pclass=${className}></p>`// !! This is INVALID!html`<p${attr}="test"></p>`// !! This is INVALID!
Boolean Values

If an attribute maps to a"boolean attribute", and the attribute value consistssolely of a placeholder expression which evaluates to a boolean, then this can be used to toggle the attribute on or off. For example:

import{html,render}from'@github/jtml'constinput=(required=false)=>html`<inputrequired="${required}"/>`constdiv=(hidden=false)=>html`<divhidden="${hidden}"></div>`render(input(false),document.body)// Will render `<input />`render(input(true),document.body)// Will render `<input required />`render(div(true),document.body)// Will render `<div></div>`render(div(false),document.body)// Will render `<div></div>`
Multiple values, whitespace

If an attribute consists of multiple placeholder expressions, these will all be mapped to strings. Any included whitespace is also rendered as you might expect. Here's an example:

import{html,render}from'@github/jtml'constp=({classOne, classTwo, classThree})=>html`<pclass="${classOne}${classTwo}${classThree}"></p>`render(p({classOne:'red',classTwo:'box',classThree:''}),document.body)// ^ Renders `<p></p>`consti=({classOne, classTwo})=>html`<iclass="${classOne}-${classTwo}"></i>`render(i({classOne:'red',classTwo:'box'}),document.body)// ^ Renders `<i></i>`
Iterables (like Arrays)

Any placeholder expression which evaluates to an Array/Iterable is joined with spaces (Array.from(value).join(' ')). This means you can pass in an Array of strings and it'll be rendered as a space separated list. These can still be mixed with other placeholder expressions or static values. An example:

import{html,render}from'@github/jtml'constp=({classes, hidden=false})=>html`<pclass="bold${classes}${hidden ?'d-none' :''}"></p>render(p({classes: ['red', 'box'], hidden: true}), document.body)// ^ Renders `<pclass="bold red box d-none"></p>`render(p({classes:['red','box'],hidden:false}), document.body)// ^ Renders `<pclass="bold red box "></p>`
Events

If an attributes name begins withon, and the value consists of a single placeholder expression that evaluates to a function, then this will become an Event Listener, where the event name is the attribute name without theon, so for example:

import{html,render}from'@github/jtml'consthandleClick=e=>console.log('User clicked!')render(html`<buttononclick="${handleClick}"></button>`,document.body)// ^ Renders `<button></button>`// Effectively calls `button.addEventListener('click', handleClick)`

The event name can be any event name that is also possible as an attribute, for exampleonloaded will listen for theloaded event,onwill-load will bind to thewill-load event. Special characters such as:s are not allowed as attribute names, and as such you cannot bind to an event name with these special characters using this pattern.

Nodes

Placeholder expressions can also be put where an HTML node might be - in other words inside a tag, rather than inside an attribute. These behave differently to placeholder expressions inside attribute values:

HTML Escaping

Any HTML inside a string is automatically escaped. Values get added asText nodes, meaning it is impossible to inject HTML unless you explicitly want to, making them safe for XSS. This is not manually handled by the library, but is core to the design - meaning the browser handles this escaping! An example:

import{html,render}from'@github/jtml'constunsafe='<script>alert(1)</script>'render(html`<div>${unsafe}</div>`,document.body)// ^ Renders `<div>&lt;script&gt;alert(1)&lt;/script&gt;</div>`
Sub Templates

If a placeholder expression evaluates to a sub template, then that sub template will be rendered and added to as a child to the node, in the position you'd expect:

import{html,render}from'@github/jtml'constembolden=word=>html`<strong>${word}</strong>`render(html`<div>Hello${embolden('world')}!</div>`,document.body)// ^ Renders `<div>Hello <strong>world</strong>!</div>`

Document Fragments

You can also pass document fragments in, and they will be rendered as you might expect. This is useful for mixing-and-matching template libraries:

import{html,render}from'@github/jtml'constvanillaEmbolden=word=>{constfrag=document.createDocumentFragment()conststrong=document.createElement('strong')strong.append(String(word))frag.append(strong)returnfrag}render(html`<div>Hello${vanillaEmbolden('world')}!</div>`,document.body)// ^ Renders `<div>Hello <strong>world</strong>!</div>`
Iterables (like Arrays)

Any placeholder expression which evaluates to an Array/Iterable is evaluated per-item. If a single item is a Document Fragment or Sub Template then it will be rendered as you might expect, otherwise it is treated as a String and gets added as aText node. All of the contents of the Array will be rendered as one. Some examples:

import{html,render}from'@github/jtml'constdata=[{name:'Spanner',value:5},{name:'Wrench',value:5}]constrow=({name, value})=>html`<tr><td>${name}</td><td>${value}</td></td>`consttable=rows=>html`<table>${rows.map(row)}</table>`render(table(data),document.body)// ^ Renders// <table>//   <tr><td>Spanner</td><td>5</td></tr>//   <tr><td>Wrench</td><td>5</td></tr>// </table>

Directives

For more advanced behaviours, a function can be wrapped with thedirective function to create aDirective which gets to customize the rendering flow. jtml also includes some built in directives (see below).

A directive must follow the following signature. It can take any number of arguments (which are ignored) and must return a function which receives the TemplatePart:

typeDirective=(...values:unknown[])=>(part:TemplatePart)=>void

Here's an example of how a directive might work:

import{html,render,directive}from'@github/jtml'// A directive can take any number of arguments, and must return a function that takes a `TemplatePart`.constrenderLater=directive((text,ms)=>part=>{// A parts value can be set using `.value`part.value='Loading...'setTimeout(()=>part.value=text,ms)})render(html`<div>${renderLater('Hello world',1000)}`,document.body)// ^ Renders <div>Loading...</div>// After 1000ms, changes to `<div>Hello world</div>`

Built in Directives

until

jtml ships with a built-in directive for handling Promise values, calleduntil.until takes any number of Promises, and will render them, right to left, as they resolve. This is useful for passing in asynchronous values as the first arguments, timeout messages as the middle value, and synchronous values for the placeholder values, like so:

import{html,render,until}from'@github/jtml'constdelay=(ms,value)=>newPromise(resolve=>setTimeout(resolve,ms,value))constrequest=delay(1000,'Hello World')constloading='Loading...'consttimeout=delay(2000,'Failed to load')render(html`<div>${until(request,timeout,loading)}</div>`)// ^ renders <div>Loading...</div>// After 1000ms will render <div>Hello World</div>
import{html,render,until}from'@github/jtml'constdelay=(ms,value)=>newPromise(resolve=>setTimeout(resolve,ms,value))constrequest=delay(3000,'Hello World')// Request takes longer than the timeoutconstloading='Loading...'consttimeout=delay(2000,'Failed to load')render(html`<div>${until(request,timeout,loading)}</div>`)// ^ renders <div>Loading...</div>// After 2000ms will render <div>Failed to load</div>

CSP Trusted Types

You can callTemplateResult.setCSPTrustedTypesPolicy(policy: TrustedTypePolicy | Promise<TrustedTypePolicy> | null) from JavaScript to set aCSP trusted types policy, which can perform (synchronous) filtering or rejection of the rendered template:

import{TemplateResult}from"@github/jtml";importDOMPurifyfrom"dompurify";// Using https://github.com/cure53/DOMPurify// This policy removes all HTML markup except links.constpolicy=trustedTypes.createPolicy("links-only",{createHTML:(htmlText:string)=>{returnDOMPurify.sanitize(htmlText,{ALLOWED_TAGS:["a"],ALLOWED_ATTR:["href"],RETURN_TRUSTED_TYPE:true,});},});TemplateResult.setCSPTrustedTypesPolicy(policy);

Note that:

  • Only a single policy can be set, shared by allrender andunsafeHTML calls.
  • You should callTemplateResult.setCSPTrustedTypesPolicy() ahead of any other call of@github/jtml in your code.
  • Not all browserssupport the trusted types API in JavaScript. You may want to use therecommended tinyfill to construct a policy without causing issues in other browsers.

About

Write HTML in JavaScript, using template-tags.

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors12


[8]ページ先頭

©2009-2025 Movatter.jp