This is series of articles where I’m experimenting with Vanilla JS way of writing modern web applications. I’m trying to find a pattern, that would bring fast, fun and powerful development process without using libs.

Part 1 was about creating old-fashion server-side apps with Node.js

Part 2 was about finding a way to develop dynamic user interfaces

Part 3 is going to extend Part 2, it's about client side JS too

What I came to so far:

App consist of components

Components are available on the client and on the server

Components live in a global namespace ( window / global )

/ ) Each component is just a JS function which returns HTML string

Events are mounted inline via attributes.

For example, Box component:

function Box({ text }) { return ` <div class = "box" onClick= "Box.onClick()" > ${text} < /div> `; } Box.onClick = () => { alert( 'box was clicked' ); };

Part 3

In previous article I ended with NameEditor example:

Here is the code from part 2 ( instanceIndex is replaced with id ):

function App ( { url } ) { return ` ... ${NameEditor({ id: 0 } )} ${NameEditor({ id: 1 } )} ${NameEditor({ id: 2 } )} ... ` } function NameEditor ( { id } ) { return ` <div class="nameEditor"> <div>Enter you name:</div> <input onKeyUp="NameEditor.onKeyUp(this, ${id} )" /> <div> Hello, <span id="nameEditor__name- ${id} "></span> </div> </div> ` ; } NameEditor.onKeyUp = (input, id) => { document .getElementById( `nameEditor__name- ${id} ` ).innerText = input.value; };

Now I'm going to play with NameEditor . Let's add this feature: editing name inside one NameEditor would change the name of all other NameEditor s of the same kind. Let me show what I mean:

Actually this is very easy to implement, I just need to use getElementsByClassName instead of getElementById :

function NameEditor ( { id } ) { const color = { 0 : '#b6e1c1' , 1 : '#b7c4ed' , }[id]; return ` <div class="nameEditor" style="background: ${color} "> ... Hello, <span class="nameEditor__name- ${id} "></span> ... </div> ` ; } NameEditor.onKeyUp = (input, id) => { const elems = document .getElementsByClassName( `nameEditor__name- ${id} ` ); for ( const elem of elems) { elem.innerText = input.value; } };

It works. Nice. But whilst getElementsByClassName is a very fast operation, it's not a good idea to call it every time I need to access elements.

I need some sort of cache system. The cache should keep references to elements, and it should be automatically updated whenever elements are added/removed.

Seems like it's not a trivial task. What a lame! I don't wanna write this cache system. How cool it would be if browsers had this caching mechanism out-of-the-box...

And you know what? Browsers do have it! Yes, all browsers starting from IE9 have this caching mechanism included. I was pretty shocked when I discovered it. It turns out, that getElementsByClassName returns live HTMLCollection . Here is what live means:

const Box = () => '<div class="box"></div>' ; const boxes = document .getElementsByClassName( 'box' ); document .body.innerHTML = ` ${Box()} ${Box()} ${Box()} ` ; console .log(boxes.length); document .body.innerHTML = ` ${Box()} ` ; console .log(boxes.length);

boxes variable is automatically updated when elements are added/removed. Isn't it crazy cool? I'm in a web development for a pretty long time, but I never heard of this feature.

What a relieve, now I don't have to worry about caching. And here is the same code, but using benefits of live HTMLCollection:

const nameElems = {}; function getNameElems ( id ) { return nameElems[id] || ( nameElems[id] = document .getElementsByClassName( `nameEditor__name- ${id} ` ) ); } NameEditor.onKeyUp = (input, id) => { for ( const elem of getNameElems(id)) { elem.innerText = input.value; } };

This code can scare you, because it's very verbose. Let's imagine we have watchClass function, which hides some verbosity:

const nameElems = watchClass( id => `nameEditor__name-${ id }`); NameEditor.onKeyUp = (input, id ) => { nameElems( id , elem => elem.innerText = input.value); };

Or even imagine we have changeInnerText which can shorten all the code into just one line:

NameEditor.onKeyUp = changeInnerText( 'nameEditor__name' );

As you can see, verbosity is not a problem. Tiny helpers can hide all the verbosity.

I have not implemented something significant in this article, but I've found a great way to track elements. From now I will write all code using getElementsByClassName and I will no longer use getElementById .

In Part 4 (will be available soon) I'm going to use all the techniques I've discovered so far to write something more complex.