Skip to content

Instantly share code, notes, and snippets.

@Synvox
Last active March 24, 2017 18:02
Show Gist options
  • Save Synvox/f142dcdcfda7add214158f37231ca659 to your computer and use it in GitHub Desktop.
Save Synvox/f142dcdcfda7add214158f37231ca659 to your computer and use it in GitHub Desktop.
JSX Renderer

Build a JSX Renderer

Background

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.

Objective:

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 template to get started:

Use This as a template and click fork to start.

Getting Started

  1. Start by creating an Element of nodeType nodeName using document.createElement()
  2. Add attributes using .setAttributes()
  3. Add children using .appendChild()
  4. Return the Element
  5. Add the element to the DOM with document.body.appendChild(element)
  6. Fix for edge cases like string/number and array

Hints and Tips:

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/number represents text as a child of an element. These you can transform to a Node/object with document.createTextNode() See MDN
  • The array represents a rendered list of elements that need to be added to the element. These elements may be string/number or Node/object types. The array happens when you add an array as a child of an element. This is most common with array.map() See MDN
  • The Node represents something that you can give directly to Node.appendChild like another Dom Element or similar. See MDN

Extra Credit

  1. 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}.

  1. Sometimes nodeName will be an identifier. In other words, it will call your domify method and nodeName will be a function and not a string. 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>
  )
}

See Also:

JSX | XML-like syntax extension to ECMAScript

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment