DEV Community

artydev
artydev

Posted on

The magic function...

The core of VanJS is based on the following object:

const tags = new Proxy({}, {
    get: (tag, name) => {
        return (...args) => {
            let el = document.createElement(name)
            args.map(cl => {
                el.appendChild(typeof (cl) === 'string' ? cl = document.createTextNode(cl):cl)
            })
            return el
        }
    }
})
Enter fullscreen mode Exit fullscreen mode

When tags is accessed via any tag name property, the getter return a function which, when called, create a new DOM element based on the tag name, and that where resides the 'magic'

Example :

const div = tags.div
Enter fullscreen mode Exit fullscreen mode

Which can be rewritten

const { div }  = tags
Enter fullscreen mode Exit fullscreen mode

Let's use it to display a list of users Users :

const {div, h1, ul, li} = tags

const users = ["HAL", "BERT"]

let list_users = ul(...users.map((user) => li(user)))

document.body.appendChild(list_users)

Enter fullscreen mode Exit fullscreen mode

With that in place, you can go pretty far when building UI, without using any external libraries, neither VanJS.

Personnaly, I find this awesome. If you want to learn more, see my posts about VanJS

Top comments (8)

Collapse
 
efpage profile image
Eckehard

Building the DOM from Javascript functions is a big change in paradigm. Though it may look pretty similar to using HTML, it solves one of the main issues of the concept: DOM elements can be bound to local variables, giving you strong encapsulation, which is impossible using the standard getElementBy... functions.

There have been several approaches to use this concept, like

RE:DOM

const hello = el("p", "👋Hello");
mount(document.body, hello);
Enter fullscreen mode Exit fullscreen mode

VanJS

const Hello = () => div(
  p("👋Hello"),
)
van.add(document.body, Hello())
Enter fullscreen mode Exit fullscreen mode

DML

  p("👋Hello")
Enter fullscreen mode Exit fullscreen mode

You will find many similar projects on gitHub, but all more or less insulated from each other. Most of these projects are niche and don't have a huge impact.

Ok, there are gadual differences. VanJS implements a strong state concept, which I belive is not necessary if you are working directly on the DOM. On the other hand, building a page with DML is much easier than with RE:DOM or VanJS, as you do not need to append the elements manually. But this are nuances that do not make a general difference. In the core, the functionallity is very similar: They all use the HTML-DOM-API directly. And dealing with DOM references is already built in in Javascript.

I´m still missing a forum that bring all these projects and ideas together. What are the core principles?

  • The DOM is created from Javascript, no HTML needed
  • Properties and inline styles can be applied directly, but using CSS in the conventional way is still possible
  • DOM references are stored in Javascript variables, so DOM manipulation can be applied without retrieving the references
  • Reactivity can be supported by the browser, no virtual DOM needed

I´m sure, bringing all the good Ideas together could gain a huge benefit. Many of the concepts would not have worked 10 years ago. Browser compatibility was low, and the Javascript engines where struggeling with performance issues. But this has changed sinds the time, when React was created. Maybe, this are not concepts for all applications, but for a large portion of the developers it could make life much easier.

Collapse
 
artydev profile image
artydev • Edited

I agree with you on all the points.
Concerning the forum, a specific 'tag' for example 'VanillaDOM" annouced to the the DEV TO could be starting point

Collapse
 
artydev profile image
artydev

I think that if DML could be formatted, properly indented, in an Editor, it could be more easily adopted...

Collapse
 
efpage profile image
Eckehard

Sorry, I did not get your point? You can get indentation by adding { } in the code...

Thread Thread
 
artydev profile image
artydev

In the following code, the indentation between 'view' and 'end' is done manually
and disapear whenever you format with Pretier ar any other code formater

function Counter (start = 0) {
  const counter = state(start);
  let view = bdiv();
      h1("Counter ", counter)
      button("INC").onclick = () => ++counter.val;
  end()
  return view
}
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
artydev profile image
artydev • Edited

I have just tried

begin(document.body);
{
  let app = bdiv();
  {
    h1({ style: "text-align:center" }, "Demo VanJS / DML");
    Counter(0);
    Counter(4);
    Counter(56);
  }
  end();
  app.style = style_app;

  h1("Here is an orphan counter");
  Counter(12);
}
end();

Enter fullscreen mode Exit fullscreen mode

and the indentation is preserved, great :-D

Than you Eckehard

Thread Thread
 
efpage profile image
Eckehard • Edited

You can also use a short form for indentation in VScode, which is readable as well. It´s just a matter of personal preference:

  let app = bdiv(); {
    h1({ style: "text-align:center" }, "Demo VanJS / DML");
    Counter(0);
    Counter(4);
    Counter(56);
  }; end();
Enter fullscreen mode Exit fullscreen mode

With respect to Tao´s whish to keep the code as small as possible, van_dml contains just a minimal version of begin/end. The next version of DML, which I´m currently working on, will look like this:

begin(document.body);
{
    let app = bdiv();   
    {
        h1({ style: "text-align:center" }, "Demo VanJS / DML");
        Counter(0);
        Counter(4);
        Counter(56);
    }
    end(app);
    app.style = style_app;
}
end(document.body);
Enter fullscreen mode Exit fullscreen mode

This is super helpful in larger applications, as you immediately know, which level is closed by end(). You can still use end() without arguments, but if you include the reference, that was used in the corresponding begin(), DML performs a stack check to see, if all levels are closed correctly. This comes very handy in larger applications.

I preserved the option to perform multiple end´s like this, which sometimes makes the code shorter:

begin(document.body);
{
    let app = bdiv();   
    {
        h1({ style: "text-align:center" }, "Demo VanJS / DML");
        Counter(0);
        bdiv("border: 1px solid black;");   
        {
            h1({ style: "text-align:center" }, "Demo VanJS / DML");
            Counter(0);
        }
    }
}
end(3, document.body);
Enter fullscreen mode Exit fullscreen mode
Thread Thread
 
artydev profile image
artydev

Great Eckehard, I will be the first to use it :-)