DEV Community

Cover image for Reactive DOM
Jin
Jin

Posted on • Originally published at mol.hyoo.ru

Reactive DOM

Previously, the DOM was slow and inconvenient. To cope with this, various template engines and techniques were invented: VirtualDOM, IncrementalDOM, ShadowDOM. However, the fundamental problems of RealDOM remain:

  1. Greed. The browser cannot at any time ask the application code “I want to render this part of the page, generate me elements from the middle of the fifth to the end of the seventh.” We have to first generate a huge DOM so that the browser only shows a small part of it. And this is extremely resource-intensive.
  2. Indifference. The state of the DOM logically depends on both application states and the states of the DOM itself. But the browser doesn't understand these dependencies, can't guarantee them, and can't optimize DOM updates.
  3. Thorny. In fact, we don't need the DOM. We need a way to tell the browser how and when to render our components.

Well, okay, let's imagine what would happen if the DOM and the rest of the runtime were reactive. We could, without any libraries, connect any states through simple invariants and the browser would guarantee their execution in the most optimal way!

I sketched out a small sketch of what it might look like. For example, let's take and bind the paragraph text to the value of the input field:

<input id="input" />
<p id="output"></p>
<script>

    const input = document.getElementById('input')
    const output = document.getElementById('output')

    Object.defineProperty( output, 'innerText', {
        get: ()=> 'Hello ' + input.value
    } )

</script>
Enter fullscreen mode Exit fullscreen mode

And that's it, no libraries, no event handlers, no DOM manipulation. Only our desires in their purest form.

Do you want to try ReactiveDOM in action right now? I published a prototype of the polyfill $mol_wire_dom.

It is not very efficient, it does not support many things, but for demonstration it will do:

<div id="root">

    <div id="form">
        <input id="nickname" value="Jin" />
        <button id="clear">Clear</button>
        <label>
            <input id="greet" type="checkbox" /> Greet
        </label>
    </div>

    <p id="greeting">...</p>

</div>
Enter fullscreen mode Exit fullscreen mode
import { $mol_wire_dom, $mol_wire_patch } from "mol_wire_dom";

// Make DOM reactive
$mol_wire_dom(document.body);

// Make globals reactive
$mol_wire_patch(globalThis);

// Take references to elements
const root = document.getElementById("root") as HTMLDivElement;
const form = document.getElementById("form") as HTMLDivElement;
const nickname = document.getElementById("nickname") as HTMLInputElement;
const greet = document.getElementById("greet") as HTMLInputElement;
const greeting = document.getElementById("greeting") as HTMLParagraphElement;
const clear = document.getElementById("clear") as HTMLButtonElement;

// Setup invariants

Object.assign(root, {
    childNodes: () => (greet.checked ? [form, greeting] : [form]),
    style: () => ({
        zoom: 1 / devicePixelRatio
    })
});

Object.assign(greeting, {
    textContent: () => `Hello ${nickname.value}!`
});

// Set up handlers
clear.onclick = () => (nickname.value = "");
Enter fullscreen mode Exit fullscreen mode

Here we also applied $mol_wire_patch to make global properties reactive. Therefore, when the browser zoom changes, the interface size will change to compensate. When you click the button, the name entered in the field will be cleared. And the current name will be displayed in the greeting, which is shown only when the checkbox is checked.

Now answer me a simple question: why don’t we still have something like this in browsers?!?

Top comments (0)