6

Given the HTML as a string, the Xpath and offsets. I need to highlight the word.

In the below case I need to highlight Child 1

HTML text:

<html>
 <body>
       <h2>Children</h2>Joe has three kids:<br/>
       <ul>
        <li>
        <a href="#">Child 1 name</a>
        </li>
        <li>kid2</li>
        <li>kid3</li>
       </ul>
 </body>
</html>

XPATH as : /html/body/ul/li[1]/a[1]

Offsets: 0,7

Render - I am using react in my app. The below is what I have done so far.

public render(){
  let htmlText = //The string above
  let doc = new DOMParser().parseFromString(htmlRender,'text/html');
  let ele = doc.evaluate("/html/body/ul/li[1]/a[1]", doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null); //This gives the node itself
  let spanNode = document.createElement("span");
  spanNode.className = "highlight";

  spanNode.appendChild(ele);
  // Wrapping the above node in a span class will add the highlights to that div
  //At this point I don't know how to append this span to the HTML String
  return(
    <h5> Display html data </h5>
    <div dangerouslySetInnerHTML={{__html: htmlText}} />
   )

I want to avoid using jquery. Want to do in Javascript(React too) if possible!

Edit:

So if you notice the Render function it is using dangerouslySetHTML. My problem is I am not able manipulate that string which is rendered.

suprita shankar
  • 1,554
  • 2
  • 16
  • 47
  • It may be just me, but it's not very clear what exactly you need to accomplish? Do you need to simply modify the value of `htmlText` before you put it in `dangerouslySetInnerHtml`? – hazardous Mar 21 '17 at 08:15
  • Is this your actual code snippet or just an example? Lot of stuff is puzzling here, there is the `public` before render, then you are trying to nest `html` inside a `div`. Could you please share a more complete code? git repo would be preferable. – hazardous Mar 21 '17 at 08:17

2 Answers2

5

This is what I ended up doing.

public render(){
  let htmlText = //The string above
  let doc = new DOMParser().parseFromString(htmlRender,'text/html');
  let xpathNode = doc.evaluate("/html/body/ul/li[1]/a[1]", doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null); 
  const highlightedNode = xpathNode.singleNodeValue.innerText;
  const textValuePrev = highlightedNode.slice(0, char_start);
  const textValueAfter = highlightedNode.slice(char_end, highlightedNode.length);
  xpathNode.singleNodeValue.innerHTML = `${textValuePrev}
                                         <span class='pt-tag'>
                                         ${highlightedNode.slice(char_start, char_end)}
                                         </span> ${textValueAfter}`;
  return(
    <h5> Display html data </h5>
    <div dangerouslySetInnerHTML={{__html: doc.body.outerHTML}} />
   )
suprita shankar
  • 1,554
  • 2
  • 16
  • 47
3

Xpath is inherently cross component, and React components shouldn't know much about each other. Xpath also basically requires all of the DOM to be created in order to query it. I would render your component first, then simply mutate the rendered output in the DOM using the Xpath selector.

https://jsfiddle.net/69z2wepo/73860/

var HighlightXpath = React.createClass({
  componentDidMount() {
     let el = document.evaluate(this.props.xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
     el.singleNodeValue.style.background = 'pink';
  },
  render: function() {
    return this.props.children;
  }
});

Usage:

<HighlightXpath xpath="html//body//div/p/span">
    ... app ...
</HighlightXpath>
Andy Ray
  • 30,372
  • 14
  • 101
  • 138