Displaying Text ?

Now that we can actually add styles, the next step is to add some text to display.
In the native DOM a textNode is a different type than a HTMLElement,
hence the different methods to create each. In our vDOM we follow suit.

A vText node is formally defined as: type VText = string || number. So, it’s
just a string or number :smile:

Refactor the our code

Currently our mountVElement is doing everything related to mounting. This made
sense because we were only mounting vElements . But there is a new player in town,
the vText yo, and that will force us to rethink our code.

We now have two players, vElement and vText. We want to mount the vText
and give it it’s own dedicated function, mountVText. The function mountVElement
is currently doing work which we can extract into a new function. We call this function mount.
The purpose of the mount function is to choose the approporiate function to
call when we’re recursing. This is easily shown in our new code:

  1. function mount(input, parentDOMNode) {
  2. //Hmmm lets see what input is.
  3. if (typeof input === 'string' || typeof input === 'number') {
  4. //we have a vText
  5. mountVText(input, parentDOMNode);
  6. } else {
  7. //we have a vElement
  8. mountVElement(input, parentDOMNode)
  9. }
  10. }
  11. function mountVText(vText, parentDOMNode) {
  12. // Oeeh we received a vText with it's associated parentDOMNode.
  13. // we can set it's textContent to the vText value.
  14. // https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent
  15. parentDOMNode.textContent = vText;
  16. }
  17. function mountVElement(vElement, parentDOMNode) {
  18. const { className, tag, props, style } = vElement;
  19. const domNode = document.createElement(tag);
  20. vElement.dom = domNode;
  21. if (props.children) {
  22. // Oeh, we have children. Pass it back to our mount
  23. // function and let it determine what type it is.
  24. props.children.forEach(child => mount(child, domNode));
  25. }
  26. if (className !== undefined) {
  27. domNode.className = className;
  28. }
  29. if (style !== undefined) {
  30. Object.keys(style).forEach(sKey => domNode.style[sKey] = style[sKey]);
  31. }
  32. parentDOMNode.appendChild(domNode);
  33. return domNode;
  34. }

Awesome everything is in place.
Let’s redefine anew app where we can test our new implementations:

  1. index.js
  2. ...
  3. const root = document.body;
  4. const myApp = createVElement('div', {
  5. style: { height: '100px', background: 'red'},
  6. className: 'my-class' },
  7. [ createVElement('h1', { className:'my-header' }, ['Hello!']),
  8. createVElement('div', { className:'my-container' }, [
  9. createVElement('p', {}, ['A container with some nice paragraph'])])
  10. ]
  11. );
  12. mount(myApp, root);

Awesome, we can read stuff now!

You can see that we didn’t define a createVText function.
We could do it, but this would just return it’s value (a string or a number).

Also, it might look weird that we only need to change the textContent of it’s
parentDOMNode, but again this is recursion at work. The function will receive an h1 HTMLElement
and add text to it, first we only have <h1></h1>, then in the next iteration: <h1>My Text</h1>.

Very cool! We now have a minimal vDOM implementation! We can add styles, classNames and text.
However, it is static. How would we be able to add state and add some dynamics??

Let’s find out!