0

I have some markup in JS as follows:

<div class="col-sm-4">
    <span id="some-media" class="media">Text</span>
</div>

I would like to select the class attribute of the span and prepend its value with lets say the characters: "::". So after the regex replace i would end up with:

<div class="col-sm-4">
    <span id="some-media" class="::media">Text</span>
</div>

EDIT: Note that the order of the attributes in the HTML element is variable so my span attributes could very well have different order like so:

<div class="col-sm-4">
    <span class="::media" id="some-media" >Text</span>
</div>
asulaiman
  • 1,221
  • 12
  • 18
  • 3
    Is there any specific reason you have to use *regex* for this? – Psidom Sep 13 '17 at 15:09
  • @thebluefox ive managed to select the attribute with the following regex so far: ]+class\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>. However i havent been able to prepend the characters to the class value. – asulaiman Sep 13 '17 at 15:12
  • @Psidom do you any other alternative for accomplishing this? I dont want to include a parser just to solve a small problem like this. – asulaiman Sep 13 '17 at 15:25
  • Take a look at this [answer](https://stackoverflow.com/questions/1732348/regex-match-open-tags-except-xhtml-self-contained-tags/1732454#1732454). BTW, what's wrong with a `parser`? – Psidom Sep 13 '17 at 15:28
  • Take a look at the second answer to the question you posted as well. I think a parser for solving such a trivial problem is overkill. – asulaiman Sep 13 '17 at 15:33

5 Answers5

3

You got a regex solution, this is a DOMmy one:

var html = `<div class="col-sm-4">
    <span id="some-media" class="media">Text</span>
</div>`
var doc = (new DOMParser()).parseFromString(html, "text/html");
var el = doc.getElementsByTagName('span')[0];
el.setAttribute('class', '::' + el.className);
console.log(
  doc.getElementsByClassName('::media').length > 0 // check if modification's done
);

Since you have no way except Regular Expressions this can be considered as a workaround:

(<span[^>]*class=.)([^'"]+)

JS:

var html = `<div class="col-sm-4">
    <span id="some-media" class="media">Text</span>
</div>
<span class="media" id="some-media">Text</span>
`;

console.log(
    html.replace(/(<span[^>]*class=.)([^'"]+)/g, `$1::$2`)
);
revo
  • 47,783
  • 14
  • 74
  • 117
  • I am using nodejs to solve this problem sorry should have mentioned in my question so i dont have access to DOMParser. – asulaiman Sep 13 '17 at 15:31
  • What could be the selector? `.col-sm-4 span` or `span` alone? – revo Sep 13 '17 at 15:33
  • I dont understand the question? – asulaiman Sep 13 '17 at 15:37
  • I mean how much big your HTML document would be and what are the rules to match that `span`? What if that `span` is a direct child of `body`? Should it be selected for later modification? – revo Sep 13 '17 at 15:41
  • I just want to match all the `span`s in the markup, there is no other specific rule for matching. – asulaiman Sep 13 '17 at 15:44
0

I think this is what you're after:

var test = $("#some-media")[0].outerHTML();
var test2 = '<div id="some-media" class="media">Text</div>'

if(/span/.test(test)) //Valid as contains 'span'
    alert(test.replace(/(class=")/g, "$1::"));

 if(/span/.test(test2)) //Not valid
    alert(test.replace(/(class=")/g, "$1::"));
DNKROZ
  • 2,634
  • 4
  • 25
  • 43
  • I believe this will prepend all class attribute values in the markup with the characters. I am only looking to prepend if it is an attribute of a particular html element (in this case a span). – asulaiman Sep 13 '17 at 15:22
  • this wont work if the span is nested under the div which is my usecase. – asulaiman Sep 13 '17 at 15:28
  • @asulaiman Why won't it? how are you getting your span object? See my updated answer. You can use `outerHTML()` to get the html of an object – DNKROZ Sep 13 '17 at 15:44
  • The markup is encapsulated in a string that i am trying to parse. This is on node it isnt in a web browser. – asulaiman Sep 13 '17 at 15:45
  • My bad, missed the node part – DNKROZ Sep 13 '17 at 15:47
0

This isn't using regex, but you can do it like this in vanilla JavaScript:

const el = document.getElementsByClassName('media')[0];
el.className = '::' + el.className;

Or in jQuery:

const $el = $('div span.media');
$el.attr('class', '::' + $el.attr('class'));

Hope this helps.

psilocybin
  • 1,070
  • 7
  • 25
0

Don't parse html with regex, use DocumentFragment (or DOMParser) object instead:

var html_str = '<div class="col-sm-4"><span class="media">Text</span></div>',
    df = document.createRange().createContextualFragment(html_str),
    span = df.querySelector('span');
    
span.setAttribute('class', '::' + span.getAttribute('class'));
console.log(df.querySelector('div').outerHTML);
RomanPerekhrest
  • 88,541
  • 4
  • 65
  • 105
0

Since the order differs, writing a regex that captures all possible combinations of syntax might be rather difficult.

So we'd need a full list of rules the span follows so we can identify that span? Got some more info about if the span occurs in a longer HTML string? Or is the string this span and this span only?

An alternative would be to use one of the several node DOM modules available, so you can work with HTML nodes and be able to use any of the above solutions to make the problem simpler.

But since you're using node:

1) Are you using any templating engines? If so, why not rerender the entire template?

2) Why does the class name have to change on the server side? Isn't there a workaround on the clientside where you do have access to the DOM natively? Or if it's just to add styling, why not add another css file that overwrites the styling of spans with className 'media'?

3) If all of the above is not applicable and it;s a trivial problem like you say, what error di you get using a simple replace?

strHTML.replace( 'class="media"', 'class="::media"' )

or if it has to be regex:

strHTML.replace( /class=\"(.*)\"/, 'class=\"::$1\"' );

Shilly
  • 8,511
  • 1
  • 18
  • 24