Originally published on Medium
A few years ago if you needed to add some interactive feature or functionality to your website the chances are you’d reach out for the DOM-centric selector sexiness of jQuery. These days however with the rise of “the modern javascript framework” more and more developers are flocking to these tools, (and for good reason). But what if your site doesn’t require a state and a declarative UI… should we still be using jQuery or is there an alternative way, a better way, an ES6 way?
I want to state = { workInProgress: true } that this is very much a work in progress and more of a high level overview of our build process. It works for our specific needs as an agency but might not fit your own.
A precursor
In our static sites prior to moving to ES6 we would utilise libraries like Bootstrap and customise them accordingly using jQuery for any more complex interactivity. However after a while we found that our JS would quickly become bloated, unstructured, hard to maintain and difficult to pass between developers. While these issues can’t solely be blamed on our use of jQuery some technologies such as BEM, SASS and Twig encourage modularity, structure and maintainability in our codebase whereas jQuery doesn’t, and as a growing agency we wanted to change this.
The plan
We had a clear idea of what we wanted to accomplish with the transition from jQuery to ES6 so we laid out some key features:
Modularity
We wanted to break down our JS file structure to replicate something more similar to our SASS setup, separating functions into separate files and using ES6 import to pull through only what we needed per project.Structure
To improve cross-project knowledge and the passing of projects between developers we wanted to encourage common structuring of functions, at least at the beginning of a project.Adaptability
Sometimes our builds are passed onto a client who in turn insert them into their own stack to add more data driven content. Due to this it was important that our JS could be “hooked” (buzzwords yes please) into and updated accordingly, regardless of the client’s stack.Modern
At Inktrap we also develop more data driven web applications where the use of a framework such as React or Vue.js is required and ES6 is heavily used. We wanted to make sure that building a static site would also utilise the same or similar methods and thought process as our more complex builds, even if it wasn’t using a framework.
The results
We decided to first create a folder structure and split up our functions into files — this consisted of the following:
Within globals.js
we created two global objects, FUNCTIONS
& INSTANCES
which I’ll briefly go into now:
FUNCTIONS
:
An object of, you guessed it – all the function handlers in the site which can be called from the front-end at any time to initialise interactivity on any DOM elements.
INSTANCES
:
An object consisting of any initialised class instances (which we call “modules”) containing methods and callbacks that can be accessed by any other JS functions.
We then came up with a common structure for our function handlers, here’s an example being used in dropdownsHandler.js
:
As you can see within the function handler we are initialising a new instance of our dropdown
module. To give you a quick idea of what this does here’s an example of the initialisation and public methods that module provides.
Now - the above might seem a little like overkill just to initialise a simple dropdown. However using this method will give us access to two important abilities when it comes to “hooking” (yes I know) our front-end system into other client environments.
Before I go into that though, here’s a quick view of our load.js
file which deals with the overall initialisation of our function handlers on page load:
After using the initialisation method above we now have access to our global FUNCTIONS
& INSTANCES
objects from the window object and we can see what they contain in the console:
The above visualises those two important abilities I mentioned earlier - the first being we now have a list of our custom function handlers that can be called at any point using:
window.FUNCTIONS.dropdownHandler(‘.myPassedElement’);
this is particularly useful for initialising over dynamic content that’s been rendered after page load.
And secondly we now also have a list of any instances that have been initialised, each with their own methods and callbacks available to use elsewhere within our JS, for example:
window.INSTANCES.dropdowns.helloImADropdown.showDropdown();
Both of these abilities were essential in making our front-end templates more adaptable to any client environment due to the fact they can now “hook” (last time I promise) into almost any aspect of our JS.
Our learnings
After we implemented this approach on a couple of production projects this is what we learnt, aside from the benefits previously mentioned above:
Back-porting
With modularity comes improved maintainability. In other words, by splitting up all of our functions into defined files, whenever we added new functionality or fixed a bug, we could easily back-port it into all other projects using the same system without worrying too much about merge conflicts.Improved cross-project knowledge
If every function is built using the common initialisation structure it makes it easier to pass between developers as they already have an idea of what they’re looking for and working with.Removing reliance and improving control
By removing libraries such as bootstrap and jQuery we no longer relied on third party tools so heavily and had complete control over what our clients had access to and could utilise from our front-end JS.Fallbacks
Because we do not use a javascript dependant view (essentially we are using the base stack of HTML, CSS, JS) if for some reason our JS doesn’t load the user will still receive some resemblance of our site with HTML and CSS. Essentially we add the JS as an additional interactive layer, initialised by our markup.
Final thoughts, feelings & emotions
Now many of these practices above have been used before however usually in the context of a framework rather than a static site which is what gave me the idea to write this article. I believe there is a space between a static site and a full blown JS SPA which needs some attention and although I don’t think this is the perfect solution, in my mind its a start.
As I mentioned also in the beginning of this article, this is very much a high level overview of our build process specifically looking at our transition from jQuery to ES6 and the benefits it’s had. There’s plenty more I would like to go into such as how we initialise our JS from markup in a consistent way, our SASS/BEM setup and even potentially open sourcing our boilerplate repo but in the interest of time I wanted to keep this article (relatively) concise.
I do however plan on continuing to report on our learnings as we meander through these ever changing times of JS.
If you’ve got any feedback we’d love to hear from you — please leave a comment here, or drop us a line on Twitter, we’re . @InktrapDesign
Top comments (6)
Man I want to review that code, it's good but there are a few small nitpicky things 😎
Ha, thanks - the plan is definitely to open it up for feedback, nitpicking and PR's if there is interest. The more collaboration the better 👍
Out of interest what would you change?
Just a few little thing really, I'm all about details.
There are some let's that should be const because you don't change the values of them, I use way more const than let usually.
Perhaps more destructuring would be great.
I would also not use uppercase variables for the window object literals. Uppercase is is generally used for constants.
The try catch, not sure that's doing much because errors will be reported either way and there is no fallback logic anyway to handle a fault.
The comments could consolidated to reside in the jsdoc block with a @description, you might not need to explain as much with the comments, line by line.
That's mostly opinion and subject to code style and reasons I'm not aware of 😋
The 😈 is always in the details..
Agree that there are probably too many miscellaneous let's lying around.. For the window objects I guess I see them as constants in some ways - as in they are used throughout the application and shouldn't be changed, however they are appended to so perhaps uppercase is not appropriate.
The try catch is more to stop the whole thread crashing (known potential other issue..) if there is an error within a function handler. And with the comment's style it was more for the purpose of this article rather than for production, but none the less a good point.
Really appreciate the feedback, will keep you posted if we manage to open source it ✨
I am loving this, making a conscious decision on needing a framework. 👍 Great post!
Thanks, exactly - they are great, but not always.. Appreciate the love 👍