パッケージの詳細

html-to-react

aknuds11.7mMIT1.7.0

A lightweight library that converts raw HTML to a React DOM structure.

react, react-component, html

readme

html-to-react

Greenkeeper badge Build Status npm version Dependency Status Coverage Status npm

A lightweight library that converts raw HTML to a React DOM structure.

Why?

I had a scenario where an HTML template was generated by a different team, yet I wanted to leverage React for the parts I did have control over. The template basically contains something like:

<div class="row">
  <div class="col-sm-6">
    <div data-report-id="report-1">
      <!-- A React component for report-1 -->
    </div>
  </div>
  <div class="col-sm-6">
    <div data-report-id="report-2">
      <!-- A React component for report-2 -->
    </div>
  </div>
</div>

I had to replace each <div> that contains a data-report-id attribute with an actual report, which was nothing more than a React component.

Simply replacing the <div> elements with a React component would end up with multiple top-level React components that have no common parent.

The html-to-react module solves this problem by parsing each DOM element and converting it to a React tree with one single parent.

Installation

$ npm install --save html-to-react

Examples

Simple

The following example parses each node and its attributes and returns a tree of React elements.

const ReactDOMServer = require('react-dom/server');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlInput = '<div><h1>Title</h1><p>A paragraph</p></div>';
const htmlToReactParser = new HtmlToReactParser();
const reactElement = htmlToReactParser.parse(htmlInput);
const reactHtml = ReactDOMServer.renderToStaticMarkup(reactElement);

assert.equal(reactHtml, htmlInput); // true

With Custom Processing Instructions

If certain DOM nodes require specific processing, for example if you want to capitalize each <h1> tag, the following example demonstrates this:

const ReactDOMServer = require('react-dom/server');
const HtmlToReact = require('html-to-react');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlInput = '<div><h1>Title</h1><p>Paragraph</p><h1>Another title</h1></div>';
const htmlExpected = '<div><h1>TITLE</h1><p>Paragraph</p><h1>ANOTHER TITLE</h1></div>';

const isValidNode = function () {
  return true;
};

// Order matters. Instructions are processed in the order they're defined
const processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions();
const processingInstructions = [
  {
    // Custom <h1> processing
    shouldProcessNode: function (node) {
      return node.parent && node.parent.name && node.parent.name === 'h1';
    },
    processNode: function (node, children) {
      return node.data.toUpperCase();
    }
  },
  {
    // Anything else
    shouldProcessNode: function (node) {
      return true;
    },
    processNode: processNodeDefinitions.processDefaultNode
  }
];
const htmlToReactParser = new HtmlToReactParser();
const reactComponent = htmlToReactParser.parseWithInstructions(htmlInput, isValidNode,
  processingInstructions);
const reactHtml = ReactDOMServer.renderToStaticMarkup(reactComponent);
assert.equal(reactHtml, htmlExpected);

Replace the Children of an Element

There may be a situation where you want to replace the children of an element with a React component. This is beneficial if you want to:

  • a) Preserve the containing element
  • b) Not rely on any child node to insert your React component

Example

Below is a simple template that could get loaded via ajax into your application

Before
<div class="row">
  <div class="col-sm-6">
    <div data-container="wysiwyg">
      <h1>Sample Heading</h1>
      <p>Sample Text</p>
    </div>
  </div>
</div>
After

You may want to extract the inner html from the data-container attribute, store it and then pass it as a prop to your injected RichTextEditor.

<div class="row">
  <div class="col-sm-6">
    <div data-container="wysiwyg">
      <RichTextEditor html={"<h1>Sample heading</h1><p>Sample Text</p>"} />
    </div>
  </div>
</div>

Setup

In your instructions object, you must specify replaceChildren: true.

const React = require('react');
const HtmlToReact = require('html-to-react');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlToReactParser = new HtmlToReactParser();
const htmlInput = '<div><div data-test="foo"><p>Text</p><p>Text</p></div></div>';
const htmlExpected = '<div><div data-test="foo"><h1>Heading</h1></div></div>';

const isValidNode = function () {
  return true;
};

const processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions();

// Order matters. Instructions are processed in
// the order they're defined
const processingInstructions = [
  {
    // This is REQUIRED, it tells the parser
    // that we want to insert our React
    // component as a child
    replaceChildren: true,
    shouldProcessNode: function (node) {
      return node.attribs && node.attribs['data-test'] === 'foo';
    },
    processNode: function (node, children, index) {
      return React.createElement('h1', {key: index,}, 'Heading');
    }
  },
  {
    // Anything else
    shouldProcessNode: function (node) {
      return true;
    },
    processNode: processNodeDefinitions.processDefaultNode,
  },
];

const reactComponent = htmlToReactParser.parseWithInstructions(
  htmlInput, isValidNode, processingInstructions);
const reactHtml = ReactDOMServer.renderToStaticMarkup(
  reactComponent);
assert.equal(reactHtml, htmlExpected);

With Preprocessing Instructions

There may be situations where you want to preprocess nodes before rendering them, analogously to the custom processing instructions functionality. The rationale for supporting preprocessing hooks is generally that you might want to apply more general processing to nodes, before applying custom processing hooks to filtered sets of nodes. You could accomplish the same by calling common functions from your custom processing hooks, but the preprocessing hooks API makes it more convenient.

Example

Below is a simple template in which you may want to replace div IDs, via a preprocessing hook:

<div class="row">
  <div id="first" data-process="shared">
    <p>Sample For First</p>
  </div>
  <div id="second" data-process="shared">
    <p>Sample For Second</p>
  </div>
</div>

We want to process the above HTML into the following:

<div class="row">
  <h1 id="preprocessed-first">First</h1>
  <h2 id="preprocessed-second">Second</h2>
</div>

We can accomplish that with the following script, using a combination of preprocessing and custom processing instructions:

const React = require('react');
const HtmlToReact = require('html-to-react');
const HtmlToReactParser = require('html-to-react').Parser;

const htmlToReactParser = new HtmlToReactParser();
const htmlInput = '<div class="row">' +
  '<div id="first" data-process="shared">' +
    '<p>Sample For First</p>' +
  '</div>' +
  '<div id="second" data-process="shared">' +
    '<p>Sample For Second</p>' +
  '</div>' +
'</div>';

const htmlExpected = '<div class="row">' +
  '<h1 id="preprocessed-first">First</h1>' +
  '<h2 id="preprocessed-second">Second</h2>' +
'</div>';

const isValidNode = function () {
  return true;
};

const preprocessingInstructions = [
  {
    shouldPreprocessNode: function (node) {
      return node.attribs && node.attribs['data-process'] === 'shared';
    },
    preprocessNode: function (node) {
      node.attribs = {id: `preprocessed-${node.attribs.id}`,};
    },
  }
];
const processNodeDefinitions = new HtmlToReact.ProcessNodeDefinitions();
const processingInstructions = [
  {
    shouldProcessNode: function (node) {
      return node.attribs && node.attribs.id === 'preprocessed-first';
    },
    processNode: function(node, children, index) {
      return React.createElement('h1', {key: index, id: node.attribs.id,}, 'First');
    },
  },
  {
    shouldProcessNode: function (node) {
      return node.attribs && node.attribs.id === 'preprocessed-second';
    },
    processNode: function (node, children, index) {
      return React.createElement('h2', {key: index, id: node.attribs.id,}, 'Second');
    },
  },
  {
    shouldProcessNode: function (node) {
      return true;
    },
    processNode: processNodeDefinitions.processDefaultNode,
  },
];

const reactComponent = parser.parseWithInstructions(htmlInput, isValidNode, processingInstructions,
  preprocessingInstructions);
const reactHtml = ReactDOMServer.renderToStaticMarkup(reactComponent);
assert.equal(reactHtml, htmlExpected);

Tests & Coverage

Test locally: $ npm test

Test with coverage and report coverage to Coveralls: $ npm run test-coverage

Test with coverage and open HTML report: $ npm run test-html-coverage

更新履歴

Change Log

Master/Unreleased

v1.7.0

  • Don't process attributes for custom elements 151 (bilobom)

v1.6.0

v1.5.1

v1.5.0

  • Drop dependency on Ramda

v1.4.8

v1.4.7

  • Make sure event handlers, f.ex. "onclick", are functions #126 (aknuds1)

v1.4.6

  • Handle boolean attributes differing from React's representation #132 (aknuds1)

v1.4.5

v1.4.4

  • Upgrade htmlparser2 to v5

v1.4.3

  • Handle non-boolean empty element attributes

v1.4.2

  • Support onclick attribute of HTML elements

v1.4.1

v1.4.0

v1.3.4

  • Use lodash.camelcase instead of underscore.string.fp #76 (codepunkt)

v1.3.3

  • Handle free-standing text nodes in HTML

Full Changelog

v1.3.2

  • Handle directives in HTML

Full Changelog

v1.3.1

  • Handle invalid inline styles

Full Changelog

Merged pull requests:

v1.3.0

  • Upgrade to React 16

Full Changelog

v1.2.12

  • Trim inline style props values

Full Changelog

Merged pull requests:

v1.2.11

  • Handle base64 inlined images

Full Changelog

Merged pull requests:

v1.2.10

  • Handle background-image styles

Full Changelog

Merged pull requests:

v1.2.9

  • Handle for attribute

Full Changelog

v1.2.8

  • Handle xmlns:xlink

Full Changelog

Merged pull requests:

v1.2.7

  • Depend directly on domhandler, for compatibility with older Node.js

Full Changelog

v1.2.6

  • Make library smaller

Full Changelog

v1.2.5

Full Changelog

Merged pull requests:

  • Test of inline style with characters entities. #21 (Gycianka)

v1.2.4

Full Changelog

Merged pull requests:

  • parsing multiple root elements #17 (oroce)

v1.2.3

Full Changelog

Merged pull requests:

  • Occurrence of ampersand in attributes are decoded to & to avoid ano… #15 (gerhardsletten)

v1.2.2

Full Changelog

Merged pull requests:

  • handle xmlns attributes (e.g. xlink:href) #9 (gfx)

v1.2.1

Full Changelog

v1.2.0

Full Changelog

Merged pull requests:

v1.1.2

Full Changelog

v1.1.1

Full Changelog

v1.1.0

Full Changelog

v1.0.0

Full Changelog

Fixed bugs:

  • Travis build fails due to a ReferenceError #11

Closed issues:

  • Should not insert spans into tables even if there is white space #30
  • Img, br and hr tags produce warnings #29
  • using import instead of require #26
  • Cherry pick lodash #22
  • problem with textarea #9
  • Warning: Each child in an array or iterator should have a unique "key" prop. Check the render method #7
  • State of the library? #6
  • best way to pass callbacks? #5

Merged pull requests:

v0.1.0 (2015-06-20)

Full Changelog

v0.0.6 (2015-06-20)

Full Changelog

v0.0.5 (2015-06-20)

Full Changelog

v0.0.4 (2015-06-20)

Full Changelog

v0.0.3 (2015-06-20)

Full Changelog

v0.0.2 (2015-06-20)

* This Change Log was automatically generated by github_changelog_generator