OOP in the browser!
<!doctype html>
<html lang="en">
<head>(...)</head>
<body>
<div id="root">
<app-component>
<slot>"Your App Goes Here!"</slot>
</app-component>
</div>
<script type="module"></script>
</body>
</html>
- No config
- No framework
- No abstractions
Just raw Web API.
Powered by TailwindCSS, ESBuild, and fast-refreshing development server!
Step by step!
start
class AppComponent {}
AppComponent.constructor()
AppComponent extends HTMLElement
HTMLElement.super()
this
AppComponent.innerHtml
CustomElementRegistry
'app-component': AppComponent
AppComponent.render()
AppComponent.render(innerHTML)
AppComponent.setup()
createElement('app-component')
App()
render(App)
<app-component>
- Tips
- Further Reading
$ git clone git@github.com:nathanjhood/web-components.git
$ cd web-components
$ npm install
# For Windows...
$env:NODE_ENV="development"
# For Linux/Mac...
export NODE_ENV="development"
$ npm run start
# ...
Rebuilding...
Done in 1623ms.
Server running at http://127.0.0.1:3000/
To exit: Ctrl + c
Open in your browser and edit src/App.js
- the page will automatically refresh itself after every save.
// src/App.js
class AppComponent {}
class AppComponent {
constructor() {/** setup goes here... */}
}
// "I am a HTMLElement"
class AppComponent extends HTMLElement {
constructor() {}
}
// "...plus more ;) "
class AppComponent extends HTMLElement {
constructor() {
super(); // MUST do this first...
}
}
class AppComponent extends HTMLElement {
constructor() {
// inside here, "this" means "this 'AppComponent'"...
super();
this. // <-- '.' should produce a long list of props and methods...
}
}
// 'innerHTML' === <app-component>innerHTML</app-component>
class AppComponent extends HTMLElement {
constructor() {
super();
this.innerHTML = `<slot>Your app goes here</slot>`;
}
}
// IMPORTANT
window.customElements.define('app-component', AppComponent);
window.customElements.define('app-component', // <-- wrap the class!
class AppComponent extends HTMLElement {
constructor() {
super();
this.innerHTML = `<slot>Your app goes here</slot>`;
}
}
); // <-- '.define()' ends here!
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.innerHTML = this.render();
}
render() {
return `<slot>Your app goes here</slot>`;
}
}
);
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.innerHTML = this.render('Your app goes here');
}
render(innerHTML) {
return `<slot>${innerHTML}</slot>`;
}
}
);
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.setup();
}
setup() {
this.innerHTML = this.render('Your app goes here');
}
render(innerHTML) {
return `<slot>${innerHTML}</slot>`;
}
}
);
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.setup();
}
setup() {
this.innerHTML = this.render('Your app goes here');
}
render(innerHTML) {
return `<slot>${innerHTML}</slot>`;
}
}
);
const app = document.createElement('app-component');
const App = () => {
// define the component
window.customElements.define('app-component',
class AppComponent extends HTMLElement {
constructor() {
super();
this.setup();
}
setup(): void {
this.innerHTML = this.render('Your app goes here');
}
render(innerHTML) {
return `<slot>${innerHTML}</slot>`;
}
}
);
// then return it
return document.createElement('app-component');
}
// Now we can assign it :)
const app = App();
// src/index.js
import App = require('./App');
const render = (element) = {
// ...attaches passed-in element to document
}
// so, pass it our App :)
render(App)
<!-- This is what you see in your IDE... -->
<!doctype html>
<html lang="en">
<head></head>
<body>
<div id="root"></div>
<script type="module" src="./static/js/index.js"></script>
</body>
</html>
<!-- ...this is what you see in your web browser! -->
<!doctype html>
<html lang="en">
<head></head>
<body>
<div id="root">
<app-component>
#shadowRoot (open)
<slot>"Your App Goes Here!"</slot>
</app-component>
</div>
<script type="module"></script>
</body>
</html>
// example:
const Button = () => {
return document.createElement('button')
}
// HTMLButtonElement
const button = Button();
// example
const CustomButton = () => {
class CustomButtonElement extends HTMLButtonElement {
constructor() {
super();
}
}
customElements.define('custom-button', CustomButtonElement)
return document.createElement('custom-button');
};
// CustomButtom
const customButton = CustomButton();
/**
* @typedef CustomButtonProps
* @property {'submit' | 'reset' | 'button'} type
*/
/**
* @param {CustomButtonProps} props
* @returns {HTMLButtonElement}
*/
const CustomButton = (props) => {
class CustomButtonElement extends HTMLButtonElement {
constructor() {
super();
this.type = props.type;
}
}
customElements.define('custom-button', CustomButtonElement);
return document.createElement('custom-button');
};
const customButton = CustomButton({ type: 'submit' });
/**
* @typedef CustomButtonProps
* @property {'submit' | 'reset' | 'button'} type
* @property {Node | undefined} children
*/
/**
* @param {CustomButtonProps} props
* @returns {HTMLButtonElement}
*/
const CustomButton = (props) => {
class CustomButtonElement extends HTMLButtonElement {
constructor() {
super();
this.type = props.type;
if (props.children) this.appendChild(props.children);
}
}
customElements.define('custom-button', CustomButtonElement);
return document.createElement('custom-button');
};
const customButtonA = CustomButton({ type: 'submit' });
const customButtonB = CustomButton({ type: 'submit', children: customButtonA });
/**
* @typedef CustomButtonProps
* @property {'submit' | 'reset' | 'button'} type
* @property {Node | undefined} children
* @property {string | undefined} className
*/
/**
* @param {CustomButtonProps} props
* @returns {HTMLButtonElement}
*/
const CustomButton = (props) => {
class CustomButtonElement extends HTMLButtonElement {
constructor() {
super();
this.type = props.type;
if (props.children) this.appendChild(props.children);
if (props.className) this.className = props.className;
}
}
customElements.define('custom-button', CustomButtonElement);
return document.createElement('custom-button');
};
const tailwindButton = CustomButton({
type: 'submit',
className: 'flex align-left text-white bg-red-500',
});