Flexible JavaScript Components w/o the Overhead

Patrick W
4 min readOct 22, 2019

--

We all love our JavaScript components these days, whether it be React, Vue, Ember, Web Components, etc. When I first started using these frameworks, I was amazed with the modularity and re-usability of the components. They finally provided a good way of organizing a block of HTML, with associated state and behaviors.

But after only a short time, I started to become weary of ‘writing for the framework’ instead of using conventional JavaScript patterns and plain HTML. I grew tired of maintaining an endless list of NPM packages that my app somehow relied on. If you grew up manipulating the DOM and writing event handlers, the modern frameworks often begin to feel like overkill. I wanted to have more control over my components, and be able to use the components seamlessly in existing (and possibly legacy) websites without modifying any build tools.

Introducing patchScriptUI

So… I decided to write my own component system that aims to provide raw utilities that enhance vanilla JavaScript features. I call it patchScriptUI and it’s open source on GitHub.

A UI library for people who already know JavaScript DOM manipulation and event handling.

The library itself is around 150 lines of JavaScript with ZERO dependencies, and it provides the following utilities:

  • Creating a component

That’s it. In the following examples, I will demonstrate how these components can be used with zero build tools.

Component Definition

A patchScriptUI component is simply a JavaScript object of the form:

{
template: htmlTemplateLiteral,
behavior: function()
}

Where ‘template’ is a string of HTML, and ‘behavior’ is a function that defines… the behavior of the component.

But, for the sake of re-usability, we can write a named function that returns this object. As an example, let’s create a component that changes it’s background color each time it’s clicked.

function colorChanger()
{
function behavior()
{
} return({
template: `<div style="width:250px;height:250px">
</div>`,
behavior
})
}

This is the general outline of the entire component. One thing I haven’t mentioned yet is that the library binds the created HTML element to the ‘this’ keyword inside of your provided behavior() function. The behavior() function also only runs 1 time after the component is created.

I still love jQuery, so let’s use it to add some event listeners to our new component:

function colorChanger()
{
function getColor()
{
function getColorNum()
{
return Math.floor(Math.random() * 255);
}
var rgb = {
r: getColorNum(),
g: getColorNum(),
b: getColorNum()
}
return `rgb(${rgb.r},${rgb.g}, ${rgb.b})`;
}
function behavior()
{
//get initial color:
$(this).css("background-color", getColor());
//Setup actual event handler:
$(this).on("click", function () {
$(this).css("background-color", getColor());
});
}
return ({
template: `<div style="width:250px;height:250px">
</div>`,
behavior
})
}

As you can see, in the behavior() function we set our components background color using getColor(), which returns an RGB color as a string. We then setup an event-handler to get a new color on each click. This will create:

Now you may be thinking, “hey, we just wrote a JavaScript function. How’d the component get onto the DOM?” Super simple. You define an HTML element anywhere on the page, and give it a unique ID. For example:

<div id="colorChanger"></div>

This is where the library comes in. Before you can use a component, you must register it’s container. This let’s the library remove the container element from the DOM until the component is actually rendered. This allows us to conditionally render components without littering the DOM with empty containers. Once the container is registered, you can simply call patchScript.createComponent to render the component instance:

//This method can accept a singular 
//parameter, or an array of ID's to register
patchScript.registerContainers('colorChanger')
//Now, we can render the component whenever we want like:
patchScript.createComponent(colorChanger(), 'colorChanger')
//createComponent also returns the resulting HTMLElement, in case you want to keep track of it.

That’s the basics of the component system! Using just these 2 methods, you can create complex components (including child-components) using just your vanilla JavaScript expertise. This is where you can stop reading and start experimenting. However, if you want to see examples of more complicated components, keep reading.

Going Deeper

To demonstrate parent-child relationships, let’s create a component which renders a variable number of the colorChanger() we defined previously.

Using the library to create components inside of another component is actually the same workflow:

function colorChangerContainer(numColorChangers)
{
var colorChangerIDs = [];
for(var i = 0; i < numColorChangers; i++)
{
//The library provides a function to get a uniqueID:
colorChangerIDs.push(patchScript.getUniqueID())
}
function behavior()
{
//Register all of our containers:
patchScript.registerContainers(colorChangerIDs);
//Render all child colorChangers:
for(const uniqueID of colorChangerIDs)
{
patchScript.createComponent(colorChanger(), uniqueID)
}
} return({
template:`<div>
<h1>Here are ${numColorChangers} colorChangers!</h1>
${colorChangerIDs.map(uniqueID => `<div
id=${uniqueID}></div>`).join('')}
</div>`,
behavior
});
}

Let’s render this component:

<div id="colorChangerContainer"></div><script>
patchScript.registerContainers('colorChangerContainer');
//Let's have our container render 3 colorChangers:
patchScript.createComponent(colorChangerContainer(3),
'colorChangerContainer')
</script>

This will create:

Wrapping Up

That was a very quick overview of the provided methods. To see examples of more complicated patterns, including async component functions and event handling between components, head on over to the GitHub!

For now, here’s a loginForm() component that interacts with several child components under the hood including a ‘captcha’ made up of our colorChanger components:

--

--