Docs
Virtual DOM
Learn Virtual DOM

Learning Virtual DOM

⚠️

Looking for React example? Go back to the Start Here page

Create a blank HTML file somewhere on your computer with a name like: mega-millions.html. If you want to edit in your browser, fork this Stackblitz.

Open in StackBlitz

Using a text editor, fill the file with these contents:

<html>
  <head></head>
  <body>
    <script type="module">
      import { render } from 'https://cdn.skypack.dev/million';
      render(document.body, 'You won $$$!');
    </script>
  </body>
</html>
You Won $$$!

Open your file in a web browser, if you see You won $$$!, you're ready to rumble!

Now that you're all set up to play around, let's look at a couple practical examples as a foundation for teaching you the basics of Million. By the end of this exercise, you should be more than equipped to start building stuff on your own.

  1. Building a counter
  2. Building a search input

Building a counter

Let's start with a simple "counter" example to demonstrate the basics of rendering and virtual node construction, two core features.

Insert the following into the <script> tag:

import { _, m, render } from 'https://cdn.skypack.dev/million';

let seconds = 0;

setInterval(() => {
  render(document.body, m('p', _, [`Time elapsed: ${seconds}`]));
  seconds++;
}, 1000);

Time Elapsed: 0

Now, you can see with a bit of JavaScript, we've created an interactive "counter".

Let's walk through what's happening briefly:

  • render() function has a standard interface that is used in many Virtual DOM libraries. First argument is a DOM node that will be used as the parent DOM reference, and the second one is a Virtual DOM to render.

  • m() function will instantiate a "Virtual DOM" node for an element.

Basically, the m() function is how you construct what you want the UI to look like, and the render() function is when you want to put it on the screen.

Building a search input

Now that we've seen some basic functionality, let's see how we can build a search input with more complex features.

import {
  _,
  m,
  render,
  startTransition,
  style,
  kebab,
  Flags,
} from 'https://cdn.skypack.dev/million';
import { compareTwoStrings } from 'https://cdn.skypack.dev/string-similarity';

const entries = ['Apple', 'Banana', 'Grape', 'Orange', 'Strawberry'];

const update = (highlightedEntries) => {
  const vnode = m('div', _, [
    m('input', { onInput: ({ target }) => search(target.value) }, []),
    m('ul', _, highlightedEntries, Flags.ELEMENT_KEYED_CHILDREN),
  ]);
  startTransition(() => {
    render(document.body, vnode);
  });
};

const search = (query = '') => {
  update(
    entries.map((entry) => {
      // Compare two strings returns a float based off of similarity
      // Ex: 0.8 means high similarity, 0.2 means low similarity
      const shouldShow =
        compareTwoStrings(entry.toLowerCase(), query.trim().toLowerCase()) >
        0.25;

      return m(
        'li',
        {
          key: `${entry}-${shouldShow}`,
          style: shouldShow
            ? style(kebab({ textDecoration: 'underline', fontWeight: 'bold' }))
            : undefined,
        },
        [entry],
      );
    }),
  );
};

search();
  • Apple
  • Banana
  • Grape
  • Orange
  • Strawberry

By default, all of the "entries" (Apple, Banana, Grape, Orange, Strawberry) will be listed, but you can highlight them by typing into the text input. As you type, the list of entries will change to reflect what you're searching for.

Now there's quite a bit happening here, so let's go through this snippet piece by piece.

  • VNode props (the second parameter in the m() function) reflect the props and attributes on a real DOM node. Just like real DOM nodes, we can use onInput to bind an event handler to the input element. The handler calls the search function, which takes a query and determines the entries that match the query.
  • We can use the <Array>.map() function to help us construct VNodes from a non-VNode array.
  • The key property on the <li> element and the ELEMENT.ONLY_KEYED_CHILDREN flags allows Million to shortcut to an optimized fast path. Every element inside the <ul> tag is unique and non-changing, which is why we can used the keyed diffing algorithm.
  • The style function converts a style object into a string, and kebab converts camelCase properties to kebab-case.
  • We can use the startTransition() function to schedule a task to be run on the next animation frame, to ensure over 60 FPS.

Recap

If you've made it this far, you've been exposed to the following features of Million:

That's a great start, however, there are many more features to sink your teeth into. The best way to absorb Million is to read through this documentation. No need to comb over every word, but if you at least glance through every page you will be MUCH more effective when creating libraries or applications with Million.

Happy Coding!

Last updated on July 28, 2022