Giter Site home page Giter Site logo

remarkablemark / html-react-parser Goto Github PK

View Code? Open in Web Editor NEW
2.0K 7.0 125.0 3.9 MB

📝 HTML to React parser.

Home Page: https://b.remarkabl.org/html-react-parser

License: MIT License

JavaScript 7.82% TypeScript 92.18%
html-react-parser react-parser react html dom parse parser javascript jsx typescript

html-react-parser's Introduction

html-react-parser

NPM

NPM version Bundlephobia minified + gzip Build Status codecov NPM downloads Discord

HTML to React parser that works on both the server (Node.js) and the client (browser):

HTMLReactParser(string[, options])

The parser converts an HTML string to one or more React elements.

To replace an element with another element, check out the replace option.

Example

import parse from 'html-react-parser';

parse('<p>Hello, World!</p>'); // React.createElement('p', {}, 'Hello, World!')

Replit | JSFiddle | StackBlitz | TypeScript | Examples

Table of Contents

Install

NPM:

npm install html-react-parser --save

Yarn:

yarn add html-react-parser

CDN:

<!-- HTMLReactParser depends on React -->
<script src="https://unpkg.com/react@18/umd/react.production.min.js"></script>
<script src="https://unpkg.com/html-react-parser@latest/dist/html-react-parser.min.js"></script>
<script>
  window.HTMLReactParser(/* string */);
</script>

Usage

Import ES module:

import parse from 'html-react-parser';

Or require CommonJS module:

const parse = require('html-react-parser').default;

Parse single element:

parse('<h1>single</h1>');

Parse multiple elements:

parse('<li>Item 1</li><li>Item 2</li>');

Make sure to render parsed adjacent elements under a parent element:

<ul>
  {parse(`
    <li>Item 1</li>
    <li>Item 2</li>
  `)}
</ul>

Parse nested elements:

parse('<body><p>Lorem ipsum</p></body>');

Parse element with attributes:

parse(
  '<hr id="foo" class="bar" data-attr="baz" custom="qux" style="top:42px;">',
);

replace

The replace option allows you to replace an element with another element.

The replace callback's first argument is domhandler's node:

parse('<br>', {
  replace(domNode) {
    console.dir(domNode, { depth: null });
  },
});
Console output

Element {
  type: 'tag',
  parent: null,
  prev: null,
  next: null,
  startIndex: null,
  endIndex: null,
  children: [],
  name: 'br',
  attribs: {}
}

The element is replaced if a valid React element is returned:

parse('<p id="replace">text</p>', {
  replace(domNode) {
    if (domNode.attribs && domNode.attribs.id === 'replace') {
      return <span>replaced</span>;
    }
  },
});

The second argument is the index:

parse('<br>', {
  replace(domNode, index) {
    console.assert(typeof index === 'number');
  },
});

Note

The index will restart at 0 when traversing the node's children so don't rely on index being a unique key. See #1259.

replace with TypeScript

For TypeScript, you'll need to check that domNode is an instance of domhandler's Element:

import { HTMLReactParserOptions, Element } from 'html-react-parser';

const options: HTMLReactParserOptions = {
  replace(domNode) {
    if (domNode instanceof Element && domNode.attribs) {
      // ...
    }
  },
};

Or use a type assertion:

import { HTMLReactParserOptions, Element } from 'html-react-parser';

const options: HTMLReactParserOptions = {
  replace(domNode) {
    if ((domNode as Element).attribs) {
      // ...
    }
  },
};

If you're having issues take a look at our Create React App example.

replace element and children

Replace the element and its children (see demo):

import parse, { domToReact } from 'html-react-parser';

const html = `
  <p id="main">
    <span class="prettify">
      keep me and make me pretty!
    </span>
  </p>
`;

const options = {
  replace({ attribs, children }) {
    if (!attribs) {
      return;
    }

    if (attribs.id === 'main') {
      return <h1 style={{ fontSize: 42 }}>{domToReact(children, options)}</h1>;
    }

    if (attribs.class === 'prettify') {
      return (
        <span style={{ color: 'hotpink' }}>
          {domToReact(children, options)}
        </span>
      );
    }
  },
};

parse(html, options);
HTML output

<h1 style="font-size:42px">
  <span style="color:hotpink">
    keep me and make me pretty!
  </span>
</h1>

replace element attributes

Convert DOM attributes to React props with attributesToProps:

import parse, { attributesToProps } from 'html-react-parser';

const html = `
  <main class="prettify" style="background: #fff; text-align: center;" />
`;

const options = {
  replace(domNode) {
    if (domNode.attribs && domNode.name === 'main') {
      const props = attributesToProps(domNode.attribs);
      return <div {...props} />;
    }
  },
};

parse(html, options);
HTML output

<div class="prettify" style="background:#fff;text-align:center"></div>

replace and remove element

Exclude an element from rendering by replacing it with <React.Fragment>:

parse('<p><br id="remove"></p>', {
  replace: ({ attribs }) => attribs?.id === 'remove' && <></>,
});
HTML output

<p></p>

transform

The transform option allows you to transform each element individually after it's parsed.

The transform callback's first argument is the React element:

parse('<br>', {
  transform(reactNode, domNode, index) {
    // this will wrap every element in a div
    return <div>{reactNode}</div>;
  },
});

library

The library option specifies the UI library. The default library is React.

To use Preact:

parse('<br>', {
  library: require('preact'),
});

Or a custom library:

parse('<br>', {
  library: {
    cloneElement: () => {
      /* ... */
    },
    createElement: () => {
      /* ... */
    },
    isValidElement: () => {
      /* ... */
    },
  },
});

htmlparser2

Warning

htmlparser2 options don't work on the client-side (browser) and they only work on the server-side (Node.js). By overriding the options, it can break universal rendering.

Default htmlparser2 options can be overridden in >=0.12.0.

To enable xmlMode:

parse('<p /><p />', {
  htmlparser2: {
    xmlMode: true,
  },
});

trim

By default, whitespace is preserved:

parse('<br>\n'); // [React.createElement('br'), '\n']

But certain elements like <table> will strip out invalid whitespace:

parse('<table>\n</table>'); // React.createElement('table')

To remove whitespace, enable the trim option:

parse('<br>\n', { trim: true }); // React.createElement('br')

However, intentional whitespace may be stripped out:

parse('<p> </p>', { trim: true }); // React.createElement('p')

Migration

v5

Migrated to TypeScript. CommonJS imports require the .default key:

const parse = require('html-react-parser').default;

If you're getting the error:

Argument of type 'ChildNode[]' is not assignable to parameter of type 'DOMNode[]'.

Then use type assertion:

domToReact(domNode.children as DOMNode[], options);

See #1126.

v4

htmlparser2 has been upgraded to v9.

v3

domhandler has been upgraded to v5 so some parser options like normalizeWhitespace have been removed.

Also, it's recommended to upgrade to the latest version of TypeScript.

v2

Since v2.0.0, Internet Explorer (IE) is no longer supported.

v1

TypeScript projects will need to update the types in v1.0.0.

For the replace option, you may need to do the following:

import { Element } from 'domhandler/lib/node';

parse('<br class="remove">', {
  replace(domNode) {
    if (domNode instanceof Element && domNode.attribs.class === 'remove') {
      return <></>;
    }
  },
});

Since v1.1.1, Internet Explorer 9 (IE9) is no longer supported.

FAQ

Is this XSS safe?

No, this library is not XSS (cross-site scripting) safe. See #94.

Does invalid HTML get sanitized?

No, this library does not sanitize HTML. See #124, #125, and #141.

Are <script> tags parsed?

Although <script> tags and their contents are rendered on the server-side, they're not evaluated on the client-side. See #98.

Attributes aren't getting called

The reason why your HTML attributes aren't getting called is because inline event handlers (e.g., onclick) are parsed as a string rather than a function. See #73.

Parser throws an error

If the parser throws an error, check if your arguments are valid. See "Does invalid HTML get sanitized?".

Is SSR supported?

Yes, server-side rendering on Node.js is supported by this library. See demo.

Elements aren't nested correctly

If your elements are nested incorrectly, check to make sure your HTML markup is valid. The HTML to DOM parsing will be affected if you're using self-closing syntax (/>) on non-void elements:

parse('<div /><div />'); // returns single element instead of array of elements

See #158.

Don't change case of tags

Tags are lowercased by default. To prevent that from happening, pass the htmlparser2 option:

const options = {
  htmlparser2: {
    lowerCaseTags: false,
  },
};
parse('<CustomElement>', options); // React.createElement('CustomElement')

Warning

By preserving case-sensitivity of the tags, you may get rendering warnings like:

Warning: <CustomElement> is using incorrect casing. Use PascalCase for React components, or lowercase for HTML elements.

See #62 and example.

TS Error: Property 'attribs' does not exist on type 'DOMNode'

The TypeScript error occurs because DOMNode needs to be an instance of domhandler's Element. See migration or #199.

Can I enable trim for certain elements?

Yes, you can enable or disable trim for certain elements using the replace option. See #205.

Webpack build warnings

If you see the Webpack build warning:

export 'default' (imported as 'parse') was not found in 'html-react-parser'

Then update your Webpack config to:

// webpack.config.js
module.exports = {
  // ...
  resolve: {
    mainFields: ['browser', 'main', 'module'],
  },
};

See #238 and #213.

TypeScript error

If you see the TypeScript error:

node_modules/htmlparser2/lib/index.d.ts:2:23 - error TS1005: ',' expected.

2 export { Parser, type ParserOptions };
                        ~~~~~~~~~~~~~

Then upgrade to the latest version of typescript. See #748.

Performance

Run benchmark:

npm run benchmark

Output of benchmark run on MacBook Pro 2021:

html-to-react - Single x 1,018,239 ops/sec ±0.43% (94 runs sampled)
html-to-react - Multiple x 380,037 ops/sec ±0.61% (97 runs sampled)
html-to-react - Complex x 35,091 ops/sec ±0.50% (96 runs sampled)

Run Size Limit:

npx size-limit

Contributors

Code Contributors

This project exists thanks to all the people who contribute. [Contribute].

Code Contributors

Financial Contributors

Become a financial contributor and help us sustain our community. [Contribute]

Individuals

Financial Contributors - Individuals

Organizations

Support this project with your organization. Your logo will show up here with a link to your website. [Contribute]

Financial Contributors - Organization 0 Financial Contributors - Organization 1 Financial Contributors - Organization 2 Financial Contributors - Organization 3 Financial Contributors - Organization 4 Financial Contributors - Organization 5 Financial Contributors - Organization 6 Financial Contributors - Organization 7 Financial Contributors - Organization 8 Financial Contributors - Organization 9

Support

License

MIT

html-react-parser's People

Contributors

adammockor avatar albertaz1992 avatar andrewleedham avatar bschlenk avatar dalenberg avatar dbvisel avatar dependabot[bot] avatar dogriffiths avatar dreierf avatar github-actions[bot] avatar hansottowirtz avatar heldergoncalves92 avatar jho406 avatar kfritsch avatar livdunn avatar mergify[bot] avatar monkeywithacupcake avatar mrlika avatar nadiaivaniuckovich avatar natterstefan avatar nikolay-borzov avatar oxozon4 avatar phawxby avatar remarkablemark avatar samikarak avatar ssi02014 avatar valin4tor avatar vidhu avatar yuliya-ivaniukovich avatar yunyu avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

html-react-parser's Issues

Unable to use html-react-parser in create-react-library

I was creating an npm module with the help create-react-library,
I which i was using html-react-parser.
but after importing html-react-parser, its giving error

[!] Error: Unexpected token
node_modules/entities/maps/entities.json (1:9)
1: {"Aacute":"\u00C1","aacute":"\u00E1","Abreve":"\u0102","abreve":"\u0103","ac":"\u223E","acd":"\u223F","acE":"\u223E\u0333","Acirc":"\u00C2","acirc":"\u00E2","acute":"\u00B4","Acy":"\u0410","acy":"\u0430","AElig":"\u00C6","aelig":"\ ....very long json 

error Command failed with exit code 1.

Is it compatible with htmlparser format?

Is it possible for us to call the following?

// htmlparser format, not htmlparser2
const htmlparserDoc = [ { type: 'tag'
  , name: 'a'
  , attribs: { href: 'test.html' }
  , children: [ { data: 'hello world', type: 'text' } ]
  }
];

domToReact(htmlparserDoc, options);

The htmlparser2 format contains circular references, which prevents us from calling JSON.stringify() on it, so we'd like to use htmlparser if we can.

Error management in non well formatted HTML strings

Hello,

When trying to render a non well formatted HTML string it breaks the entire execution of JS in the page. Could you implement an error handling directly or we should use React Error Boundaries feature ?

react-dom.development.js:11330 Uncaught DOMException: Failed to execute 'createElement' on 'Document': The tag name provided ('p<strong') is not a valid name. at createElement$1 (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:61750:34) at createInstance (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:63109:22) at completeWork (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:56249:32) at completeUnitOfWork (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:58550:20) at performUnitOfWork (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:58710:14) at workLoop (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:58722:26) at renderRoot (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:58753:9) at performWorkOnRoot (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:59328:24) at performWork (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:59249:9) at performSyncWork (http://www.travelcircus.sandbox/static/react/js/bundles/productpage.bundle.js?v=c43812121e594f158520698ba706118fdev:59226:5)

react-dom.development.js:9643 The above error occurred in one of your React components: in p<strong (created by HtmlParser) in div (created by HtmlParser) in HtmlParser (created by ProductDescription) in div (created by AccordionPane) in div (created by AccordionPane) in AccordionPane (created by Accordion) in div (created by Accordion) in Accordion (created by ProductDescription) in ProductDescription (created by Connect(ProductDescription)) in Connect(ProductDescription) (created by App) in div (created by App) in div (created by App) in IntlProvider (created by App) in App (created by Connect(App)) in Connect(App) in Provider

Doesn't work with web components with custom attributes

If I pass in an html string with attributes this doesn't recognize, they are not included in the props.

<my-custom-element label="mylabel" my-custom-prop="someValue"></my-custom-element>

This will only include label, but not my-custom-prop.

Tag attributes are getting converted to lower case

When parsing a dom element with mixed case attribute names, the resulting react module has both the component name and attribute names to lower case. I understand the component name getting set to lower case is mentioned in #62, but the attributes also getting set to lowercase seems like a bug due to this line: https://github.com/remarkablemark/html-react-parser/blob/97d6868/index.js#L5 which specifically specifies to not lowercase attribute names.

Is a bug in htmlparser2? or is there some issue with this module?

Export `domToReact` in public API

Right now, if I use the replace option to substitute certain DOM nodes with React elements, there's no good way to keep the children of that node (to pass as children to the React element) – because the React element will expect React element children, not the DOM node objects. So the current options are:

  • Turn the children back into HTML somehow and run them back through the parser in my replace function (wasteful).
  • Reach into html-react-parser/lib/dom-to-react module myself (undocumented, private API).

A nicer option would be to either export domToReact from the main entry point, or instead of throwing on non-string inputs, assume it is already a parsed DOM tree, so you can simply do parser(node.children).

Brackets "<,>" and Single Quotation Marks in attributes breaking either layout or attribute parsing

Expected Behavior

I have two buttons with onclick attributes as shown below.

<button onclick="(function(e){console.log(2 > 5)})();return">
<button onclick="(function(e){console.log('sometext')})();return">

The expected behavior is for

node.attribs.onclick = "(function(e){console.log(2 > 5)})();return"

or

node.attribs.onclick = "(function(e){console.log('sometext')})();return"

Actual Behavior

For the first button my layout breaks like

<button <!-- no attributes whatsoever -->>
<!-- whatever elements should be after this button -->
</button>

As for the second button, node.attribs.onclick becomes empty and a new attribute (function(e){console.log('sometext')})();return is added to node.attribs as shown below.

{
"(function(e){console.log('sometext')})();return": null
}

Steps to Reproduce

Just passing any html string with buttons or elements with attributes as described above to the parser function

Reproducible Demo

Unfortunately I can't create a demo at the moment, but I basically get a page generated by Wordpress, which I can confirm is not broken, by comparing the expected html with the code stored on a Redux state.

Don't change case of tags

I noticed that the parser changes everything to lower case, which is bad for me since I have some case sensitive tags.

Is there a way to prevent this?

Exclude nodes with replace

Are there any plans to be able to easily exclude nodes entirely with the replace option?

Perhaps by returning null?

how to use Parse in loop for replace the value with multiple?

just like as

("hello")

and I want to replace hello with match the tag id and found in an array and return and HTML. Is It Possible to Parser in loop?

here is my code

rowTextContent =

[quote]4becc77f37ba3[/quote]

Parser(rowTextContent, {
replace: (domNode) => {

                                    if (domNode) {

                                         var re = new RegExp("^_quote_([a-z0-9]+)_quote_$");
                                        var quoteData = domNode.childrenreplace('[quote]', '_quote_').replace('[/quote]', '_quote_');

                                        if (re.test(quoteData)) {

                                            var id = re.exec(quoteData)[1];
                                                                                   
                                            for (let i = 0; i <= this.state.rowData.narration.text.quotes.length; i++) {

                                                let text = this.state.rowData.narration.text.quotes[i];
                                                                  
                                                if (id === text.parent) {

                                                    return (

                                                        <div className="quote" id={text.slug} key={i}>
                                                            <div className="heading" onClick={this.props.showHideRow.bind(this, text.slug)}>
                                                                <img id="bullet105" src={`${mediaUrl}/interface/triangle0`}
                                                                    className={this.state.isShowRow && this.state.textSlug1 === text.slug ? "imgRadious" : "noimgRadious"} />
                                                                <span className="heading">{text.heading}</span>
                                                            </div>
                                                            {
                                                                <div className="textcontainer " style={{ display: 'block', overflow: 'hidden', height: 'auto' }}>
                                                                    <div className="text" duration="5">
                                                                        {textContent}
                                                                    </div>
                                                                </div>
                                                            }
                                                        </div>
                                                    )
                                                    
                                                }
                                            }
                                    
                                        }
                                    }
                                }
                            })

any suggestion guys?

Support for React 15.4.x?

It looks like this library has been broken by the move to React 15.4.x.

ERROR in ./~/html-react-parser/lib/attributes-to-props.js
Module not found: Error: Cannot resolve module 'react/lib/HTMLDOMPropertyConfig' in MYPATH/node_modules/html-react-parser/lib
 @ ./~/html-react-parser/lib/attributes-to-props.js 6:28-70

ERROR in ./~/html-react-parser/lib/property-config.js
Module not found: Error: Cannot resolve module 'react/lib/HTMLDOMPropertyConfig' in MYPATH/node_modules/html-react-parser/lib
 @ ./~/html-react-parser/lib/property-config.js 7:28-70

ERROR in ./~/html-react-parser/lib/property-config.js
Module not found: Error: Cannot resolve module 'react/lib/SVGDOMPropertyConfig' in MYPATH/node_modules/html-react-parser/lib
 @ ./~/html-react-parser/lib/property-config.js 8:27-68

Fix <script> and <style> parsing

Current behavior:

var Parser = require('html-react-parser');

Parser('<script>1 < 2;</script>'); // works
Parser('<script></script>'); // returns `[]`
Parser('<style></style>'); // does not work

The error stems from the flawed logic here. The if-check for node.children[0] needs to be separated and there needs to be a check for style tag as well.

React's Dangerously Set innerHTML needs to be used for setting style content (similar to script) to prevent CSS from being escaped.

var React = require('react');
var render = require('react-dom/server').renderToStaticMarkup;

render(React.createElement('style', {}, 'div > p { font-family: "Open Sans"; }'));
// '<style>div &gt; p { font-family: &quot;Open Sans&quot;; }</style>'

Parse text nodes

I have text nodes like <span style="background-color:rgb(187,255,187)">Hello World!</span> and I have tried like

var parsedElem=HTMLReactParser(node.innerHTML);
var reactElem=React.createElement(parsedElem, {className: "span"});
ReactDOM.render(reactElem, this.overlay);

but the parsed element is the contents (i.e. the text of the node) not the <span> itself.

Possible Img Require() problem?

I'm trying to narrow down why my images are breaking when parsed.

The HTML string to be parsed is this;
<img src={require("./img/articles/2019/1/react.png")}></img>

Which is then parsed into this;
<img src="{require("./img/articles/2019/1/react.png")}">
Resulting in a broken Image.

For testing I hardcoded an Image in below with this;
<img src={require("./img/articles/2019/1/react.png")}/>
And it loads just fine.

Any thoughts?

attribs

Hello, thanks for the great library!

I was wondering about your choice in abbreviating attributes as attribs. It's not standard and not easily recognizable (the only thing it reminds me of is ribs, which I love, but are unrelated).
Most of the project I've used, if abbreviating at all, which is becoming more rare these days, use attr and attrs.

Conventions are cool because they provide mental framework, so we don't spend extra attention and effort to reinvent the wheel, or recognize it as such

Thanks,
Wish you all the best

Keep attributes while replacing

My question has already been answered by Mark. Thanks again!
This issue is for documentation purposes.

Input: <XRef href="ID8000">ID 8000</XRef>
Expected Output: <a href="ID8000">ID 8000</a>

Solution:

const React = require('react');
const ReactDOMServer = require('react-dom/server');
const HTMLReactParser = require('html-react-parser');
const domToReact = require('html-react-parser/lib/dom-to-react');

const xml = '<XRef href="ID8000">ID 8000</XRef>';

const parserOptions = {
  replace: domNode => {
    if (domNode.name === 'xref') {
      return React.createElement(
        'a',
        domNode.attribs,
        domToReact(domNode.children, parserOptions)
      );
      // return (
      //   <a {...domNode.attribs}>
      //     {domToReact(domNode.children, parserOptions)}
      //   </a>
      // );
    }
  }
};

const reactElement = HTMLReactParser(xml, parserOptions);
console.log(ReactDOMServer.renderToStaticMarkup(reactElement));

https://repl.it/@remarkablemark/html-react-parser-issue-2018-08-03

Fails on createElements that doesn't exist

Hey @remarkablemark,

I'm working on a project where we can't control the user input and we rely on that to create some React elements, like bold/italic and this kind of stuff.

So, we are running into a problem when the input looks like: <c&/> or any non-valid html tag, but still looks like someone is trying to parse it.

Failed to execute 'createElement' on 'Document': The tag name provided ('c&') is not a valid name.

and it comes from ReactDOM, so I suspect that we are sending down to React non-valid html tags, for instance, containing &.

I created an example to explain it better: https://codesandbox.io/s/z21yv25y0p

Considering that html-react-parser uses html-dom-parser, I tried directly what's the actual parsing result from <c&/> and looks valid:

/*
  [Object]
    0: Object
    next: null
    prev: null
    parent: null
    name: "c&"
    attribs: Object
    type: "tag"
    children: Array[1]
      0: Object
      next: null
      prev: null
      parent: Object
      next: null
      prev: null
      parent: null
      name: "c&"
      attribs: Object
      type: "tag"
      children: Array[1]
        type: "text"
        data: " CodeSandBo0o0o0ox"
 */

Let me know if you need more info about this.
Thanks!

What is the correct way to pass props to a react component during the replace?

Input

<div class="gallery">
     <img src="1.jpg" />
     <img src="2.jpg" />
</div>

Output

 <Gallery images={images} />

Parser

const original = `
  <div class="gallery">
     <img src="1.jpg" />
     <img src="2.jpg"/>
  </div>
`;
const elements = parse(original, {
  replace: ({ attribs, children }) => {
    if (!attribs) return;
    if (attribs.class && attribs.class.includes("gallery")) {
      let photos = []
      children.forEach((img) => {
        if(img.name == 'img') {
          photos.push({
            src: img.attribs.src,
            width: 2,
            height: 2
          })
        }
      });
      return <Gallery photos={photos} />;
    }
  }
});

This is working fine , the Gallery component is being rendered , however the photos prop array is not being passed down to the component during render. What is the correct way of passing a prop?

The image will be download even without render it to the page when parsing images

Currently I am try to parse the html that contains img tag.

My template text:

    <div>
      <img src="imgUrl" alt="" />
    </div>

After parsed this template, the image will be download even without insert the parse result.

Because I am going to wrap all the images into a lazy load component, which works with your component.

The following could help to parse string to document, even the string contains image tag, the image won't be downloaded.

    const parser = new domparser();
    const doc = parser.parsefromstring(text, "text/html")

SVG paring fails with an embedded script tag

This is a simplified copy of my SVG. I've removed a lot of the text / use statements to keep it a reasonable length.

<?xml version="1.0" encoding="utf-8"?> <svg xmlns="http://www.w3.org/2000/svg" id="carriageLayout0065" contentScriptType="text/ecmascript" contentStyleType="text/css" width="915" height="270" zoomAndPan="magnify" preserveAspectRatio="xMidYMid meet" version="1.0"> <style><![CDATA[ text { font-family : Sans-Serif; font-size : 13px; } .outline { fill:#ECEFF1; stroke:black; } .seat { fill:#D84315; } .available { fill:#B0BEC5; } .priority.available { fill:#F8BBD0; } .companion.available { fill:#F8BBD0; } .wheelchair.available { fill:#F8BBD0; } .selected.available { fill:#8BC34A; } use.available { cursor : pointer; } text { pointer-events: none; } .table { fill:#37474F; } .luggage { fill:#BCAAA4; } #suitcase { fill : black; } .berth { stroke : black; fill : none; } .bed { stroke : black; } ]]></style> <defs> <g id="fseat"> <rect x="0" y="0" class="bg" style="stroke:none" width="35" height="37" rx="7" ry="7"/> <rect x="1" y="5" style="fill:black;stroke:black;opacity:0.35;" width="5" height="27" rx="2" ry="2"/> <rect x="8" y="1" style="fill:black;stroke:black;opacity:0.35;" width="20" height="5" rx="2" ry="2"/> <rect x="8" y="30" style="fill:black;stroke:black;opacity:0.35;" width="20" height="5" rx="2" ry="2"/> </g> <g id="bseat"> <rect x="0" y="0" class="bg" style="stroke:none" width="35" height="37" rx="7" ry="7"/> <rect x="30" y="5" style="fill:black;stroke:black;opacity:0.35;" width="5" height="27" rx="2" ry="2"/> <rect x="8" y="1" style="fill:black;stroke:black;opacity:0.35;" width="20" height="5" rx="2" ry="2"/> <rect x="8" y="30" style="fill:black;stroke:black;opacity:0.35;" width="20" height="5" rx="2" ry="2"/> </g> <g id="sfseat"> <rect x="0" y="0" class="bg" style="stroke:none" width="30" height="37" rx="7" ry="7"/> <rect x="1" y="5" style="fill:black;stroke:black;opacity:0.35;" width="5" height="27" rx="2" ry="2"/> <rect x="8" y="1" style="fill:black;stroke:black;opacity:0.35;" width="15" height="5" rx="2" ry="2"/> <rect x="8" y="30" style="fill:black;stroke:black;opacity:0.35;" width="15" height="5" rx="2" ry="2"/> </g> <g id="sbseat"> <rect x="0" y="0" class="bg" style="stroke:none" width="30" height="37" rx="7" ry="7"/> <rect x="25" y="5" style="fill:black;stroke:black;opacity:0.35;" width="5" height="27" rx="2" ry="2"/> <rect x="8" y="1" style="fill:black;stroke:black;opacity:0.35;" width="15" height="5" rx="2" ry="2"/> <rect x="8" y="30" style="fill:black;stroke:black;opacity:0.35;" width="15" height="5" rx="2" ry="2"/> </g> <g id="lfseat"> <rect x="0" y="0" class="bg" style="stroke:none" width="40" height="37" rx="7" ry="7"/> <rect x="1" y="5" style="fill:black;stroke:black;opacity:0.35;" width="5" height="27" rx="2" ry="2"/> <rect x="8" y="1" style="fill:black;stroke:black;opacity:0.35;" width="25" height="5" rx="2" ry="2"/> <rect x="8" y="30" style="fill:black;stroke:black;opacity:0.35;" width="25" height="5" rx="2" ry="2"/> </g> <g id="lbseat"> <rect x="0" y="0" class="bg" style="stroke:none" width="40" height="37" rx="7" ry="7"/> <rect x="35" y="5" style="fill:black;stroke:black;opacity:0.35;" width="5" height="27" rx="2" ry="2"/> <rect x="8" y="1" style="fill:black;stroke:black;opacity:0.35;" width="25" height="5" rx="2" ry="2"/> <rect x="8" y="30" style="fill:black;stroke:black;opacity:0.35;" width="25" height="5" rx="2" ry="2"/> </g> <g id="sberth"> <rect x="0" y="0" width="90" height="190" class="berth"/> </g> <g id="dberth"> <rect x="0" y="0" width="90" height="190" class="berth"/> </g> <g id="mbed"> <rect x="0" y="0" width="70" height="40" class="bed bg"/> </g> <g id="wchair" transform="scale(0.05)"> <rect width="570" height="720" x="20" y="0" rx="60" ry="60" stroke="none" class="bg"/> <path transform="translate(60,80)" fill="black" fill-rule="evenodd" clip-rule="evenodd" d="M161.9882813,98.1240234&#10; c24.9628906-2.3046875,44.3574219-23.8110352,44.3574219-48.9658203C206.3457031,22.0830078,184.2626953,0,157.1875,0&#10; s-49.1572266,22.0830078-49.1572266,49.1582031c0,8.2568359,2.3037109,16.7055664,6.1445313,23.8105469l17.515625,246.4667969&#10; l180.3964844,0.0488281l73.9912109,173.3652344l97.1445313-38.0976563l-15.0429688-35.8203125l-54.3662109,19.625&#10; l-71.5908203-165.2802734l-167.7294922,1.1269531l-2.3027344-31.2128906l121.4228516,0.0483398v-46.1831055l-126.0546875-0.0493164&#10; L161.9882813,98.1240234z"/> <path transform="translate(60,80)" fill="black" fill-rule="evenodd" clip-rule="evenodd" d="M343.4199219,451.5908203&#10; c-30.4472656,60.1875-94.1748047,99.8398438-162.1503906,99.8398438C81.4296875,551.4306641,0,470.0009766,0,370.1611328&#10; c0-70.1005859,42.4853516-135.2436523,105.8818359-164.1210938l4.1025391,53.5375977&#10; c-37.4970703,23.628418-60.6123047,66.262207-60.6123047,110.9506836c0,72.4267578,59.0712891,131.4970703,131.4970703,131.4970703&#10; c66.2617188,0,122.7646484-50.8515625,130.4697266-116.0869141L343.4199219,451.5908203z"/> </g> <g id="suitcase" transform="scale(0.1)"> <g transform="translate(30,-65)"> <rect x="48" y="32" width="144" height="160"/> <path style="fill:black" d="m 40,32 0,160 -16,0 a 24,24 0 0 1 -24,-24 l 0,-112 a 24,24 0 0 1 24,-24 z"/> <path d="m 200,32 16,0 a 24,24 0 0 1 24,24 l 0,112 a 24,24 0 0 1 -24,24 l -16,0 z"/> <path d="m 84,30 0,-12 a 18,18 0 0 1 18,-18 l 36,0 a 18,18 0 0 1 18,18 l 0,12 -12,0 0,-12 a 6,6 0 0 0 -6,-6 l -36,0 a 6,6 0 0 0 -6,6 l 0,12 z"/> </g> </g> </defs> <rect x="10" y="10" width="893" height="195" class="outline"/> <rect x="106" y="20" width="25" height="60" class="table"/> <rect x="106" y="20" width="25" height="60" class="table"/> <rect x="106" y="20" width="25" height="60" class="table"/> <use xmlns:xlink="http://www.w3.org/1999/xlink" id="PL-61A" x="40" y="14" xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace" xlink:href="#sbseat" class="seat"/> <use xmlns:xlink="http://www.w3.org/1999/xlink" id="PL-60A" x="40" y="52" xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace" xlink:href="#sbseat" class="seat"/> <use xmlns:xlink="http://www.w3.org/1999/xlink" id="PL-57A" x="73" y="14" xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace" xlink:href="#sfseat" class="seat"/> <use xmlns:xlink="http://www.w3.org/1999/xlink" id="PL-55A" x="134" y="14" xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace" xlink:href="#sbseat" class="seat"/> <use xmlns:xlink="http://www.w3.org/1999/xlink" id="PL-56A" x="73" y="52" xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace" xlink:href="#sfseat" class="seat"/> <use xmlns:xlink="http://www.w3.org/1999/xlink" id="PL-54A" x="134" y="52" xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace" xlink:href="#sbseat" class="seat"/> <use xmlns:xlink="http://www.w3.org/1999/xlink" id="PL-49A" x="196" y="14" xlink:type="simple" xlink:actuate="onRequest" xlink:show="replace" xlink:href="#sbseat" class="seat"/> <text x="44" y="37">61A</text> <text x="44" y="75">60A</text> <text x="77" y="37">57A</text> <text x="138" y="37">55A</text> <text x="77" y="75">56A</text> <text x="138" y="75">54A</text> <script><![CDATA[ var svgDoc = document.getElementById('carriageLayout0065'); var selector = '[id^=' + String.fromCharCode(039) + 'PL-' + String.fromCharCode(039) + ']'; var ids = svgDoc.querySelectorAll(selector); for (var x=0;x<ids.length;x++) { if (ids[x].classList.contains('available')) { ids[x].addEventListener('click',function(event){ if (top.toggleSeat(event)) { event.target.classList.toggle('selected'); } }) } } ]]></script> </svg>

This results in the following error:

SyntaxError: Unexpected end of input
appendChild
node_modules/react-dom/cjs/react-dom.development.js:8701
8698 | }
8699 |
8700 | function appendChild(parentInstance, child) {

8701 | parentInstance.appendChild(child);
8702 | }
8703 |
8704 | function appendChildToContainer(container, child) {
View compiled
commitPlacement
node_modules/react-dom/cjs/react-dom.development.js:15066
15063 | if (isContainer) {
15064 | appendChildToContainer(parent, node.stateNode);
15065 | } else {
15066 | appendChild(parent, node.stateNode);
15067 | }
15068 | }
15069 | } else if (node.tag === HostPortal) {
View compiled
commitAllHostEffects
node_modules/react-dom/cjs/react-dom.development.js:15822
15819 | switch (primaryEffectTag) {
15820 | case Placement:
15821 | {
15822 | commitPlacement(nextEffect);
15823 | // Clear the "placement" from effect tag so that we know that this is inserted, before
15824 | // any life-cycles like componentDidMount gets called.
15825 | // TODO: findDOMNode doesn't rely on this any more but isMounted
View compiled

If I remove the script then it works as expected.

{ParseHTML(svgString.replace(/</?script[^>]*/g,''))}

Access to parent node by ref inside parser function

Hello,
how to call inside the "replace" function refs? The following code returns nulls:

import React from "react";
import parse from "html-react-parser";
import domToReact from "html-react-parser/lib/dom-to-react"

export default class SinglePortfolioItem extends React.Component {
    constructor(props) {
        super(props);
        this.parentRef = React.createRef();
    }
    parserOptions = () => {return {
        replace: domNode => {
            const {attribs, children} = domNode;
            console.log(this.parentRef)
            if (!attribs) return;

            if(attribs.bgcolor) {
                return (
                    <table style={{backgroundColor: attribs.bgcolor}} className='wp-block-f97a-portfolio-container'>
                        {domToReact(children, this.parserOptions())}
                    </table>
                )
            }

            if(attribs.textcolor) {
                return <div className='f97a-portfolio-description' style={{color: attribs.textcolor}}>
                {domToReact(children, this.parserOptions())}
                </div>
            }

            if(attribs.class && attribs.class.indexOf('download_links') > -1) {
                if (attribs.shouldshow) {
                    return <a href={attribs.shouldshow} target="_blank"><img className={attribs.class} src={attribs.src} /></a>
                }
                return <>{domToReact(children, this.parserOptions())}</>;
            }
            if(domNode.name && domNode.name == 'span') return <React.Fragment />;
            if(attribs.class && attribs.class.indexOf('blocks-gallery-item') > -1) {
                return <li className="portfolioListItem blocks-gallery-item">{domToReact(children, this.parserOptions())}</li>
            }
        }
    } }
    render() {

        const Inners = (props) => { return parse(this.props.portf.content.rendered, this.parserOptions())}

        return(
            <div ref={this.parentRef} className='singlePortfolioElement'>
                <span className='portfolioTitle'>{this.props.portf.title.rendered}</span>
                <Inners prntRef={this.parentRef} />
            </div>
        )
    }
}

2019-02-19 17-13-33

Input default values

I've used this package as a peer dependency for react-libre-form, and we're currently using it in a "large scale" React application. Great work on it!

The issue is about react-libre-form, kinda. It's a React component for WP Libre Form, a form plugin for WordPress that let's you have 100% control of the markup.

So this is my problem. Say I get this chunk of HTML from the WP REST API:

<input type="text" name="name" id="fb-name" placeholder="Matti Meikäläinen" defaultValue="Oispa kaljaa">

In this case, defaultValue has been prefilled from the user details. But when I ran this through the parser, the input appears, but the default value is nowhere to be seen.

If I change defaultValue into value, it "works", except the usual React yadda yadda about not specifying an input handler.

<input type="text" name="name" id="fb-name" placeholder="Matti Meikäläinen" value="Oispa kaljaa">

The thing is, I'd rather have them as uncontrolled inputs, and don't want to use the value attribute. I'm guessing the defaultValue is a JSX thing and that's why it doesn't work.

But do I have any options, other than adding a input handler?

Can't resolve 'css/lib/parse' in ...style-to-object'

ERROR in ./node_modules/style-to-object/index.js
Module not found: Error: Can't resolve 'css/lib/parse' in '....\node_modules\style-to-object'
 @ ./node_modules/style-to-object/index.js 1:12-36
 @ ./node_modules/html-react-parser/lib/attributes-to-props.js
 @ ./node_modules/html-react-parser/lib/dom-to-react.js
 @ ./node_modules/html-react-parser/index.js
 @ ./src/js/common/components/ExpandableContent.jsx
 @ ./src/js/app/catalog/documentation/flex-dropdown/FlexDropdownUsage.jsx
 @ ./src/js/app/catalog/Controller.js
 @ ./src/js/app/catalog/Module.js
 @ ./src/js/app.js

Any ideas if this is webpack issue or style-to-object or html-react-parser ?

support custom style properties '--*' (see https://drafts.csswg.org/css-variables/ for more information)

Hello,

Our current application requires to use custom styles, so I would like to parse custom styles beginning with '--' without camelCasing the first character after the second dash, so that the parser is compliant to csswg specs for custom styles.
(info : it is also good to know, that react does only support custom styles beginning with '--
' )

I looked into the library files and found html-react-parser/lib/utilities.js which contains the function camelCase. This function is (obviously) responsible for mistakenly converting double dashed styles into camelCases. So for example '--customstyle' gets converted to '-Customstyle'.

besides checking if the string does not contain a dash, it could also be checked if the string is a custom style first.

  // customStyle or no hyphen found
  if (string.startsWith('--') || string.indexOf('-') === -1) {
    return string;
  }

this change would easily solve our problems

SSR - crashes with "navigator is not defined"

Hi,

in compiled "html-react-parser.js" is a function (isIE) which seems to be called only by importing your lib. On server side this function crashes my build due to unchecked existence of navigator.

"ReferenceError: navigator is not defined"

Deep parsing with replace, readme example doesn't work

This worked perfectly for the first level in my HTML, thank you! But children were not getting replaced. I even used your prettify example from the readme, and my output is:

<div>
    <span style="font-size: 42px;">
        <span class="prettify">
            keep me and make me pretty!
        </span>
    </span>
</div>

only getting the first replaced element, not both.

Am I misinterpreting your example, or is something broken?

Parsing Events / Event Listeners

How would you handle applying event listeners? My current HTML/JavaScript app is currently complemented with addEventListener()s, which I figured I would just refactor to be onclick, onchange, etc. However, neither onclick nor onClick seemed to work. I also tried with ='return myFunc(e)', ='myFunc(e)', and just ='myFunc'. I see the classNames changing/working (when I change them), but I can't seem to get the event added.

I'm thinking I will need to use the replace via the parser options and add them in there, but figured I'd check to see if that's the proper (if not only) approach.

Also, many thanks for this utility!

Allow replace to return an array or replacement nodes

We have some templates which have a placeholder <div id='foo'></div> where we want to inject the child nodes of a component.

      if (domNode.attribs && domNode.attribs.id === this.props.hook) {
        if (Array.isArray(this.props.children)) {
          return this.props.children; // Broken
        } else {
          return this.props.children; // Works fine
        }
      }

This works great if props.children is only a single react node, if it's an array of React nodes it breaks. I believe the problem to be here. A array is obviously not a valid element.

I believe we need something a bit more like.

        if (isReplacePresent) {
          replacements = options.replace(node);

          if (!Array.isArray(replacements)) {
            replacements = [replacements];
          }

          for (var j = 0, repLen = replacements.length; j < repLen; j++) {
            var replacement = replacements[j];

            if (React.isValidElement(replacement)) {
                // specify a "key" prop if element has siblings
                // https://fb.me/react-warning-keys
                if (len > 1 || repLen > 1) {
                    replacement = React.cloneElement(replacement, { key: i.toString() + '-' + j.toString() });
                }
                result.push(replacement);
            }
          }

          continue;
        }

Support for React 16.0.0

./~/html-react-parser/lib/property-config.js Module not found: Can't resolve 'react-dom/lib/SVGDOMPropertyConfig' in 'C:\Users\roweb\Documents\workspace\poc-digital-job-aids-ui\node_modules\html-react-parser\lib'

It looks like some of the SVGDOMPropertyConfig files are no longer available to import with the new version of react. Their suggestion is to copy and paste the code...

See: facebook/react#10391

I don't think we plan to expose them directly. Their format sometimes changes and we want to be able to make more under the hood changes to them in the future.

While it is a bit annoying I encourage you to just copy and paste them (and update if you notice changes on our side).

Error: img is a void element tag and must neither have `children` nor use `dangerouslySetInnerHTML`

Hi!

Great plugin, really think this is going to be helpful for what I'm working on.

I noticed that parsing any img-element causes the above error. This may also apply to any element that never should have child elements.

A temporary workaround is to use something like this:

const parserConfig = {
    replace: domNode => {
        if(domNode.type === DOM_TYPE_TAG && domNode.name === 'img') {
            return <img src={domNode.attribs.src} alt={domNode.attribs.alt} className={domNode.attribs.class} />;
        }
    }
};

Fix HTML to DOM client parser

DOMParser wraps certain elements within <html> and <body>, which breaks client-side parser.

Example:

new DOMParser().parseFromString('<div>', 'text/html');
// #document
//     <html>
//         <head></head>
//         <body>
//             <div></div>
//         </body>
//     </html>

Module not found: Error: Can't resolve 'react-dom/lib/HTMLDOMPropertyConfig'

It looks like the current version of this project is attempting to use a private React API that has been removed in recent versions of React. I'm getting the following error when trying to use this library within zeit next project while building for production(npm run build):

{ Error: ./node_modules/html-react-parser/lib/property-config.js
Module not found: Error: Can't resolve 'react-dom/lib/HTMLDOMPropertyConfig' in '<node_modules path>/html-react-parser/lib'
resolve 'react-dom/lib/HTMLDOMPropertyConfig' in '<node_modules path>/html-react-parser/lib'
  Parsed request is a module
  using description file: <node_modules path>/html-react-parser/package.json (relative path: ./lib)

Here is a tweet from Dan Abramov related to the issue.

Whitespace text nodes cannot appear as a child of <table>

When I want to create a React table from a multi line string I will get an error from React:

index.js:1452 Warning: validateDOMNesting(...): Whitespace text nodes cannot appear as a child of <table>. Make sure you don't have any extra whitespace between tags on each line of your source code.

Example code that will trigger the error

render() {
  const table = parser.parse(`
    <table>
      <tbody>
        <tr>
          <td>Lorem Ipsum</td>
        </tr>
      </tbody>
    </table>
  `);

  return (
    <React.Fragment>
      {table}
    </React.Fragment>
  );

Off course I can fix this error by writing the table on one line, but that is not the solution where I'm looking for.
const table = parser.parse('<table><tbody><tr><td>Lorem Ipsum</td></tr></tbody></table>');

With dangerouslySetInnerHTML I can use a multi line string without any problems. I also tried the package html-to-react but it will have the same problem.

Thanks for your time and help,
Ben

Write client-side tests

Test the following:

  • bundles correctly
  • works in the various UMD environments:
    • CommonJS
    • RequireJS (AMD)
    • <script> tag
  • client parser works as expected

Strangely adding a 141% padding-bottom to an inserted <div> upon rendering.

Hi there! Awesome module. Everything is working flawlessly, with the exception of one thing. I am parsing <iframe> with this. It suits my needs fine with a single exception thus far. When I parse an <iframe> from a Google Doc or something similar the module is automatically inserting a padding-bottom: 141.423% to the element in question. I can hack this with CSS by targeting my parent class of .message-unfurl div { } but that effects other things. I tried looking into my node_modules folder to see if i could just hardcode it in the return there, but did not see the code for that. Any help would be greatly appreciated. I have added an image below of the selected element. Thank you!

http://imgur.com/a/CaYFd

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.