Writing HTML in JavaScript is a pain. In older applications a developer may use .innerHTML throughout the application to update the content of a webpage.
Here are some bad examples:
// Using jQuery:
$('<div>Hello World</div>').appendTo('body')
// Using JavaScript
document.body.innerHTML = '<div>Hello World</div>'This is really bad, because doing so forces the browser to destroy the previous content and build from a string.
The web gurus around the world thought there must be a better way. That is why they have created a spec, piloted by Facebook.
This defines a way to write html-like structures in JavaScript, that are transformed to functions at compile time.
e.g.:
function List({items}) {
return (
<ul>
{items.map(item=><li>{item}</li>)}
</li>
)
}
const ulElement = List({
items: ['item1','item2','item3']
})
// ulElement now represents:
// <ul>
// <li>item1</li>
// <li>item1</li>
// <li>item1</li>
// </ul>This has a number of benefits. Under the hood, this JSX structure:
/** @jsx domify */
<div>Hello World</div>Compiles to:
domify(
"div",
null,
"Hello World"
)JSX is just function calls. You don't need to worry about parsing JSX. There are tools that will do that for you.
Your task is to write the domify function. Here is an example implementation:
function domify(nodeName, attributes={}, ...children){
return { nodeName, attributes, children }
}This implementation simply serializes the elements to a JavaScript object. You need to write one that actually creates the DOM structure. Do not use .innerHTML.
The domify function must return a real DOM element, not a string. The example above is only a stub.
Use This as a template and click fork to start.
- Start by creating an
ElementofnodeTypenodeNameusingdocument.createElement() - Add attributes using
.setAttributes() - Add children using
.appendChild() - Return the
Element - Add the element to the DOM with
document.body.appendChild(element) - Fix for edge cases like
string/numberandarray
NodeName:
The nodeName will often be a string that you can pass to document.createElement(nodeName) to get an initial node. See MDN
Attributes
The attributes will be null or an object of key-values To start, you can iterate through attributes with a for in loop if attributes is an object. You can add these attributes with Element.setAttribute(key, value). See MDN
Children
The children may be null or an array of other children. Each item in the array may be one of three types: string/number, array, or Node/object.
- The
string/numberrepresents text as a child of an element. These you can transform to aNode/objectwithdocument.createTextNode()See MDN - The
arrayrepresents a rendered list of elements that need to be added to the element. These elements may bestring/numberorNode/objecttypes. The array happens when you add an array as a child of an element. This is most common witharray.map()See MDN - The
Noderepresents something that you can give directly toNode.appendChildlike another Dom Element or similar. See MDN
- Functions can be bound as well in JSX by passing a function as an attribute. e.g.:
<div onClick={()=>alert('Hello World')}>Click Me!</div>When adding attributes, check to see if you are given a function and bind the appropriate event with addEventListener or Element.on{Event}.
- Sometimes
nodeNamewill be an identifier. In other words, it will call yourdomifymethod andnodeNamewill be afunctionand not astring. In JSX, you can define extra components by using an element starting with a capital. e.g.
<MyComponent></MyComponent>This will call domify(MyComponent, null, null). All you need to do if nodeName is a function is:
return nodeName({...attributes, children})which will call nodeName with the same attributes, but with children also available to it. That way other components can do something like:
function MyComponent({children}) {
return (
<div class="my-components-class">{children}</div>
)
}