How to create fluent interfaces the easy way with vanilla JavaScript
Fluent interfaces make your code more readable and, just as importantly, more fun to work with. We’ll build a small DOM manipulation library with a fluent interface to show how this simple technique can improve your code style and efficiency.
--
Fluent interfaces are a beautiful thing.
I mean, when you take something like this:
And compare it to this:
Ok, it’s not like the first is completely unreadable, at least not with this simple example. But if you’ve had significant experience working with native DOM methods in JavaScript you know how much more clunky and cluttered it can get.
And doesn’t the second example feel more like the actual HTML fragment the code is creating? Even if you don’t have trouble reading the first example, having code to generate HTML that reads like what’s actually going on and nests like HTML elements helps you see what’s going on more clearly at a glance.
Remember, it’s not just about how terse you can make your code or what neat tricks you can do with it that matters; keeping in mind how your code clearly presents the end product is also important. And I would say element.addId('id')
more intuitively depicts what’s going on behind the scenes than element.id = 'id'
.
Plus it’s not really much fun to type all those variable declarations, equals signs, and semicolons.
…obviously that last bit is just my ever so humble (but accurate) opinion…
By the way, kids, always use semicolons — Andrea Giammarchi, paraphrased
Fluent Interfaces, tl;dr
First, what is a fluent interface?
In software engineering, a fluent interface (as first coined by Eric Evans and Martin Fowler) is a method for designing object oriented APIs based extensively on method chaining with the goal of making the readability of the source code close to that of ordinary written prose, essentially creating a domain-specific language within the interface. (source)
Let’s say the list item has buttons to perform different actions for each task, like marking it complete. Assuming there’s some kind of data persistence on the backend, here’s what this could look like with jQuery:
The fluent interface makes the code much more compact and readable than it would have been otherwise.
How to create a simple fluent interface
The essential element of creating a fluent interface with chainable methods in JavaScript is to write methods that perform an action on the object and then return this
.
We’ll build a small library to add and modify HTML elements to a document since working with the DOM is a great use case for this sort of thing.
Step 1: Define the constructor
I prefer using the old style constructor with methods declared on the prototype to ES2015’s class
syntax both because get off my lawn and, more importantly, because I agree with Eric Elliott that class
in JavaScript is fundamentally flawed.
We’re just going to make a wrapper around a regular HTML element for simplicity. If the argument passed to the constructor is an instance of the DOM Level 2 HTMLElement
interface we’ll just wrap it with the object. Otherwise it should be a string so we’ll use document.createElement
to make it.
We’ll also add a create
method to call the constructor and allow immediate chaining.
Using HtmlElement.create
not only allows you to chain your element creation in the example above, but also absolves you of having to remember to use new
, which is a common source of bugs.
Step 2: Methods to add attributes and text content
Now let’s define the attribute methods used in the example. Note the use of the ES2015 rest parameter syntax in addClasses
. We’ll also set the element’s textContent
property and use shortcut evaluation with the ||
and &&
operators for cases where the relevant properties are undefined
. That will be relevant when we add methods to create child elements, in case we don’t want to specify all the properties for every child element.
Now you can create an element, id and any number of classes one nice method chain.
Obviously for a real application or library you’d need methods to add other attributes, including custom and data attributes. You’d also want methods to remove attributes as well, but this will do for an example.
Step 3: Add methods to nest child elements
These methods do exactly as advertised, adding one or more child elements to the parent.
Step 4: Methods to select and append elements
Obviously if we were using this in the wild we’d want more methods to select and manipulate elements, traverse the DOM, and more, but for demonstration purposes these three will do.
Step 5: Put it all together
Here’s the complete “class.”
Moving forward: What next?
Now that we’ve seen a basic implementation of a fluent interface, it’s simple to make additions and improvements to extend functionality.
Let me know in the responses if this is helpful, and how you might use fluent interfaces to improve your own projects in the future!