The DOM Quest
Tally Barak
Posted on October 19, 2019
I had some experience in Angular and recently had to work with Vue and React.
[In case you are worried, this is not one of the Gazillion posts comparing Angular and React with a clear agenda hidden behind some pseudo-objective facade].
I was used to working with Angular in a certain way and was really surprised the first time I inspected the DOM of the React app.
There were no components!
Look at the 3 images below: the first one is... Wait. give it a try yourself to see which one is each. All apps are built with similar components and are based on standard CLI bootstraps.
[The 3 applications are part of the Bit beginners tutorial. You can find their code here: Angular, React and Vue. ]
Did you guess? The first one is React, the second Angular and the last one is Vue.
Here is something to note on the Angular app: it has elements called: <app-root>
, <app-top-bar>
and <app-product-list>
. The 3 components that are defined in my application. The Vue and React app have some hints with classes and ids, but effectively they are just using <div>
s.
So what are those Elements?
They are clearly HTML elements. You can select them in the browser's console:
And then do the usual things - attach events, click and so on.
Are they Web Components?
Asks the savvy reader. Let's find out. The MDN Web docs explains Web Components:
Web Components is a suite of different technologies allowing you to create reusable custom elements — with their functionality encapsulated away from the rest of your code — and utilize them in your web apps.
The 3 aforementioned technologies are:
- Custom Elements
- Templates
- Shadow DOM
Let's check each one of them:
Custom Elements
Custom Elements are defined using the CustomElementRegistry.define()
. The define function also has a getter that is returning the constructor of the custom element by its name. So, if our component is a custom element querying the window.CustomElements.get
should return its constructor.
Templates
If templates are defined, they should exist as a tag inside the DOM. They will not be rendered, but they still exist. So all we need to do is search for the <template>
tag on our HTML source.
Shadow DOM
Elements that have a shadow DOM have the shadowRoot attribute set. In Chrome this attributes is visible on the DOM. It is not searchable, but visible on the DOM.
[Another method to identify shadow DOM is to try and select elements that are located inside the shadow DOM. Those elements are not available if you try and run `$('name-of-element')]
If we look at this example from MDN and check those criteria:
Running the same tests on our Angular app will result in failure in all three tests.
So - they are not web components, and nor just custom elements.
HTML Elements
Turns out that the answer is simple - they are just plain HTML Elements. Apparently, you can use any name as to attach elements to the DOM, not just the tags you are familiar with such as Div, P or H1.
So how does a <foo></foo>
differs from <div></div>
and <input/>
?
HTML elements (or tags) are split into categories. This link shows the different categories. (on a personal note I must say that this seems like a specification that is dragging a lot of legacies that no one was willing to touch).
A slightly more clear definition is the interface the elements implement.
All HTML elements conform to the HTMLElement interface. This is the div
and foo
elements. This interface contains properties such as innerText
or events like click
. (For the sake of accuracy, the click event is implemented on Element, as it is applicable to other elements such as SVGElement). The input tag, on the other hand, is implemented using the HTMLInputElement interface that extends the standard for the specific functionality an Input element has.
What is it good for?
I could not really find any reference to why this design decision made by the Angular team. But if you think about it, Angular is Google who is the promotors of Web Components (yes, Polymer...). Also, with Angular Elements and the ability to add shadow DOM to an Angular component, this decision is pretty straightforward. If you have an inside view on the Angular decision, feel free to share.
I found that having the component as an element on the DOM is extremely useful in at least three cases:
- Debug: just viewing the element on the DOM, on any browser, without the need to add an extension, can really reduce the debug time.
- Testing: When you write tests to a component, especially E2E tests, finding an element on the page by its tag name is probably the easiest thing to do.
- Styling: This is a topic for a whole other discussion, but it is quite easy to style
my-component .container
without the need to create special hashes for each component.
Conclusion
The differences between frameworks are sometimes deeper than we think and much less discussed. I was curious to understand how exactly the elements in Angular are built. Researching for this article has led me to some areas less known to humankind. The HTML elements categories section is just one of them.
If you found any mistakes that I made or have some additional light to shed on this, do not hesitate to leave a comment.
If you learned something new, it is great to also leave a comment, or just like it.
Posted on October 19, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.