https://github.com/bwhmather/bdc-js Skip to content Sign up * Product + Features + Mobile + Actions + Codespaces + Copilot + Packages + Security + Code review + Issues + Discussions + Integrations + GitHub Sponsors + Customer stories * Team * Enterprise * Explore + Explore GitHub + Learn and contribute + Topics + Collections + Trending + Skills + GitHub Sponsors + Open source guides + Connect with others + The ReadME Project + Events + Community forum + GitHub Education + GitHub Stars program * Marketplace * Pricing + Plans + Compare plans + Contact Sales + Education [ ] * # In this repository All GitHub | Jump to | * No suggested jump to results * # In this repository All GitHub | Jump to | * # In this user All GitHub | Jump to | * # In this repository All GitHub | Jump to | Sign in Sign up {{ message }} bwhmather / bdc-js Public * Notifications * Fork 2 * Star 17 Ben's DOM Clobberer License MIT license 17 stars 2 forks Star Notifications * Code * Issues 2 * Pull requests 2 * Actions * Security * Insights More * Code * Issues * Pull requests * Actions * Security * Insights bwhmather/bdc-js This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository. master Switch branches/tags [ ] Branches Tags Could not load branches Nothing to show {{ refName }} default View all branches Could not load tags Nothing to show {{ refName }} default View all tags 19 branches 15 tags Code Latest commit @bwhmather bwhmather Documentation improvements ... f103f81 Sep 28, 2021 Documentation improvements f103f81 Git stats * 199 commits Files Permalink Failed to load latest commit information. Type Name Latest commit message Commit time .github/workflows examples src tests .gitignore .travis.yml LICENSE README.md package-lock.json package.json rollup.config.js tsconfig.json tslint.json View code [ ] Ben's DOM Clobberer Installation Usage Preserving input state Event Handlers Keyed updates Autofocus CSS Web Components Contributing License README.md Ben's DOM Clobberer Build Status Ben's DOM Clobberer (BDC) is a tiny javascript library for updating the html DOM to match a javascript description. It weighs less that 1KiB when gzipped, and presents a simple API that makes it easy to describe HTML without needing to resort to JSX. Unlike libraries such as react, BDC does not have a component system. Instead it follows the Elm model and requires a complete description of the target state to be provided as a static data structure. BDC can be used with WebComponents if stateful sub-trees are required. BDC does not construct a virtual DOM. Instead it diffs directly against the real thing. This is what enables BDC to be so small, but comes at the cost of making updates relatively slow. The BDC submission to the JS Framework Benchmark competition does extremely well in the startup tests, but is one of the slowest for repeated updates. BDC is a good choice for small projects that are too dynamic to complete easily using vanilla javascript, but where a complex build setup and tens of megabytes of compiled code would be overkill. It is a bad choice for pages that must repeatedly re-render a large amount of dynamic data, and for more complicated applications where you may benefit from the larger ecosystem around other libraries. BDC is now largely finished. The API is frozen and all bugs that I am aware of have been fixed. It has seen enough real world use that I am now confident to recommend it in the niche it aims to fill. Expect a steady trickle of documentation improvements and occasional bug fixes but no breaking changes. Installation Releases of BDC are published to npm. They can be installed by running: npm install bdc The npm package contains Common-JS, ES6 and IIFE bundles of the library, as well as a type script definition file. Alternatively, prebuilt source files are pushed to github releases and can be saved and included directly in your project. Usage BDC exposes two functions, h and clobber. h is used to build up a javascript representation of the target state of a subtree of the DOM. It returns a new tree node with the given type, attributes and children. Children can be other nodes constructed using h, or strings. clobber takes a DOM element and a node or list of nodes describing the target state of the children of element, and will apply any DOM manipulations that are needed to make the two match. Using it inline is simple: clobber( document.body, h("marquee", [ h("span", {"style": "font-weight: bold"}, "Hello"), ", ", h("blink", "world"), "!", ]), ); When run, this example will update the DOM of the current document to be match what would result from the following HTML: Hello, World! Both h and clobber can accept child nodes either as variadic arguments or as a list. The following two calls are equivalent: h("ul", [h("li", ["milk"]), h("li", ["eggs"]), h("li", ["binliners"])]); h("ul", h("li", "milk"), h("li", "eggs"), h("li", "binliners")); Both will return a static tree that maps to the following html: Attributes can be set by passing an object as the second argument to h. There is no way to set attributes on the root element passed to clobber. This example will map to a div with width set to "200px": h("div", {height: "2000px"}, "TALL") Preserving input state Values set by BDC will take priority over previous changes by the user. Internally, BDC uses DOM property assignment wherever possible, only falling back to setAttribute if the element doesn't export the property of interest. This article is a good resource if you would like to learn more about the difference. Applications built with BDC are required to listen for changes to input state and update the node DOM to match. Failing to do so will result in the element DOM state being replaced the next time that clobber is called. It is very often acceptable to be lazy about this. If you can be certain that no other events will trigger a clobber, listening for the onchange event instead of oninput can be a reasonable optimisation. Event Handlers Attributes prefixed with on are bound as event handlers. BDC expects event handlers to be function objects, not strings. Event handlers are called in exactly the same way as if they had been bound using the addEventListener method. let red = false; function onclick(evt) { red = !red; redraw(); evt.preventDefault(); } function redraw() { let style = red ? 'color: red;' : ''; clobber(document.body, h("button", {onclick: onclick, style: style}, 'Click!')); } redraw(); Event handlers are a special case because the DOM API provides no way to enumerate event handlers bound to an element. BDC keeps a record of what event handlers it has bound to any elements, and will safely add, remove and deduplicate them, but will not touch existing event handlers. This may be a problem if BDC inherits a server rendered DOM with event handlers already in place as BDC will not know to avoid binding them again. This is one place where BDC does sort-of resemble a vdom library. Keyed updates Nodes can be assigned a key by setting the x-bdc-key special attribute. When updating an element, for every child node BDC will search through each unmatched child element to find the first with the same type and key. It will then move it to the next place in the list. If no match can be found, BDC will create a new element. This means that changes to nodes with the same key will affect the same element, even if the nodes are shuffled. This is essential for preserving input focus, and can potentially make updates faster by minimizing changes if nodes are inserted. In the following example, the order of two inputs is switched while preserving input state and focus. clobber($root, h("ul", {}, [ h("li", {x-bdc-key: "a"}, h("input", {})), h("li", {x-bdc-key: "b"}, h("input", {})), ])); clobber($root, h("ul", {}, [ h("li", {x-bdc-key: "b"}, h("input", {})), h("li", {x-bdc-key: "a"}, h("input", {})), ])); The BDC algorithm to figure out the mapping from key nodes to DOM elements is, worst case, O(n^2) in the number of nodes. If the elements are in approximately the right order, real performance will be closer to O(n). Autofocus BDC will automatically focus the last new element created with autofocus set to true. If autofocus is set on an already existing element, it will have no effect. If no new elements are created with autofocus set to true, BDC will preserve the current focus. clobber(document.getElementById("form"), [ h("label" {for: "input"}, "Text field"), h("input", {id: "input", autofocus: true}), ]); CSS The style attribute is set as a string, as most users would expect, but is implemented as a special case and therefore deserves mention. While most attributes have fairly straightforward DOM property counterparts, style is parsed and exposed as a CSSStyleDeclarationProperty. Rather than expect users to construct on of these for each render, BDC will set the existing objects cssText property to match what the user passes. Web Components BDC does make some guarantees that the identity of the nodes that it manages will remain stable (see Keyed Updates). It will, however, automatically remove modifications made by other code to the nodes that it is responsible for. To avoid this, you can wrap elements that should not be changed by BDC in a Web Component. BDC can instantiate Webcomponents in exactly the same way as it instantiates regular DOM elements, and will not reach into the shadow DOM. See examples/popover for a simple demonstration. Contributing BDC development is hosted on github at https://github.com/bwhmather/ bdc-js. Bug reports and pull requests welcomed enthusiastically. License BDC is made available under the terms of the MIT license. See LICENSE for details. About Ben's DOM Clobberer Resources Readme License MIT license Stars 17 stars Watchers 1 watching Forks 2 forks Releases 5 Release 1.0.0 Latest Oct 12, 2020 + 4 releases Used by 6 * @krausest * @prudepixie * @hans-lizihan * @bwhmather * @bwhmather Contributors 2 * @bwhmather bwhmather Ben Mather * @dependabot[bot] dependabot[bot] Languages * TypeScript 94.8% * JavaScript 4.2% * HTML 1.0% Footer (c) 2022 GitHub, Inc. Footer navigation * Terms * Privacy * Security * Status * Docs * Contact GitHub * Pricing * API * Training * Blog * About You can't perform that action at this time. You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.