Say Goodbye to 80% of Your Frontend

Transform the DOM with a simple state machine modeled in JSON and remove 80% of all the nonsense. No javascript, no libraries, no transpilers, just simple, clean, efficient code.

Download

~1.7KB!

Include It

Add It To Your Html

            <!-- Include the script -->
<script src="/static/scripts/dom-x.js"></script>

<!-- Add the custom element to the body of HTML file -->
<dom-x src="[YOUR-STATE-MACHINE-CONFIG].json"></dom-x>

          

Write Some Markup

Click The Stoplight

              
  <dom-x src="stoplight.json"></dom-x>
  <div id="stoplight">
    <div id="stoplight__red"></div>
    <div id="stoplight__yellow"></div>
    <div id="stoplight__green"></div>
  </div>
  
            

Define A State Machine

stoplight.json

                {
    // the initial state
    "initialState": "red",

    // attach listeners
    "listeners": [["#stoplight", "click", "changeState"]],

    // state definitions
    "states": {
      // the "red" state
      "red": {
        // on entry, run these "transformers"
        "entry": [
          // add the class "active" to the red light
          ["addClass", "#stoplight__red", "active"],
          // remove the class "active" from the green light
          ["removeClass", "#stoplight__green", "active"],
          // dispatch the "changeState" event in 2000ms
          ["dispatch", "changeState", 2000]
        ],
        // when the "changeState" event is dispatched, change the state to "yellow"
        "changeState": [["state", "yellow"]]
      },
      // the "yellow" state
      "yellow": {
        "entry": [
          ["addClass", "#stoplight__yellow", "active"],
          ["removeClass", "#stoplight__red", "active"],
          ["dispatch", "changeState", 2000]
        ],
        "changeState": [["state", "green"]]
      },
      // the "green" state
      "green": {
        "entry": [
          ["addClass", "#stoplight__green", "active"],
          ["removeClass", "#stoplight__yellow", "active"],
          ["dispatch", "changeState", 2000]
        ],
        "changeState": [["state", "red"]]
      }
    }
  }

How Is That Better!?

In Short:

Behavior is local (no more following wires), the state is modeled (the only thing that's actually hard), less client/server coupling (it's all backend) and it's toolable (you can write your own transformers). And in the end, it's just a lot less code and complexity.

Transformers

All The Things You Can Do


// Name: Append
// Desc: Append to element
// Type: [transformer: "append", selector: string, html: string];
["append", "#list", "<li>new item</li>"],

// Name: Attr
// Desc: Set an attribute
// Type: [transformer: "attr", selector: string, attr: string, value: string];
["attr", "#dialog", "open", "true"],

// Name: Add Event Listener
// Desc: Attach an event handler to trigger an action
// Type: [transformer: "addEventListener", selector: string, event: string, stateEvent:string];
["addEventListener", "#btn", "click", "changeState"],

// Name: Dispatch
// Desc: Dispatch an action
// Type: [transformer: "dispatch", action: string, delay?: number];
["dispatch", "reset", 2000],

// Name: Get
// Desc: Trigger a get request and process the result (a transformation object)
// Type: [transformer: "get", url: string, ...args: any[]];
["get", "/some/endpoint"],

// Name: History
// Desc: Update the browser history
// Type: [transformer: "history", method: string, ...args: (string | number)[]];
["history", "pushState", {}, "Record List", "/records/list"],

// Name: Location
// Desc: Update the browser location
// Type: [transformer: "location", url:string];
["location", "/records/list"],

// Name: Post
// Desc: Post selected data to endpoint and process the result (a transformation object)
// Type: [transformer: "post", url: string, ...data: [key: string, selector: string, val: "value" | "dataset" | "formData"][]];
["post", "/some/endpoint", [
  ["form-data", "#todoForm", "formData"]
]],

// Name: Remove attribute
// Desc: Remove an attribute
// Type: [transformer: "removeAttribute", selector: string, attr: string];
["removeAttribute", "#dialog", "open"],

// Name: Remove class
// Desc: Remove a class
// Type: [transformer: "removeClass", selector: string, className: string];
["removeClass", "#dialog", "open"],

// Name: RemoveEventListener
// Desc: Remove an event listener
// Type: [transformer: "removeEventListener", selector: string, event: string, stateEvent: string];
["removeEventListener", "#btn", "click", "changeState"],

// Name: Replace
// Desc: Replace an element
// Type: [transformer: "replace", selector: string, html: string];
["replace", "#list", "<li>updated item</li>"],

// Name: SetAttribute
// Desc: Set an attribute
// Type: [transformer: "setAttribute", selector: string, attr: string, value: string];
["setAttribute", "#dialog", "open", "true"],

// Name: State
// Desc: Change current state
// Type: [transformer: "state", state: string];
["state", "processing"],

// Name: Text content
// Desc: Set the text content of an element
// Type: [transformer: "textContent", selector: string, text: string];
["textContent", "#dialog", "Processing..."],

// Name: Wait
// Desc: Add a wait between transformations
// Type: [transformer: "wait", ms: number];
["wait", 2000],

// Name: Window
// Desc: Call a method on the window object
// Type: [transformer: "win", method: string, ...args: any[]];
["window", "alert", "Hello World!"],

What Else?

Couple domx with my other library, cap ui

cap ui is a set of cut and paste ui components. The state/behavior of the components are expressed via BEM css syntax which makes Domx and cap ui a match made in heaven.

©Copyright 2024 All rights reserved. Made in the USA 🇺🇸 by Kevin Lint as a product of Lint Trap Media.