thunderdom
v1.0.0
Published
ThunderDOM/e is a super lightweight frontend library with no build step. So simple you can learn in 5 minutes!
Downloads
13
Maintainers
Readme
ThunderDOM/e
Welcome to the ThunderDOM/e!
Now with added SSR Elements. Mmm.. delicious!
ThunderDOM/e is a super lightweight frontend library (Just 4kb un-minified) with no build step! ThunderDOM/e makes it easier to create web components without using huge bloated frameworks like Vue, Svelt, React or Angular or even libs like Solid or jQuery.
ThunderDOM/e components are based off of native web component architecture and use the shadow DOM which has been supported by all browsers since 2020. This simplifies the process of component creation and aligns the code with other languages using properties, getters, setters and events, reducing the cognitive load of the developer and the learning curve of the library.
It's so simple, a developer who knows javascript at the base level can learn in 5 minutes.
ThunderDOM/e should play nicely with HTMX hx- attributes but at this point that is untested so if you try it out, let me know!
ThunderDOM/e is comprised of just two classes and a few functions (as a lot of the functionality is currently built into all browsers), ThunderDOM/e just provides a clear and concise interface with which to interact with the web component system.
Client Rendered Components
This is an example of a client side ThunderDOMElement:
import * as TD from "../js/thunderDOM/thunderDOM.js";
class MyButton extends TD.ThunderDOMElement{
//Variable declarations, getters, setters
_clickCount = 0;
get clickCount() {
return this._clickCount;
}
set clickCount(value) {
this._clickCount = value;
const el = this.getEl("#foo");
el.textContent = `Click Me (${value} clicks)`;
}
//DOM Element HTML, listener bindings and functions
constructor() {
super();
this.component(()=>{
return `<button id="foo">Click Me</button>`
});
//this.iCss(`#foo{background-color: green;}`); <-- You can also write inline CSS!
this.css('/css/my-button.css');
this.bind("#foo","click",(e)=>{this.clickCount++});
}
}
customElements.define("my-button", MyButton);
Components may include as many HTML elements as you need but for this simple example we are just making a button.
This allows you to add a button to any page for client side js rendering:
<my-button></my-button>
This means you can use any backend or static html doc to output your components easily, here is an example in ASP .NET Core!
@for (var i = 0; i < 10; i++)
{
<my-button id="button-@i"></my-button>
}
This will output a button with an id of "button-0" to "button-9". You can even use these id's for CSS selection of inner shadow dom elements allowing you to style your ThunderDOM components on a per element basis.
/*my-button.css*/
button {
background-color: yellowgreen;
}
/*If the shadow dom root (root component / parent) has id "button-1"*/
:host-context(#button-1) button {
background-color: indigo;
}
This CSS will style all 10 of the button's background color to "yellowgreen" via the component CSS and style just "button-1"'s background colour to "indigo".
You can also write inline CSS with iCss("button{ background-color: yellowgreen; }")
or use a lib like Tailwind.
this.shadowRoot
returns the root element of the component we created when calling component()
But of course you might want to render your HTML on the server for SEO!
Server Rendered Components (SSR)
The latest update of ThunderDOM/e now supports binding events to server rendered components! There are a few differences from client side components to bare in mind when creating SSR components.
- No need to include the HTML markup in the javascript class
- SSR components cannot have their own local css like client side components
Component CSS support will improve in the future once more browsers adopt support for Declarative Shadow DOM
This is an example of a ThunderDOM SSR Element:
class MySsrButton extends TD.ThunderDOMElementSSR{
//Variable declarations, getters, setters
_clickCount = 0;
get clickCount() {
return this._clickCount;
}
set clickCount(value) {
this._clickCount = value;
//const el = this.getEl("#inner-foo"); <--you can still select children
this.root.textContent = `Click Me (${value} clicks)`; //<--this.root to select root element
}
//listener bindings and functions
setBindings(){
this.bind(this.root,"click",(e)=>{this.clickCount++});
}
}
Its actually far simpler than the client side example. You can see that everything is the same minus a few lines of code and we are now extending ThunderDOMElementSSR
.
You can declare your component underneath the class object just like with client rendered components but we use a different function this time.
TD.DefineComponent("my-ssr-button", MySsrButton);
This will make ThunderDOM/e aware of your button so that it can carry out automatic binding on DOM load.
If you want to take care of binding manually or you want to have all of your components in a manifest style structure, INSTEAD OF declaring the component, you can simply add your classes along side their element name into the initiation map manually like so:
document.addEventListener("DOMContentLoaded", () => {
TD.BindThunderDOM({
"my-ssr-button": MySsrButton,
"my-other-component" : MyOtherComponent,
//add as many components you want to bind!
});
});
Your sever side HTML markup should include an attribute of "data-(your element name)"
Dotnet Core Example:
@for (var i = 0; i < 10; i++)
{
<button id="foo" data-my-ssr-button>Click Me</button>
}
As for binding functionality dynamically post DOM update. You can use:
TD.BindThunderDOMElement(element,"my-ssr-button", MySsrButton);
To bind a component individually at any time.
If you are using HTMX to load dynamic components this would ideally be done in the "hx-on::after-request" where you can retrieve the target element.
And that's it. Interactivity is now bound to your server rendered components on the frontend! I don't know why React makes "hydration" look so difficult. :D
Script Reference:
class ThunderDOMElement{}
The base class of all ThunderDOM elements / components. Extend this class to create components.
class ThunderDOMElementSSR{}
The base class of all Server Rendered ThunderDOM elements / components. Extend this class to create SSR components.
this.component(componentHTML)
Creates the client rendered ThunderDOM component from the input html string returned by the delegate function.
this.css(path)
Attaches a CSS file to this client rendered component for styling.
this.iCss(cssString)
Allows for inline CSS styling via an input string for client rendered components.
this.bind(id, eventName, callback)
Allows for binding events easily to your component's elements.
this.getElById(id);
Returns an element: Param = A query (#id / .classname) for the root component's inner element you want to select.
this.getEl(queryString);
Shorthand for this.root.querySelector(query);
, selects a shadow DOM element via query string within the root component.
this.setBindings();
The function that sets the event bindings for your SSR components.
TD.DefineComponent("my-ssr-button", MySsrButton);
Define a component so it can be bound to the ThunderDOMe (will be done automatically on dom load)
TD.BindThunderDOMElement(element,"my-ssr-button", MySsrButton);
Bind ssr component to dom element individually
TD.BindThunderDOM(classMap);
Bind all elements (run on document ready for undefined components)
A note from Ipinzi (Arrogant Pixel)
I hope you enjoy using ThunderDOM/e, I made it because I dislike working with all the frontend JS frameworks (except Svelt) and libs. I needed something much lighter, much simpler and something that was easier to understand how to work seamlessly with any backend.
All feedback and criticisms are welcome. Open an issue or email me directly.