DEV Community

Cover image for Give an eye to VanJS, it costs nothing and you could be surprised
artydev
artydev

Posted on

Give an eye to VanJS, it costs nothing and you could be surprised

VanJS (abbr. Vanilla JavaScript) is an ultra-lightweight, zero-dependency and unopinionated Reactive UI framework based on pure vanilla JavaScript and DOM. Programming with VanJS feels a lot like React

Sample

import van from "./van.js"

const {span, button} = van.tags

const Counter = () => {
  const counter = van.state(0)
  return span(
    "❤️ ", counter, " ",
    button({onclick: () => ++counter.val}, "👍"),
    button({onclick: () => --counter.val}, "👎"),
  )
}

van.add(document.getElementById("app"), Counter())
Enter fullscreen mode Exit fullscreen mode

Top comments (4)

Collapse
 
efpage profile image
Eckehard • Edited

With some very small additions we can enable a simplified composition style in VanJS, which does not restrict any other options or styles. Included in VanJS this should give only very little additional code...


    /* ******************************************************
        Some additions to VanJS
       ****************************************************** */
    let _base;              // Global element stores current insert position. If empty, elements are added at the end of document.
    let _baseStack = [];    // Stack for storing _base positions
    let _level = 0;

    /*------------------------------------------------------
       Set a new basepoint, save return adress on baseStack
      ------------------------------------------------------ */
    function begin(ID) {
      // Save old base
      _baseStack.push(_base);
      if (_baseStack.length > 100) {
        alert("error: _baseStackOverflow in begin()");
        _baseStack = [];
      }
      // select new base, either ID or element
      if (typeof (ID) === 'string')
        _base = document.getElementById(ID)
      else
        _base = ID;
      return _base;
    };
    /*------------------------------------------------------
       restore last base from stack, returns _base.
       if cnt set, unselectBase is called cnt times
      ------------------------------------------------------ */
    function end(cnt = 1) {
      for (let i = 0; i < cnt; i++) {
        if (_baseStack.length == 0) {
          ("error: _baseStack empty in end()")
          break
        }
        else
          _base = _baseStack.pop()    // restore old stack
      }
      // if chk, check Stacklength after pop == chk
      return _base;
    }

    // Function wrapper (would be unnecessary if included)
    function f(tagFunction) {
      return (...args) => {
        _level++
        let ret = tagFunction(...args) // Call 
        if (--_level === 0)
          if (_base)
            van.add(_base, ret)
        return ret;
      }
    }

    // Tag definition
    var { button, div, br, pre } = van.tags

    // apply wrapper function for auto-append (unnecessary if included)
    div = f(div)
    button = f(button)
    br = f(br)


    /* ******************************************************
        End additions to VanJS
       ****************************************************** */

    // Example code from VanJS
    const sleep = ms => new Promise(resolve => setTimeout(resolve, ms))

    const Run = ({ sleepMs }) => {
      const headingSpaces = van.state(40), trailingUnderscores = van.state(0)
      const animate = async () => {
        while (headingSpaces.val > 0) {
          await sleep(sleepMs)
          --headingSpaces.val, ++trailingUnderscores.val
        }
      }
      animate()
      const helloText = van.bind(headingSpaces, trailingUnderscores,
        (h, t) => `${" ".repeat(h)}🚐💨Hello VanJS!${"_".repeat(t)}`)
      return div(pre(helloText))
    }

    // Start the document
    begin(document.body)
      begin(div({style: "border: 3px dashed blue; padding : 10px; border-radius: 10px; display: inline-block; min-width: 600px; "}))
        const dom = div({style: "border: 2px solid; padding : 10px;"})
        begin(div({style: "border: 2px solid; padding : 10px; display: inline-block;"}))
            button({ onclick: () => van.add(dom, Run({ sleepMs: 2000 })) }, "Hello 🐌")
            button({ onclick: () => van.add(dom, Run({ sleepMs: 500 })) }, "Hello 🐢")
            button({ onclick: () => van.add(dom, Run({ sleepMs: 100 })) }, "Hello 🚶‍♂️")
            button({ onclick: () => van.add(dom, Run({ sleepMs: 10 })) }, "Hello 🏎️")
            button({ onclick: () => van.add(dom, Run({ sleepMs: 2 })) }, "Hello 🚀")
        end()  
      end()
    end()
  </script>
</body>
</html>
Enter fullscreen mode Exit fullscreen mode

This is the working example

Collapse
 
artydev profile image
artydev • Edited

Thank you Eckehard :-)

Collapse
 
efpage profile image
Eckehard

There are different ways to add the "DML-Style" to VanJS, but the most convenient would be an extension of the VanJS-core. I made a pull-request to add this feature. Please check out!

Thread Thread
 
artydev profile image
artydev • Edited

Hy Eckehard,
For me that would be a very great addition :-),
You have reached what I have wanted to do with MVU :-)
Hope Tao will find this nice too :-)