Skip to content

diva-e/dom-to-react

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

38 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Dom to React

dom-to-react is a replacement for React's own method dangerouslySetInnerHTML.

It lets you build a React structure from a regular HTML-Dom. Your React application is aware of all elements wthin the added DOM, and it even lets you initialize your own React components anywhere inside that structure.

dom-to-react is very lightweight. It does not use any further third party modules.

How to install

Use npm (or your preferred package manager)

npm install --save-dev dom-to-react

Simple usage

It takes a regular Dom-Node as entrypoint from which it creates the according React-elements (by calling React.createElement()) recursively. So the simplest way is:

<main id="app">
  <div class="content-to-render">
    <p>A Paragraph</p>
  </div>
</main>
import Dom2react from 'dom-to-react';
import React from 'react';
import { render } from 'react-dom';

const d2r = new Dom2react();
const rootNode = document.querySelector('.content-to-render');

render(d2r.prepareNode(rootNode), document.querySelector('#app'));

This will create the initial DOM structure, but now all elements are React elements. Easy, isn't it?

Advanced usage

when creating an instance of Dom2React, a configuration can be provided which allows to manipulate and handle the original DOM:

const d2r = new Dom2react(options);

where options is an array with instruction objects with each 2-3 callback functions all the functions are being passed the following params:

  • @param {node} the node being tested/manipulated
  • @param {key} the React-key which would be assigned when the node renders (always in the format ${level}-${index})
  • @param {level} the level how deep in the DOM the nod is nested (an integer)
  • @param {parser} the parser itself
var options = [
  {
	// If this function returns true, the two following functions are called as long as they are defined
	// This function must always return true or false
	'condition': function(node, key) { return node.nodeName.toLowerCase() === 'div'; },

	// This function can be used for easy manipulations to the node, e.g. for removing or adding attributes
	// This function must always return a DOM-Node (even if it's a new one created by document.createElement)
	'modify':  function(node, key, level) { return document.createElement('div'); },

	//This function is used to inject your own components into the parsed DOM
	// This function must return an object which can be rendered by React (a react element or null)
	'action':  function(node, key, level) { return React.createElement('div'); }
  }
];

Example instructions

Add a class to all divs:

{
  condition: function(node, key, level, parser) { return node.nodeName.toLowerCase() === 'div';} ),
  modify: function(node, key, level) {
    node.className += ' a-class-added';
    return node;
  }
}

Remove all divs with a certain class:

{
  condition: function(node, key, level parser) { return node.className.indexOf('delete-me') >= 0;} ),
  action: function(node, key, level, parser) {
    return null;
  }
}

Initialize a react component for all nodes of a certain type (e.g. the react-markdown-component):

{
  condition: function(node, key, level, parser) return {node.nodeName.toLowerCase() === 'pre'},
  action: function(node, key, level, parser) {
    return <ReactMarkdown key={key} source={node.innerText} />;
  }
}

transform one node-type into another (e.g. ul=>ol) but preserve all childnodes:

{
  condition: function(node, key, level parser) { return node.nodeName.toLowerCase() === 'ul'},
  modify: function(node, key, level, parser) {
    var ol = document.createElement('ol');
    for (var i = node.childNodes.length - 1; i >= 0; i--) {
      ol.appendChild(node.childNodes[i]);
    }
    return ol;
  }
}

Initialize a more complex component with an object parsed from a JSON within a HTML comment. (That's actually what I used this for đź‘Ť)

<div class="complex-component">
<!-- { ...complex JSON-object } -->
</div>
{
  condition: function(node, key, level, parser) { return node.className.indexOf('complex-component') >= 0;},
  action: function(node, key, level, parser) {
    var props = false;
    for (var i = node.childNodes.length - 1; i >= 0; i--) {
      if (childNode.nodeType === 8) {
      	props = JSON.parse(childNode.nodeValue);
      }
    }
    return <ComplexComponent {...props} />;
  }
}

Example: also use the style attribute of a node.

dom-to-react is not meant to directly load all instructions from a node's style-attribute.
But as suggested here, style instructions can be added if required as follows:

<div class="with-style" style="background-color: rebeccapurple; color: lime; border-radius: 7px; padding: 12px;">
  This Element has a <code>style</code> attribute.<br>
  It also shows how to use the <code>parser</code>-argument
</div>
{
  action: function(node, key, level, parser) {
    // example how to adapt styling instructions from a node
    const hyphen2CamelCase = (str) =>  str.replace(/-([a-z])/gi,(s, group) =>  group.toUpperCase());
    const style2object = (styleString) => styleString.split(';').filter(s => s.length).reduce((styles, statement) => {
      const keyValue = statement.split(':');
      styles[hyphen2CamelCase(keyValue[0]).trim()] = keyValue[1].trim();
      return styles;
    }, {});
    return <div key={key} style={style2object(node.getAttribute('style'))}>{
      // the parser can be called again to handle "regular" child-nodes within a component
      parser.prepareChildren(node.childNodes, level)
    }</div>;
}

Demo

To see the included demo in action, clone/download this repo and

npm i
npm run start

and open

http://localhost:8080/

About

A versatile replacement for 'dangerouslySetInnerHTML'. Let's you build react-components deep within a regular HTML-Dom

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published