2

In this following text of html:

<p>Here are some users you mentioned:
    <span class="mention-inserted">@johnsmith1</span>,
    <span class="mention-inserted">@daisy_apple23</span>,
    <span class="mention-inserted">@bob.erricson</span>.
    //... some more text //
</p>

How do I find all the

<span class="mention-inserted">@{user}</span>

and convert them into <a></a> tags with added classes, keeping their respective positions without removing any other text?

so

<span class="mention-inserted">@johnsmith1</span>

should simply convert to

<a class="mention-inserted active-link">@johnsmith1</a>

& so on. Is this more complex then I believe it to be? Or am I blatantly missing a simple use of regEx?

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
John Durand
  • 1,934
  • 5
  • 22
  • 34

2 Answers2

2

Instead of trying to come up with a regular expression for a non-regular language, you can instead use querySelectorAll(). This will allow you to select all span elements with the class mention-inserted. You can then loop over this collection using .forEach and change the span element to a link using .outerHTML.

See example below:

document.querySelectorAll('span.mention-inserted').forEach(elem => {
  elem.outerHTML = `<a class="${elem.classList.value} active-link">${elem.innerHTML}</a>`;
});
.active-link {
  color: blue;
  cursor: pointer;
}
<p>
  Here are some users you mentioned:
  <span class="mention-inserted foo">@johnsmith1</span>,
  <span class="mention-inserted">@daisy_apple23</span>,
  <span class="mention-inserted">@bob.erricson</span>
</p>

If your text is a string in JS (rather than HTML), you can use a DOMParser instead, which will then allow you to use the above methods:

const str = `<p>Here are some users you mentioned: <span class="mention-inserted">@johnsmith1</span>,<span class="mention-inserted">@daisy_apple23</span>,<span class="mention-inserted">@bob.erricson</span></p>`;
const parsed = new DOMParser().parseFromString(str, "text/html").body;
parsed.querySelectorAll('span.mention-inserted').forEach(elem => {
  elem.outerHTML = `<a class="${elem.classList.value} active-link">${elem.innerHTML}</a>`;
});

const result = parsed.innerHTML; // String output/result
document.body.appendChild(parsed); // HTMLHtmlElement object output
console.log(result);
.active-link {
  color: blue;
  cursor: pointer;
}
Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
  • like aforementioned, this a text of HTML not any actual rendered html, so querying is not an option. – John Durand Nov 22 '19 at 03:00
  • @JohnDurand I've updated my answer (see second code snippet) – Nick Parsons Nov 22 '19 at 03:01
  • ah, the `DOMParser`! very nice solution! it works however the result is wrapped in `` elements. How do I just return the `

    ` wrap or basically the root element in which it was input?

    – John Durand Nov 22 '19 at 03:13
  • @JohnDurand you could `.querySelector` the `body` (see the second snippet update). There might be a better approach to do this. I'll update you if I find one – Nick Parsons Nov 22 '19 at 03:18
  • nevermind, a solution is referenced here https://stackoverflow.com/questions/32280798/javascript-how-to-remove-the-htmlheadbody-elements-when-using-domparser-w – John Durand Nov 22 '19 at 03:19
  • 1
    @JohnDurand that would work too. You can actually just use `.body` instead of `.documentElement`, which will give you the body. Then you can use `.innerHTML` to get your required contents – Nick Parsons Nov 22 '19 at 03:23
2

I have tried using regular expressions to highlight html textNode and not change the html structure, but it's to complex. So I used the DOM API to implement it.

function replaceSpanWithAnchor(htmlText) {
  const node = document.createElement('div')
  node.innerHTML = htmlText
  const spans = node.querySelectorAll('span')
  for (let span of spans) {
    let a = document.createElement('a')
    a.className = "mention-inserted active-link"
    a.textContent = span.textContent
    span.replaceWith(a)
  }
  return node.innerHTML
}

console.log(replaceSpanWithAnchor('<p>Here are some users you mentioned: <span class="mention-inserted">@johnsmith1</span>,<span class="mention-inserted">@daisy_apple23</span>,<span class="mention-inserted">@bob.erricson</span>.</p>'))
// <p>Here are some users you mentioned: <a class="mention-inserted active-link">@johnsmith1</a>,<a class="mention-inserted active-link">@daisy_apple23</a>,<a class="mention-inserted active-link">@bob.erricson</a>.</p>
Yan Li
  • 63
  • 4