What about

Web Components
- Templates
- Custom elements
- Shadow DOM
- ❤️ CSS variables
What about
document.body.onload = addElement;
function addElement() {
const newDiv = document.createElement("div");
const newContent = document.createTextNode("Hello Munich");
newDiv.appendChild(newContent);
const currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
}
ancient-code/vanilla-dom-manipulation.js
| $(document).ready(function(){ |
| $.getJSON('https://some-api', function (data) { |
| var items = []; |
| $.each(data, function (key, value) { |
| items.push( "<li id='" + key + "'>" + val + "</li>" ); |
| }); |
| $( "<ul/>", { |
| "class": "my-new-list", |
| html: items.join( "" ) |
| }).appendTo( "body" ); |
| }); |
| }); |
| $(document).ready(function(){ |
| $.getJSON('https://some-api', function (data) { |
| var items = []; |
| $.each(data, function (key, value) { |
| items.push( "<li id='" + key + "'>" + val + "</li>" ); |
| }); |
| $( "<ul/>", { |
| "class": "my-new-list", |
| html: items.join( "" ) |
| }).appendTo( "body" ); |
| }); |
| }); |
old-code/jquery.js
| return ( |
| <div className="App"> |
| <h1>Map do Array</h1> |
| <ul> |
| {(array || sliced ) && array.map((item, id) =>{ |
| return <li key={id}>{item.name} |
| <button onClick={() => handleEdit(item)}>edit</button> |
| </li> |
| })} |
| </ul> |
| |
| <div style={{marginTop: "50px"}}> |
| <input type="text" value={word || ''} onChange={e => setWord(e.target.value)}/> |
| <input type="submit" value="ADD" onClick={() => handleSubmit(word)}/> |
| {editing && <input type="submit" value="submit" onClick={sendEdition} />} |
| </div> |
| </div> |
| ); |
| return ( |
| <div className="App"> |
| <h1>Map do Array</h1> |
| <ul> |
| {(array || sliced ) && array.map((item, id) =>{ |
| return <li key={id}>{item.name} |
| <button onClick={() => handleEdit(item)}>edit</button> |
| </li> |
| })} |
| </ul> |
| |
| <div style={{marginTop: "50px"}}> |
| <input type="text" value={word || ''} onChange={e => setWord(e.target.value)}/> |
| <input type="submit" value="ADD" onClick={() => handleSubmit(word)}/> |
| {editing && <input type="submit" value="submit" onClick={sendEdition} />} |
| </div> |
| </div> |
| ); |
| return ( |
| <div className="App"> |
| <h1>Map do Array</h1> |
| <ul> |
| {(array || sliced ) && array.map((item, id) =>{ |
| return <li key={id}>{item.name} |
| <button onClick={() => handleEdit(item)}>edit</button> |
| </li> |
| })} |
| </ul> |
| |
| <div style={{marginTop: "50px"}}> |
| <input type="text" value={word || ''} onChange={e => setWord(e.target.value)}/> |
| <input type="submit" value="ADD" onClick={() => handleSubmit(word)}/> |
| {editing && <input type="submit" value="submit" onClick={sendEdition} />} |
| </div> |
| </div> |
| ); |
new-code/jsx.js
Literals represent a value in JavaScript.
Fixed (no variable) value that you literally provide in JavaScript.
Boolean literals
true || false
Numeric literals
let number = 1337
let hexadecimalNumber = 0x2FF
let octalNumber = 0o713
let binaryNumber = 0b01011001
Array literals
let list = ['cat', 'dog', 'catdog']
let anotherList = [1, null, { "property": "value" }]
Object literals
const person = {
name: 'Lucien',
surname: 'Immink',
company: 'Team Rockstars IT',
professions: [ 'Principal Consultant', "Google Developer Expert" ],
currentLocation: 'techcamp.hamburg',
}
RegExp literals
let regexp = /ab+c/g
let simpleRegexp = /((([A-Za-z]{3,9}:(?:\/\/)?)(?:[\-;:&=\+\$,\w]+@)?[A-Za-z0-9\.\-]+|(?:www\.|[\-;:&=\+\$,\w]+@)[A-Za-z0-9\.\-]+)((?:\/[\+~%\/\.\w\-_]*)?\??(?:[\-\+=&;%@\.\w_]*)#?(?:[\.\!\/\\\w]*))?)/
String literals
const str = 'Hello';
const multiLineStr = 'Hello\nWorld';
const json = '{"name":"Lucien","surname":"Immink","company":"Team Rockstars IT","professions":["Principal Consultant","Google Developer Expert"], "currentLocation": "techcamp.hamburg"}';
const concat = 'Hello ' + type + ' world';
Template literals
| const str = `Hello`; |
| const multiLineStr = ` |
| Hello |
| World |
| `; |
| const template = `Hello ${type} world`; |
| taggedFunction`Hello ${type} world` |
| const str = `Hello`; |
| const multiLineStr = ` |
| Hello |
| World |
| `; |
| const template = `Hello ${type} world`; |
| taggedFunction`Hello ${type} world` |
const taggedFunction = (template, ...values) => {
}
Take template literals for rendering templates and combine them with web components for lifecycle management, event handling and encapsulation of style and function you get Lit.
| import { html, LitElement } from 'lit'; |
| import { customElement, property } from 'lit/decorators.js'; |
| |
| @customElement('hello-world') |
| export class HelloWorld extends LitElement { |
| |
| @property() |
| type = 'wonderful'; |
| |
| render() { |
| return html`<p>Hello ${this.type} world</p>`; |
| } |
| } |
| import { html, LitElement } from 'lit'; |
| import { customElement, property } from 'lit/decorators.js'; |
| |
| @customElement('hello-world') |
| export class HelloWorld extends LitElement { |
| |
| @property() |
| type = 'wonderful'; |
| |
| render() { |
| return html`<p>Hello ${this.type} world</p>`; |
| } |
| } |
| import { html, LitElement } from 'lit'; |
| import { customElement, property } from 'lit/decorators.js'; |
| |
| @customElement('hello-world') |
| export class HelloWorld extends LitElement { |
| |
| @property() |
| type = 'wonderful'; |
| |
| render() { |
| return html`<p>Hello ${this.type} world</p>`; |
| } |
| } |
| import { html, LitElement } from 'lit'; |
| import { customElement, property } from 'lit/decorators.js'; |
| |
| @customElement('hello-world') |
| export class HelloWorld extends LitElement { |
| |
| @property() |
| type = 'wonderful'; |
| |
| render() { |
| return html`<p>Hello ${this.type} world</p>`; |
| } |
| } |
| import { html, LitElement } from 'lit'; |
| import { customElement, property } from 'lit/decorators.js'; |
| |
| @customElement('hello-world') |
| export class HelloWorld extends LitElement { |
| |
| @property() |
| type = 'wonderful'; |
| |
| render() { |
| return html`<p>Hello ${this.type} world</p>`; |
| } |
| } |
| import { html, LitElement } from 'lit'; |
| import { customElement, property } from 'lit/decorators.js'; |
| |
| @customElement('hello-world') |
| export class HelloWorld extends LitElement { |
| |
| @property() |
| type = 'wonderful'; |
| |
| render() { |
| return html`<p>Hello ${this.type} world</p>`; |
| } |
| } |
hello-world.ts
| <!DOCTYPE html> |
| <html lang="en"> |
| <body> |
| <hello-world type="amazing"></hello-world> |
| <script src="hello-world.js"></script> |
| </body> |
| </html> |
| <!DOCTYPE html> |
| <html lang="en"> |
| <body> |
| <hello-world type="amazing"></hello-world> |
| <script src="hello-world.js"></script> |
| </body> |
| </html> |
index.html

| renderHeader() { |
| return html`<div>Some fancy header</div>` |
| } |
| render() { |
| return html` |
| ${this.renderHeader()} |
| <p>What a nice ${new Date().getHours() < 12 ? html`morning` : html`day`} </p> |
| <div>Some fancy footer</div> |
| ` |
| } |
| renderHeader() { |
| return html`<div>Some fancy header</div>` |
| } |
| render() { |
| return html` |
| ${this.renderHeader()} |
| <p>What a nice ${new Date().getHours() < 12 ? html`morning` : html`day`} </p> |
| <div>Some fancy footer</div> |
| ` |
| } |
| renderHeader() { |
| return html`<div>Some fancy header</div>` |
| } |
| render() { |
| return html` |
| ${this.renderHeader()} |
| <p>What a nice ${new Date().getHours() < 12 ? html`morning` : html`day`} </p> |
| <div>Some fancy footer</div> |
| ` |
| } |
| renderHeader() { |
| return html`<div>Some fancy header</div>` |
| } |
| render() { |
| return html` |
| ${this.renderHeader()} |
| <p>What a nice ${new Date().getHours() < 12 ? html`morning` : html`day`} </p> |
| <div>Some fancy footer</div> |
| ` |
| } |
components/my-element.ts
| import { html, LitElement } from 'lit' |
| import { customElement, property } from 'lit/decorators.js' |
| |
| @customElement('my-footer') |
| export class MyFooter extends LitElement { |
| |
| @property({ attribute: 'brand-name'}) |
| brandName: string; |
| year: number; |
| |
| constructor() { |
| super(); |
| this.year = new Date().getFullYear(); |
| } |
| |
| render() { |
| return html`<div> |
| © ${this.year} ${this.brandName} |
| <slot></slot> |
| </div>` |
| } |
| } |
| import { html, LitElement } from 'lit' |
| import { customElement, property } from 'lit/decorators.js' |
| |
| @customElement('my-footer') |
| export class MyFooter extends LitElement { |
| |
| @property({ attribute: 'brand-name'}) |
| brandName: string; |
| year: number; |
| |
| constructor() { |
| super(); |
| this.year = new Date().getFullYear(); |
| } |
| |
| render() { |
| return html`<div> |
| © ${this.year} ${this.brandName} |
| <slot></slot> |
| </div>` |
| } |
| } |
| import { html, LitElement } from 'lit' |
| import { customElement, property } from 'lit/decorators.js' |
| |
| @customElement('my-footer') |
| export class MyFooter extends LitElement { |
| |
| @property({ attribute: 'brand-name'}) |
| brandName: string; |
| year: number; |
| |
| constructor() { |
| super(); |
| this.year = new Date().getFullYear(); |
| } |
| |
| render() { |
| return html`<div> |
| © ${this.year} ${this.brandName} |
| <slot></slot> |
| </div>` |
| } |
| } |
components/my-footer.ts
| import { LitElement, html } from 'lit' |
| import { customElement } from 'lit/decorators.js' |
| |
| import './my-footer' |
| |
| @customElement('my-page') |
| class MyPage extends LitElement { |
| renderHeader() { |
| return html`<div>Some fancy header</div>` |
| } |
| render() { |
| return html` |
| ${this.renderHeader()} |
| <p>What a nice ${this.greeting()}</p> |
| <my-footer brand-name="Team Rockstars IT"> |
| <ul> |
| <li><a href="/privacy-policy">Privacy policy</a></li> |
| <li><a href="/faq">FAQ</a></li> |
| </ul> |
| </my-footer> |
| ` |
| } |
| } |
| import { LitElement, html } from 'lit' |
| import { customElement } from 'lit/decorators.js' |
| |
| import './my-footer' |
| |
| @customElement('my-page') |
| class MyPage extends LitElement { |
| renderHeader() { |
| return html`<div>Some fancy header</div>` |
| } |
| render() { |
| return html` |
| ${this.renderHeader()} |
| <p>What a nice ${this.greeting()}</p> |
| <my-footer brand-name="Team Rockstars IT"> |
| <ul> |
| <li><a href="/privacy-policy">Privacy policy</a></li> |
| <li><a href="/faq">FAQ</a></li> |
| </ul> |
| </my-footer> |
| ` |
| } |
| } |
| import { LitElement, html } from 'lit' |
| import { customElement } from 'lit/decorators.js' |
| |
| import './my-footer' |
| |
| @customElement('my-page') |
| class MyPage extends LitElement { |
| renderHeader() { |
| return html`<div>Some fancy header</div>` |
| } |
| render() { |
| return html` |
| ${this.renderHeader()} |
| <p>What a nice ${this.greeting()}</p> |
| <my-footer brand-name="Team Rockstars IT"> |
| <ul> |
| <li><a href="/privacy-policy">Privacy policy</a></li> |
| <li><a href="/faq">FAQ</a></li> |
| </ul> |
| </my-footer> |
| ` |
| } |
| } |
components/my-page.ts
Event handling
| import { html, LitElement } from 'lit'; |
| import { customElement, state, eventOptions } from 'lit/decorators.js'; |
| |
| @customElement('my-counter') |
| export class HelloWorld extends LitElement { |
| @state() |
| counter = 0; |
| |
| @eventOptions({ passive: true }) |
| addCount() { |
| this.counter += 1; |
| } |
| |
| render() { |
| return html` |
| <button @click="${this.addCount}">Add more</button> |
| <p>counter is now at: ${this.counter}</p> |
| `; |
| } |
| } |
| import { html, LitElement } from 'lit'; |
| import { customElement, state, eventOptions } from 'lit/decorators.js'; |
| |
| @customElement('my-counter') |
| export class HelloWorld extends LitElement { |
| @state() |
| counter = 0; |
| |
| @eventOptions({ passive: true }) |
| addCount() { |
| this.counter += 1; |
| } |
| |
| render() { |
| return html` |
| <button @click="${this.addCount}">Add more</button> |
| <p>counter is now at: ${this.counter}</p> |
| `; |
| } |
| } |
| import { html, LitElement } from 'lit'; |
| import { customElement, state, eventOptions } from 'lit/decorators.js'; |
| |
| @customElement('my-counter') |
| export class HelloWorld extends LitElement { |
| @state() |
| counter = 0; |
| |
| @eventOptions({ passive: true }) |
| addCount() { |
| this.counter += 1; |
| } |
| |
| render() { |
| return html` |
| <button @click="${this.addCount}">Add more</button> |
| <p>counter is now at: ${this.counter}</p> |
| `; |
| } |
| } |
| import { html, LitElement } from 'lit'; |
| import { customElement, state, eventOptions } from 'lit/decorators.js'; |
| |
| @customElement('my-counter') |
| export class HelloWorld extends LitElement { |
| @state() |
| counter = 0; |
| |
| @eventOptions({ passive: true }) |
| addCount() { |
| this.counter += 1; |
| } |
| |
| render() { |
| return html` |
| <button @click="${this.addCount}">Add more</button> |
| <p>counter is now at: ${this.counter}</p> |
| `; |
| } |
| } |
| import { html, LitElement } from 'lit'; |
| import { customElement, state, eventOptions } from 'lit/decorators.js'; |
| |
| @customElement('my-counter') |
| export class HelloWorld extends LitElement { |
| @state() |
| counter = 0; |
| |
| @eventOptions({ passive: true }) |
| addCount() { |
| this.counter += 1; |
| } |
| |
| render() { |
| return html` |
| <button @click="${this.addCount}">Add more</button> |
| <p>counter is now at: ${this.counter}</p> |
| `; |
| } |
| } |
components/my-counter.ts
Styling
| import { LitElement, html, css } from 'lit' |
| import { customElement } from 'lit/decorators.js' |
| |
| @customElement('my-element') |
| export class MyElement extends LitElement { |
| static styles = css` |
| :host { |
| font-size: 2em; |
| padding: 1em; |
| border: 0.25em solid var(--blue, blue); |
| } |
| p { |
| color: var(--blue, blue); |
| } |
| ` |
| render() { |
| return html`<p>I am blue da ba dee! ®eiffel 65</p>` |
| } |
| } |
| import { LitElement, html, css } from 'lit' |
| import { customElement } from 'lit/decorators.js' |
| |
| @customElement('my-element') |
| export class MyElement extends LitElement { |
| static styles = css` |
| :host { |
| font-size: 2em; |
| padding: 1em; |
| border: 0.25em solid var(--blue, blue); |
| } |
| p { |
| color: var(--blue, blue); |
| } |
| ` |
| render() { |
| return html`<p>I am blue da ba dee! ®eiffel 65</p>` |
| } |
| } |
| import { LitElement, html, css } from 'lit' |
| import { customElement } from 'lit/decorators.js' |
| |
| @customElement('my-element') |
| export class MyElement extends LitElement { |
| static styles = css` |
| :host { |
| font-size: 2em; |
| padding: 1em; |
| border: 0.25em solid var(--blue, blue); |
| } |
| p { |
| color: var(--blue, blue); |
| } |
| ` |
| render() { |
| return html`<p>I am blue da ba dee! ®eiffel 65</p>` |
| } |
| } |
components/my-element.ts
| import { css } from 'lit' |
| |
| export const buttonStyles = css` |
| .primary-button { |
| color: var(--text-colour); |
| color: var(--primary-colour); |
| } |
| .primary-button:disabled { |
| opacity: 0.6; |
| pointer-events: none; |
| } |
| ` |
styles/index.ts
| import { css, LitElement } from 'lit' |
| import { customElement } from 'lit/decorators.js' |
| import { buttonStyles } from './styles' |
| |
| @customElement('my-element') |
| export class MyElement extends LitElement { |
| static styles = [ |
| buttonStyles, |
| css` |
| :host { |
| display: block; |
| border: 1px solid black; |
| } |
| `, |
| ] |
| } |
| import { css, LitElement } from 'lit' |
| import { customElement } from 'lit/decorators.js' |
| import { buttonStyles } from './styles' |
| |
| @customElement('my-element') |
| export class MyElement extends LitElement { |
| static styles = [ |
| buttonStyles, |
| css` |
| :host { |
| display: block; |
| border: 1px solid black; |
| } |
| `, |
| ] |
| } |
my-element.ts
Scaffold project
Lit ❤️ Vite
npm create vite@latest my-lit-app -- --template lit-ts
or
yarn create vite@latest my-lit-app --template lit-ts