We often think about the relationships between our components as a tree.
The DOM is also represented this way, with parents and children and siblings and ancestors.
I've noticed that many other problems can be transformed to look like trees, too.
This transformation can allow new perspectives, new thoughts, and new solutions to old problems.
Here are a few examples that I've been able to come up with.
Normally when we tab around on a web page it's one big loop. Once we reach the end of the page, the next tab brings us back to the beginning.
This is most simply represented as a list based on the order of the elements. We can change this order using the
tabindex attribute, but in practice this is rarely done.
This list doesn't quite cut it though, because as soon as you open a menu or drop a dropdown, the tab order is transformed into a tree.
The tab order of the menu is nested inside of the tab order of the page. If you imagine it as a tree of components, the menu is a child of the page, and the menu itself has children that can be tabbed to.
For the sake of simplicity I'm using the word "tab" quite broadly here. In real applications you'd use a combination of tabs, arrow keys, escape keys and so on to shift focus around the page.
If this menu had a sub-menu, the focus order (or tabbing order) of that sub-menu would be nested inside of the menu, creating a tree three levels deep.
I have had a lot of success thinking about focus order in this way. It's allowed me to write much simpler components for managing focus than I could have if I kept thinking of focus order (or tabbing order) as just a simple list.
But what happens if there are no menus, and the tabbing order really is just a list?
Luckily for us, a list is just a tree that has only one level — a tree with no branches.
If you're not convinced yet, sketch it out on a piece of paper. Once you start to work through it I'm sure you'll see that this is true.
This one is more advanced, and I haven't found a use for it yet, but I do find it interesting.
With the CSS property of
z-index, we can set a number that tells the browser how to stack different elements on top of each other. Elements can either be above or below each other — in other words, a list of elements.
But the browser will also create "stacking contexts", groups of elements that it will send to the GPU as one chunk to process. For example, if you use
opacity on an element, it breaks that off into a new stacking context so it can more efficiently render that piece on it's own.
Stacking contexts are a really interesting optimization that browsers perform — definitely worth looking into if you haven't already.
But what's interesting to me is how these stacking contexts affect z-index. Since stacking contexts group elements together, if one stacking context is below another, all elements in the lower context are below all elements in the higher context, no matter what the z-index values are.
Again, we can represent how these elements are stacked as a tree. Stacking contexts can be nested within each other, just like children nodes in a tree.
Since I discovered these examples, I've been thinking more about what I'm calling "abstract trees" (not totally sold on the name yet).
In both of these examples, we've put these elements into a new tree structure that is completely separate and distinct from the structure in the DOM. These trees provide a new way of organizing these elements, which can sometimes be very different than how the DOM organizes them.
The z-index tree includes all elements the DOM has, and in roughly the same order.
The focus order tree though, only has a few elements from the DOM, and often has them completely rearranged.
Creating abstract trees with slots
I'll leave you with one more idea that I think is quite powerful:
The main role of slots is to let us create abstract trees.
Without slots, the structure of your Vue application has to exactly mirror what gets output to the DOM. Or put another way, slots are the mechanism that allow you to structure your Vue application independently from how you want your markup to be rendered.
Because slots let us decouple our Vue components from what is rendered, they let us create powerful abstractions.
For a long time I've thought that slots are the key to creating highly reusable code with great abstractions. Now I think I'm beginning to understand why that's true.