0

I am using React and Javascript and have a requirement where I need to extract certain anchor tags, take their attributes like id, href and create a custom React link component out of it. My string looks like this "<p>Bla bla bla bla.</p>\n<ul>\n<li><strong>Bla bla</strong>. Bla bla bla.</li>\n<li><strong>Bla bla bla</strong>.<a href='#footnote1' id='sup1'><sup>1</sup></a> Bla bla bla.</li>\n<li><strong>Bla bla bla.<a href='#footnote2' id='sup2'><sup>2</sup></a>\r\n</strong>&#160; Bla bla bla.</li>\n<li><strong>Bla bla:</strong>Bla bla <a href=\"https://google.com\" target=\"no\">Google</a> * bla bla bla.<a href='#footnote1' id='sup3'><sup>1</sup></a></li>\n</ul>\n<p>&#160;</p>"

I want to only extract all those <a> that have <sup> inside them. I want to then take the attributes of such <a> and create a custom React link component like

<MyLink
      id={id of the anchor tag}
      addClasses={"footnote-link"}
      href={href of anchor tag}
      handleClick={myClickHandler}
      ariaProps={{
        label: {text inside the sup tag}
      }}
    >
      <sup className={href of anchor tag !== null ? "custom-class" : ""}>{text inside the sup tag}</sup>
    </MyLink>

I then want to replace those <a> with my custom component in the string. Reason I am trying to convert <a> to custom component is that I want the new anchor tag to be A11Y compliant and anchor tags and the source string are not in my control.

I tried the combination of How to get the href value of an anchor tag with javascript from a string and javascript regex to extract anchor text and URL from anchor tags, but couldn't make much progress. I also tried react-string-replace, but that didn't help much either.

I also have an option of getting <a href='#footnote1' id='sup1'><sup>1</sup></a> replaced with something like #FootNote({id:\"sup1-CB\", goto:\"sup1\", data:\"1\"})#FootNote and then use "id", "goto" and "data" to create the custom link.

Can anyone provide some guidance on how to achieve this? I apologize for the long question.

abhi
  • 349
  • 2
  • 8
  • 24
  • I ended up using this solution https://stackoverflow.com/a/38969310/6586554, which ultimately uses html-to-react custom library https://github.com/mikenikles/html-to-react. – abhi Mar 09 '21 at 18:21

2 Answers2

1

Here's an example that uses a created tag to parse your html, then use DOM methods to extract the data you need:

const str =
  "<p>Bla bla bla bla.</p>\n<ul>\n<li><strong>Bla bla</strong>. Bla bla bla.</li>\n<li><strong>Bla bla bla</strong>.<a href='#footnote1' id='sup1'><sup>1</sup></a> Bla bla bla.</li>\n<li><strong>Bla bla bla.<a href='#footnote2' id='sup2'><sup>2</sup></a>\r\n</strong>&#160; Bla bla bla.</li>\n<li><strong>Bla bla:</strong>Bla bla <a href=\"https://google.com\" target=\"no\">Google</a> * bla bla bla.<a href='#footnote1' id='sup3'><sup>1</sup></a></li>\n</ul>\n<p>&#160;</p>";

const el = document.createElement("html");
el.innerHTML = str;
const anchors = el.getElementsByTagName("a");

const MyLink = () => null;
const myClickHandler = () => null;

export default () =>
  [...anchors].map((anchor, index) => {
    const sup = anchor.getElementsByTagName("sup");
    if (sup.length === 0) {
      return null;
    }

    return (
      <MyLink
        key={index.toString()}
        id={anchor.id}
        addClasses={"footnote-link"}
        href={anchor.href}
        handleClick={myClickHandler}
        ariaProps={{
          label: sup[0].innerHTML
        }}
      >
        <sup className={anchor.href ? "custom-class" : ""}>{sup[0].innerHTML}</sup>
      </MyLink>
    );
  });


Replace MyLink and myClickHandler with your own.

Dean James
  • 2,491
  • 4
  • 21
  • 29
  • That's helpful. Thanks! It helps me replace anchor tags with my custom link component. But, I also need to display the source string (along with all p, ul, li etc.) on the browser with anchor tags replaced by my custom link. Should I use useEffect() here to update the DOM with the new string (i.e. anchors replaced by custom links) after page has rendered once? – abhi Feb 18 '21 at 19:34
  • The same approach can be used to extract the `ul` and `li` nodes also. The complexity of it will depend on how dynamic the source structure can be. – Dean James Feb 18 '21 at 19:37
  • Your response was quick. I edited my last response after that. I am not trying to extract ul, li. I am trying to render my source string

    bla bla

    • .... on the browser after having replaced the anchor tags with custom link. I hope I am making sense.
    – abhi Feb 18 '21 at 19:41
0

You might consider parsing the HTML and modifying it using DOM methods, then rendering it back out as a string. The demo below is just a proof of concept, but it could be incorporated into a React component that does the same sort of thing.

Note: This is similar to Dean's answer which has a more React-specific (and arguably cleaner) example.

const input = "<p>Bla bla bla bla.</p>\n<ul>\n<li><strong>Bla bla</strong>. Bla bla bla.</li>\n<li><strong>Bla bla bla</strong>.<a href='#footnote1' id='sup1'><sup>1</sup></a> Bla bla bla.</li>\n<li><strong>Bla bla bla.<a href='#footnote2' id='sup2'><sup>2</sup></a>\r\n</strong>&#160; Bla bla bla.</li>\n<li><strong>Bla bla:</strong>Bla bla <a href=\"https://google.com\" target=\"no\">Google</a> * bla bla bla.<a href='#footnote1' id='sup3'><sup>1</sup></a></li>\n</ul>\n<p>&#160;</p>"

const parser = new DOMParser();

const doc = parser.parseFromString(input, 'text/html');

// might be a more efficient way to do this. just querying for anchors
// and filtering out the ones that don't have a <sup> child
const anchors = [...doc.querySelectorAll('a')].filter(elem => elem.querySelector('sup'));

anchors.forEach(a => {
  const sup = a.querySelector('sup');
  a.setAttribute('aria-label', sup.innerText);
  // etc.
})

document.getElementById('demo').innerHTML = doc.body.innerHTML;
document.getElementById('source').innerText = doc.body.innerHTML;
<h2>Resulting html</h2>
<pre id="source"></pre>

<hr/>

<h2>Rendered result</h2>
<div id="demo"></div>
ray
  • 26,557
  • 5
  • 28
  • 27
  • Thanks @ray-hatfield! This solution also looks promising, but the problem is that I don't want to just add a few attributes to the anchor tag. I want to completely replace those anchor tags with my custom React link component I described in my question. – abhi Feb 19 '21 at 16:59
  • Understood. This wasn't intended as a complete solution, just pointing the way. Having a Document/DOM to work with is going to make extracting whatever info you need much, much easier and far less error prone than string manipulation. – ray Feb 19 '21 at 17:37