Replace fixed width values over 530px with 100% using RegEx Our site sends users transactional html emails. That content is then stored in a DB for retrieval when the user visits the mailbox section of our site.

The emails which get sent contain media queries in the header, which are lost as our system only pulls in the  of the content. 

This is why I need to convert at fixed widths that are greater than 530px. 

This is my take on replacing fixed widths >= 530px inside then content.

message.replace(/((?:m(?:in|ax)-)?width)\s*(\:\s?|=)\s*(['"\0])?\s*([1-9]\d{3,}|5[3-9][0-9]|[6-9][0-9]{2})(\s*(cm|in|mm|pc|pt|px|q)|(?!%|vw|vh|vmin|vmax|em|ex|ch|rem))[ ]*/gi, '$1$2100%')


It matches both HTML width attributes and CSS based properties that are fixed width values.

Is there a better, more concise way of doing this?

https://regex101.com/r/hdMYGn/6/

Example HTML:

&lt;table align="center" class="container float-center" style="border-spacing: 0; border-collapse: collapse; padding: 0; vertical-align: top; background: #ffffff; width: 580px; margin: 0 auto; Margin: 0 auto; float: none; text-align: center;" width="580"&gt;
Disclaimer

As pointed out in a comment by Thomas Ward, RegEx should not be used to parse HTML or CSS. This answer should only be taken as a review of your regular expressions only, and doesn't imply that I recommend using RegEx to parse HTMl/CSS.

As mentioned by Phrancis, you may use DOM manipulation methods to iterate over elements and set styles on them. Or, you can also use CSS to set styles to containers, such that the elements inside it respect the cascaded styles. For example, setting max-width on container and overflow will make the width of the container fixed and show scroll bars when content inside it overflows.

If you add more context regarding how and where this RegEx is being used, there might be better ways than RegEx. Please edit the question to add more context.



Review

(\:\s?|=)\s*: In this group, it is matching either : followed by optional space or = symbol. : has no special meaning in regular expression i.e. it is not meta-character thus there is no need to escape it. Next, \s? is not required here. As there is \s* after the group, it'll match any number of spaces followed by the one of the symbol from the group. So, \s? can be safely removed.

After making these changes the group will become (:|=)\s*.

It is also observed that character class is faster than OR in groups. Check the demo for [:=] and (:|=) for same input data. Notice the number of steps required to finish match. Character class took 12 steps while group with OR condition took 30 step which is more than twice of the character class.

Now, after using character class the group will become [:=]\s*

Similarly, in other character class 0 is escaped as \0 which is not necessary and can be safely removed.

Let's look at the group (\s*(cm|in|mm|pc|pt|px|q)|(?!%|vw|vh|vmin|vmax|em|ex|ch|rem)).

This matches the unit f or the width property. First, \s* to match space is used. Space cannot be used between the value and unit in CSS. That'll make the property invalid and will not be applied.

After that, there are two groups of units separated by |-OR. These two can be combined in one as (cm|in|mm|pc|pt|px|q|%|vw|vh|vmin|vmax|em|ex|ch|rem).

At the end of regex, [ ]* is used to match any number of spaces. This can be simply written as *(Note this is space followed by *). What is there is a tab instead of space? \s* will solve the problem.

Oh wait, that space is not used anywhere, so it can be removed from regex.

Mistake


If you look closely at the input and output string, you'll notice that the regex is removing single and double quotes from the attribute styles.

width='530' has become width=100%'.

To solve this, simply add $3 in the replacement part. The replacement will be now $1$2$3100%.
Another one is that the regex leaves width: 600rem; which is not intended.

This will be solved when two groups of units are combined. But, this will create an issue of not matching unit-less values like width: 600. By adding empty option in group using | will solve the problem(notice the end of group). Here's how the group will look like

(cm|in|mm|pc|pt|px|q|%|vw|vh|vmin|vmax|em|ex|ch|rem|)



Improvements

Does the above changes really improve the regex?

The above changes brings the number of steps to 1630 from 1881 with one increase in match(rem).

RegEx: ((?:m(?:in|ax)-)?width)\s*([:=])\s*(['"0])?\s*([1-9]\d{3,}|5[3-9][0-9]|[6-9][0-9]{2})(cm|in|mm|pc|pt|px|q|%|vw|vh|vmin|vmax|em|ex|ch|rem|)

Replacement: $1$2$3100%

Demo



UPDATE:


  The emails which get sent contain media queries in the header, which are lost as our system only pulls in the of the content.


The media queries related to email can be stored in a separate CSS file and it can be included on the page where email content is shown on your site.

You can also remove all the inline styles from email content HTML and put them in separate CSS file. When sending email, include this CSS file in HTML and when showing the content in your site, include other CSS file. This will show the content correctly in respective sites.