logical-elements
v0.2.4
Published
A set of tools for creating logical HTML.
Maintainers
Readme
Logical Elements
A set of tools for creating logical HTML.
A Logical Approach
Most JavaScript frameworks are trying to work around HTMLs lack of logic-based functionality, and focus on helping you merge features of JavaScript with HTML. They typically do this by bringing the HTML into the JavaScript domain and controlling the DOM. Sometimes through a virtual DOM and sometimes through a compiler that provides a JS-like experience which handles doing the tedious task of connecting JS and HTML. Each of these tools, React, Vue, Angular, Svelte, etc. requires that the code you write be transformed before a browser can read it.
Logical Elements solves the problem in the other direction. Instead of bring HTML into JavaScript, in brings JavaScript into HTML to provide framework-level reactivity and functionality without a build step. It allows you to write native HTML that can talk directly to nearby custom elements that provide JavaScript logic, reference deeply nested values, update when values change, and iterate over arrays and objects. In short, it provides elements that have logical functionality.
This is achieved through the use of a few combined browser tools such as:
- Custom Elements
- Mutation Observers
- Object Proxies
- Treewalkers
- Custom Events
- And more!
The elements provided serve as both an authoring tool and as useful ways of providing basic logic in your HtML such as if and else statements and each loops.
Core Approach
The primary idea is that HTML elements can have and read from a scoped, local state. Kind of like a React context provder, but without the need for hooks like useContext(). Users can define the state however they need, and return it to the custom HTML element. Then, any ancestor of that element can read the stored values using custom namespaced attributes. Also, since Logical Elements provides authoring tools, that means that users can define their own stateful elements and namespaced attributes.
The HTMLParsedElement Class
The first tool is an enhanced HTMLElement class to use when authoring your own custom elements. It features the following enhancements:
- Parsed Callback - Fires when the element has been fully parsed with all of its children. This solves an issue with the Custom Element
connectedCallbackwhich triggers on the opening tag of an element and doesn't know anything about its children. - Updated Callback - Fires when a parsed, attributeChanged, childrenModified, or providerUpdated callback is also fired. Will batch any of those events together if they occur at the same time.
- Children Modified Callback - Fires when the tree structure of the children changes.
- Provider Updated Callback - Fires when any parent state provider updates their state.
- Provider Subscriptions - Automatically subscribes to parent state providers and makes them available to the element.
- Update Scheduler - Helps batch updates together to minimize the number of times an element runs its update logic
- Each Child Walker - A treewalker that steps through each child of an element and fires a callback function on them.
- Lookup State Values - Lookup values from a parent state provider.
The LogicalElement Class
It extends the LogicalElement class and adds a reactive state to it to become a state provider.
- Reactive Attribute Namespaces - The ability to define reactive attributes and how to handle them. A reactive attribute will have a handler called on it on every update.
- Reactive State - Uses JavaScript object proxies to capture update events and notify subscribers. Will trigger the
stateUpdated,providerUpdated, andupdatedcallbacks. - State Updated Callback - Fires when the state of this ContextElement instance updates.
Reactive Attribute Namespaces
One of the main primary functionalities is the ability to declare attribute namespaces that will be evaluated if there is a provider update anywhere above them. Take for example the attr namespace. You can use this to assign any value to another attribute on the same element like this:
<le-context name="exampleContext">
<script>
function exampleContext (state) {
return {
isDisabled: true,
}
}
</script>
<button attr:disabled="{exampleContext.isDisabled}">This button is disabled</button>
</le-context>The namespace gets broken down in this way:
NAMESPACE:COMMAND="{PROVIDER_NAME.PROPERTY_NAME}"
So, using that information attr namespace will look up the value on the context and assign it to the indicated attribute, in this case the disabled attribute.
There are four provided reactive namespaces that are assigned by default to the ContextElement class:
attr- Assign values to attributes on the same element, will overwrite any current attribute valuescls- Assign class names conditionallyon- Add event listeners to the element for the specified eventset- Assign values to specific properties such astextContent
Any author of a LogicalElement can add their own reactive namespaces to be processed during the updatedCallback lifecycle.
