Gregory R.
Gregory R.

Reputation: 1945

How to wrap first word in a string without wrapping the HTML elements in Javascript?

I am looking to wrap the first word in a string, for example:

var html = '<br> <br> Hello world!';

I have the following code:

html.replace(/^\s*\w+/, '<div class="underline">$&</div>');

This will output:

<div class="underline"><br></div> <br> Hello world!

How can I ignore HTML tags so that the output is like this, where the first word "Hello" gets wrapped in the DIV?:

<br> <br> <div class="underline">Hello</div> world!

Upvotes: 2

Views: 282

Answers (2)

takahar tanak
takahar tanak

Reputation: 41

Try this

html.replace(/[^< ][A-Za-z]+[^> ]/, '<div class="underline">$&</div>');

or

html.replace(/([^< ][A-Za-z]+[^> ])/, '<div class="underline">$1</div>');

For reference, the same function in sed command is

echo '<br> <br> Hello world!' | sed -e 's/\([^< ][A-Za-z]\+[^> ]\)/<div class="underline">\1<\/div>/'

To support single body character,

html.replace(/([^<][A-Za-z]+[^>])/, '<div class="underline">$1</div>');

Although, if the html tag has some attributes like <p class="classname">, it won't work. DOMParser will be necessary instead of regex or, consult this topic.

Upvotes: 0

CertainPerformance
CertainPerformance

Reputation: 370949

You can use DOMParser to create a document from the string, then look through text nodes in the document to find one whose trimmed text is not the empty string. Replace that node with a <div class="underline">:

// https://stackoverflow.com/questions/2579666/getelementsbytagname-equivalent-for-textnodes
const getTextNodes = (parent) => {
    const walker = document.createTreeWalker(
        parent,
        NodeFilter.SHOW_TEXT,
        null,
        false
    );

    let node;
    const nodes = [];

    while(node = walker.nextNode()) {
        nodes.push(node);
    }
    return nodes;
};

const html = '<br> <br> Hello world!';
const doc = new DOMParser().parseFromString(html, 'text/html');
const nodes = getTextNodes(doc.body);
const node = nodes.find(node => node.textContent.trim() !== '');
const [, firstWord, rest] = node.textContent.match(/(\S+)(.*)/);
const newNode = document.createElement('div');
newNode.className = 'underline';
newNode.textContent = firstWord;
node.replaceWith(newNode);
newNode.insertAdjacentHTML('afterend', rest);
console.log(doc.body.innerHTML);

Upvotes: 5

Related Questions