Respect the DOM

Cvetanov Goce
Trustpilot Technology
5 min readMar 16, 2019

--

Thoughts on shared responsibilities of library users and maintainers

Who owns the DOM

Nowadays most of the software products being shipped are single page applications which have a completely different structure than the server side rendered web pages before. One of the main differences is the shared DOM. One single page application is active beyond the initial render and the complete DOM is not recreated on every action or navigation.

The DOM is one common thing that we, the core contributors and authors of a project, share with all the maintainers of the libraries we are using. In that sense, the DOM becomes this sacred thing that we should all respect and take care of.

In a sense of ownership of the DOM, each team gets their own part of the DOM to take care of, but it’s the team that owns the project that is responsible for the DOM as a whole. This means that if you, as a library author, need to step out of your context and make changes to the DOM which are out of your scope, you are trespassing in some territory which is not primarily in your ownership. Now, if we are restrictive in regards to what parts of the DOM we can change, we will probably not have all of these nice little libraries to work with which allow us to reach our business goals with a lot less effort than if we were to do everything ourselves and that is the good part. However, if we are touching something which is not fully ours, common understanding and good manners applied, it is expected that we put it back as we found it.

“Nice and Clean” Photo by Khai Sze Ong on Unsplash

Why do we need to own the DOM

A very common use case is when we are injecting DOM elements outside of the scope of our library, such as loading a third party script in the body, injecting a stylesheet in the head, making styling changes or attaching event listeners to elements which we did not create in the first place, and so forth. In some use cases, implementing the main feature that our library offers will obviously lead to having to interact with the context in which it is used, so we would reach out of our scope to achieve this. However, that does not give us the right to just achieve the goal we claim our library achieves and then just assume that our library stops being used at the same time as the parent app stops being used. There is a high probability that our library will be injected in the parent app dynamically which means that it might also be unmounted and destroyed at some point in time. Therefore, reaching out of our library’s context and fiddling around with the DOM means that we are leaving stuff behind after our library ends up in the parent project’s garbage (unmounted, not used, destroyed, whatever term fits). Those leftovers are now falling under the jurisdiction of the author of the project and owner of the DOM as a whole to clean up.

Example

In order to better illustrate what I mean, I will share an example. I was particularly careful when thinking about what example I should share. If I have chosen an npm library from a single, or a team of contributors, it wouldn’t have had that big of an impact since there are so many libraries out there and it could be that that particular one is just flawed. That is why, I have picked the Google Autocomplete widget as an example.

Here is a short explanation of the use case. You want to have a form and in there you need to enter a valid address, so what is the API that is best suited for working with addresses? First thought, it’s gotta be something that Google offers since their Maps are what we use on daily basis for navigating. So you go over to the Google Developers section and search for something that suits your needs and you find their Google Places Autocomplete widget. Pretty good documentation on how to set it up and you end up with a fully functional autocompletable input field for entering an address in your form. Your form is of course rendered conditionally in your SPA (when you choose to add or edit an item). But what happens after a few invocations and usages of this widget? Here are two screenshots of the DOM after adding and updating a couple of items by using that form (which means the form got rendered and then unmounted several times, thus, the widget got created and destroyed several times as well).

Aftermath of using the autocomplete widget — unused elements and styles in the DOM

The widget does not offer any on destroy callbacks or unmount events which means you end up with a bunch of items that, although hidden, are not used at all and are polluting the DOM. Now it is your responsibility, as the author and owner of the project, to clean that up by using some DOM manipulation yourself (maybe by targeting the elements based on their class, which is not documented anywhere, meaning they are internal and may change at any moment, thus breaking your clean up logic).

There are plenty of other libraries and widgets which are pretty creative in manipulating the DOM to achieve their goals that leave the DOM in a similar, or even worse situation (imagine if those div elements were not hidden 😱).

Summary

Let’s take more ownership of the stuff we build and clean up after ourselves.

We can start by searching for document. in our project and make sure that all of our appendChild, addEventListener , addClass , style and other DOM mutating calls are reversed and cleaned up when our code has done its purpose and is no longer needed. We can utilise the framework specific opt-ins for doing this, such as Vue’s beforeDestroy and React’s componentWillUnmount (or the returning callback in case you already embraced hooks and useEffect).

--

--