Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

Post History

85%
+10 −0
Q&A Can regex be used to check if input conforms to a very strict subset of HTML?

tl;dr Although it can be done with regex (and work for "most" cases), I still prefer to use a parser. Long answer I'd use something such as DOMParser to do the job: let validTags = ['p', 'span', 'b...

posted 4y ago by hkotsubo‭  ·  edited 4y ago by hkotsubo‭

Answer
#4: Post edited by user avatar hkotsubo‭ · 2020-09-04T00:47:08Z (about 4 years ago)
  • # tl;dr
  • Although it can be done with regex (and work for "most" cases), I still prefer to use a *parser*.
  • # Long answer
  • I'd use something such as [`DOMParser`](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to do the job:
  • ```javascript
  • let validTags = ['p', 'span', 'br', 'i', 'b', 'u'];
  • let validAttribs = ['style', 'href'];
  • function validHtml(string) {
  • let domparser = new DOMParser();
  • let doc = domparser.parseFromString(string, 'application/xml');
  • for (const node of doc.querySelectorAll('*')) {
  • if (! validTags.includes(node.nodeName)) return false;
  • for (const attr of node.attributes) {
  • if (! validAttribs.includes(attr.name)) return false;
  • }
  • }
  • return true;
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // false
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // false
  • ```
  • With this, not only I can easily update the rules (change the arrays of valid tags and attributes), but also validate the HTML itself - [according to the docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Error_handling), when the string is invalid, `parseFromString` returns an error document:
  • ```xml
  • <parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">
  • (error description)
  • <sourcetext>(a snippet of the source XML)</sourcetext>
  • </parsererror>
  • ```
  • So, when checking it in the `for` loop, it will enter the first `if` (because the tag `parseerror` is not in the array of valid tags) and it'll return `false` as well.
  • ---
  • You told you don't care if the HTML is valid, but even without that restriction, doing it with regex is - IMO - much worse. I could think of something like this:
  • ```javascript
  • function validHtml(string) {
  • // check if it has invalid tag
  • let tags = /<(?!\b([piu]|span|br?)\b)[^>]*>/;
  • // check if tag is valid, but with an invalid attribute
  • let attributes = /<\b([piu]|span|br?)\b[^>\w]*(?!\b(href|style)\b=[^>]*)\w[^>]*>/;
  • return (! tags.test(string)) || (! attributes.test(string));
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // true
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // true
  • ```
  • The only difference between this and the first example is the first and the last cases. In the first case, the HTML is invalid, although it has all the valid tags (so the regex says it's valid). And in the last case, the string is not even HTML at all, but the regex also says it's valid - and this also happens with strings like `' '` and `'!@#$%¨&*'`.
  • But the problem here is - IMO - how easy/hard is to read, understand and maintain each one of the options. I think the first one with `DOMParser` is much easier - and you have more control over the structure (having DOM nodes, you can easily check whatever information they have, making it easier to change the criteria - such as check attribute values, comments, text nodes and so on).
  • And I haven't done extensive tests with those regexes, so I'm pretty sure there could be lots of corner cases that they don't catch - which are already handled by a HTML parser.
  • Regarding overhead, regex also has its own. If performance is an issue, you should benchmark it anyway. But I believe you should also consider how easy it is to maitain the code - IMO, the regexes above are not trivial to understand.
  • My conclusion is that using regex might work, but I wouldn't recommend it as "the best way".
  • # tl;dr
  • Although it can be done with regex (and work for "most" cases), I still prefer to use a *parser*.
  • # Long answer
  • I'd use something such as [`DOMParser`](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to do the job:
  • ```javascript
  • let validTags = ['p', 'span', 'br', 'i', 'b', 'u'];
  • let validAttribs = ['style', 'href'];
  • function validHtml(string) {
  • let domparser = new DOMParser();
  • let doc = domparser.parseFromString(string, 'application/xml');
  • for (const node of doc.querySelectorAll('*')) {
  • if (! validTags.includes(node.nodeName)) return false;
  • for (const attr of node.attributes) {
  • if (! validAttribs.includes(attr.name)) return false;
  • }
  • }
  • return true;
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // false
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // false
  • ```
  • With this, not only I can easily update the rules (change the arrays of valid tags and attributes), but also validate the HTML itself - [according to the docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Error_handling), when the string is invalid, `parseFromString` returns an error document:
  • ```xml
  • <parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">
  • (error description)
  • <sourcetext>(a snippet of the source XML)</sourcetext>
  • </parsererror>
  • ```
  • So, when checking it in the `for` loop, it will enter the first `if` (because the tag `parseerror` is not in the array of valid tags) and it'll return `false` as well.
  • ---
  • You told you don't care if the HTML is valid, but even without that restriction, doing it with regex is - IMO - much worse. I could think of something like this:
  • ```javascript
  • function validHtml(string) {
  • // check if it has invalid tag
  • let tags = /<(?!\b([piu]|span|br?)\b)[^>]*>/;
  • // check if tag is valid, but with an invalid attribute
  • let attributes = /<\b([piu]|span|br?)\b[^>\w]*(?!\b(href|style)\b=[^>]*)\w[^>]*>/;
  • return (! tags.test(string)) || (! attributes.test(string));
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // true
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // true
  • ```
  • The only difference between this and the first example is the first and the last cases. In the first case, the HTML is invalid, although it has all the valid tags (so the regex says it's valid). And in the last case, the string is not even HTML at all, but the regex also says it's valid - and this also happens with strings like `' '` and `'!@#$%¨&*'`.
  • But the problem here is - IMO - how easy/hard is to read, understand and maintain each one of the options. I think the first one with `DOMParser` is much easier - and you have more control over the structure (having DOM nodes, you can easily check whatever information they have, making it easier to change the criteria - such as check attribute values, comments, text nodes and so on).
  • And I haven't done extensive tests with those regexes, so I'm pretty sure there could be lots of corner cases that they don't catch - which are already handled by a HTML parser.
  • Regarding overhead, regex also has its own. If performance is an issue, you should benchmark it anyway. But I believe you should also consider how easy it is to maintain the code - IMO, the regexes above are not trivial to understand.
  • My conclusion is that using regex might work, but I wouldn't recommend it as "the best way".
#3: Post edited by user avatar hkotsubo‭ · 2020-09-03T21:03:27Z (about 4 years ago)
  • # tl;dr
  • Although it can be done with regex (and work for "most" cases), I still prefer to use a *parser*.
  • # Long answer
  • I'd use something such as [`DOMParser`](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to do the job:
  • ```javascript
  • let validTags = ['p', 'span', 'br', 'i', 'b', 'u'];
  • let validAttribs = ['style', 'href'];
  • function validHtml(string) {
  • let domparser = new DOMParser();
  • let doc = domparser.parseFromString(string, 'application/xml');
  • for (const node of doc.querySelectorAll('*')) {
  • if (! validTags.includes(node.nodeName)) return false;
  • for (const attr of node.attributes) {
  • if (! validAttribs.includes(attr.name)) return false;
  • }
  • }
  • return true;
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // false
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // false
  • ```
  • With this, not only I can easily update the rules (change the arrays of valid tags and attributes), but also validate the HTML itself - [according to the docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Error_handling), when the string is invalid, `parseFromString` returns an error document:
  • ```xml
  • <parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">
  • (error description)
  • <sourcetext>(a snippet of the source XML)</sourcetext>
  • </parsererror>
  • ```
  • So, when checking it in the `for` loop, it will enter the first `if` (because the tag `parseerror` is not in the array of valid tags) and it'll return `false` as well.
  • ---
  • You told you don't care if the HTML is valid, but even without that restriction, doing it with regex is - IMO - much worse. I could think of something like this:
  • ```javascript
  • function validHtml(string) {
  • // check if it has invalid tag
  • let tags = /<(?!\b([piu]|span|br?)\b)[^>]*>/;
  • // check if tag is valid, but with an invalid attribute
  • let attributes = /<\b([piu]|span|br?)\b[^>\w]*(?!\b(href|style)\b=[^>]*)\w[^>]*>/;
  • return (! tags.test(string)) || (! attributes.test(string));
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // true
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // true
  • ```
  • The only difference between this and the first example is the first and the last cases. In the first case, the HTML is invalid, although it has all the valid tags (so the regex says it's valid). And in the last case, the string is not even HTML at all, but the regex also says it's valid.
  • But the problem here is - IMO - how easy/hard is to read, understand and maintain each one of the options. I think the first one with `DOMParser` is much easier - and you have more control over the structure (having DOM nodes, you can easily check whatever information they have, making it easier to change the criteria - such as check attribute values, comments, text nodes and so on).
  • And I haven't done extensive tests with those regexes, so I'm pretty sure there could be lots of corner cases that they don't catch - which are already handled by a HTML parser.
  • Regarding overhead, regex also has its own. If performance is an issue, you should benchmark it anyway. But I believe you should also consider how easy it is to maitain the code - IMO, the regexes above are not trivial to understand.
  • My conclusion is that using regex might work, but I wouldn't recommend it as "the best way".
  • # tl;dr
  • Although it can be done with regex (and work for "most" cases), I still prefer to use a *parser*.
  • # Long answer
  • I'd use something such as [`DOMParser`](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to do the job:
  • ```javascript
  • let validTags = ['p', 'span', 'br', 'i', 'b', 'u'];
  • let validAttribs = ['style', 'href'];
  • function validHtml(string) {
  • let domparser = new DOMParser();
  • let doc = domparser.parseFromString(string, 'application/xml');
  • for (const node of doc.querySelectorAll('*')) {
  • if (! validTags.includes(node.nodeName)) return false;
  • for (const attr of node.attributes) {
  • if (! validAttribs.includes(attr.name)) return false;
  • }
  • }
  • return true;
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // false
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // false
  • ```
  • With this, not only I can easily update the rules (change the arrays of valid tags and attributes), but also validate the HTML itself - [according to the docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Error_handling), when the string is invalid, `parseFromString` returns an error document:
  • ```xml
  • <parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">
  • (error description)
  • <sourcetext>(a snippet of the source XML)</sourcetext>
  • </parsererror>
  • ```
  • So, when checking it in the `for` loop, it will enter the first `if` (because the tag `parseerror` is not in the array of valid tags) and it'll return `false` as well.
  • ---
  • You told you don't care if the HTML is valid, but even without that restriction, doing it with regex is - IMO - much worse. I could think of something like this:
  • ```javascript
  • function validHtml(string) {
  • // check if it has invalid tag
  • let tags = /<(?!\b([piu]|span|br?)\b)[^>]*>/;
  • // check if tag is valid, but with an invalid attribute
  • let attributes = /<\b([piu]|span|br?)\b[^>\w]*(?!\b(href|style)\b=[^>]*)\w[^>]*>/;
  • return (! tags.test(string)) || (! attributes.test(string));
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // true
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // true
  • ```
  • The only difference between this and the first example is the first and the last cases. In the first case, the HTML is invalid, although it has all the valid tags (so the regex says it's valid). And in the last case, the string is not even HTML at all, but the regex also says it's valid - and this also happens with strings like `' '` and `'!@#$%¨&*'`.
  • But the problem here is - IMO - how easy/hard is to read, understand and maintain each one of the options. I think the first one with `DOMParser` is much easier - and you have more control over the structure (having DOM nodes, you can easily check whatever information they have, making it easier to change the criteria - such as check attribute values, comments, text nodes and so on).
  • And I haven't done extensive tests with those regexes, so I'm pretty sure there could be lots of corner cases that they don't catch - which are already handled by a HTML parser.
  • Regarding overhead, regex also has its own. If performance is an issue, you should benchmark it anyway. But I believe you should also consider how easy it is to maitain the code - IMO, the regexes above are not trivial to understand.
  • My conclusion is that using regex might work, but I wouldn't recommend it as "the best way".
#2: Post edited by user avatar hkotsubo‭ · 2020-09-03T20:59:36Z (about 4 years ago)
  • # tl;dr
  • Although it can be done with regex (and work for "most" cases), I still prefer to use a *parser*.
  • # Long answer
  • I'd use something such as [`DOMParser`](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to do the job:
  • ```javascript
  • let validTags = ['p', 'span', 'br', 'i', 'b', 'u'];
  • let validAttribs = ['style', 'href'];
  • function validHtml(string) {
  • let domparser = new DOMParser();
  • let doc = domparser.parseFromString(string, 'application/xml');
  • for (const node of doc.querySelectorAll('*')) {
  • if (! validTags.includes(node.nodeName)) return false;
  • for (const attr of node.attributes) {
  • if (! validAttribs.includes(attr.name)) return false;
  • }
  • }
  • return true;
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // false
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • ```
  • With this, not only I can easily update the rules (change the arrays of valid tags and attributes), but also validate the HTML itself - [according to the docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Error_handling), when the string is invalid, `parseFromString` returns an error document:
  • ```xml
  • <parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">
  • (error description)
  • <sourcetext>(a snippet of the source XML)</sourcetext>
  • </parsererror>
  • ```
  • So, when checking it in the `for` loop, it will enter the first `if` (because the tag `parseerror` is not in the array of valid tags) and it'll return `false` as well.
  • ---
  • You told you don't care if the HTML is valid, but even without that restriction, doing it with regex is - IMO - much worse. I could think of something like this:
  • ```javascript
  • function validHtml(string) {
  • // check if it has invalid tag
  • let tags = /<(?!\b([piu]|span|br?)\b)[^>]*>/;
  • // check if tag is valid, but with an invalid attribute
  • let attributes = /<\b([piu]|span|br?)\b[^>\w]*(?!\b(href|style)\b=[^>]*)\w[^>]*>/;
  • return (! tags.test(string)) || (! attributes.test(string));
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // true
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • ```
  • The only difference between this and the first example is the first case: the HTML is invalid, although it has all the valid tags.
  • But the problem here is - IMO - how easy/hard is to read, understand and maintain each one of the options. I think the first one with `DOMParser` is much easier - and you have more control over the structure (having DOM nodes, you can easily check whatever information they have, making it easier to change the criteria - such as check attribute values, comments, text nodes and so on).
  • And I haven't done extensive tests with those regexes, so I'm pretty sure there could be lots of corner cases that they don't catch - which are already handled by a HTML parser.
  • Regarding overhead, regex also has its own. If performance is an issue, you should benchmark it anyway. But I believe you should also consider how easy it is to maitain the code - IMO, the regexes above are not trivial to understand.
  • My conclusion is that using regex might work, but I wouldn't recommend it as "the best way".
  • # tl;dr
  • Although it can be done with regex (and work for "most" cases), I still prefer to use a *parser*.
  • # Long answer
  • I'd use something such as [`DOMParser`](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to do the job:
  • ```javascript
  • let validTags = ['p', 'span', 'br', 'i', 'b', 'u'];
  • let validAttribs = ['style', 'href'];
  • function validHtml(string) {
  • let domparser = new DOMParser();
  • let doc = domparser.parseFromString(string, 'application/xml');
  • for (const node of doc.querySelectorAll('*')) {
  • if (! validTags.includes(node.nodeName)) return false;
  • for (const attr of node.attributes) {
  • if (! validAttribs.includes(attr.name)) return false;
  • }
  • }
  • return true;
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // false
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // false
  • ```
  • With this, not only I can easily update the rules (change the arrays of valid tags and attributes), but also validate the HTML itself - [according to the docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Error_handling), when the string is invalid, `parseFromString` returns an error document:
  • ```xml
  • <parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">
  • (error description)
  • <sourcetext>(a snippet of the source XML)</sourcetext>
  • </parsererror>
  • ```
  • So, when checking it in the `for` loop, it will enter the first `if` (because the tag `parseerror` is not in the array of valid tags) and it'll return `false` as well.
  • ---
  • You told you don't care if the HTML is valid, but even without that restriction, doing it with regex is - IMO - much worse. I could think of something like this:
  • ```javascript
  • function validHtml(string) {
  • // check if it has invalid tag
  • let tags = /<(?!\b([piu]|span|br?)\b)[^>]*>/;
  • // check if tag is valid, but with an invalid attribute
  • let attributes = /<\b([piu]|span|br?)\b[^>\w]*(?!\b(href|style)\b=[^>]*)\w[^>]*>/;
  • return (! tags.test(string)) || (! attributes.test(string));
  • }
  • console.log(validHtml('<p></span></p>>><br href="./></span>')); // true
  • console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
  • console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
  • console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
  • console.log(validHtml('abc')); // true
  • ```
  • The only difference between this and the first example is the first and the last cases. In the first case, the HTML is invalid, although it has all the valid tags (so the regex says it's valid). And in the last case, the string is not even HTML at all, but the regex also says it's valid.
  • But the problem here is - IMO - how easy/hard is to read, understand and maintain each one of the options. I think the first one with `DOMParser` is much easier - and you have more control over the structure (having DOM nodes, you can easily check whatever information they have, making it easier to change the criteria - such as check attribute values, comments, text nodes and so on).
  • And I haven't done extensive tests with those regexes, so I'm pretty sure there could be lots of corner cases that they don't catch - which are already handled by a HTML parser.
  • Regarding overhead, regex also has its own. If performance is an issue, you should benchmark it anyway. But I believe you should also consider how easy it is to maitain the code - IMO, the regexes above are not trivial to understand.
  • My conclusion is that using regex might work, but I wouldn't recommend it as "the best way".
#1: Initial revision by user avatar hkotsubo‭ · 2020-09-03T19:04:44Z (about 4 years ago)
# tl;dr

Although it can be done with regex (and work for "most" cases), I still prefer to use a *parser*.

# Long answer

I'd use something such as [`DOMParser`](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to do the job:

```javascript
let validTags = ['p', 'span', 'br', 'i', 'b', 'u'];
let validAttribs = ['style', 'href'];

function validHtml(string) {
  let domparser = new DOMParser();
  let doc = domparser.parseFromString(string, 'application/xml');
  for (const node of doc.querySelectorAll('*')) {
    if (! validTags.includes(node.nodeName)) return false;

    for (const attr of node.attributes) {
      if (! validAttribs.includes(attr.name)) return false;
    }
  }
  return true;
}

console.log(validHtml('<p></span></p>>><br href="./></span>')); // false
console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
```

With this, not only I can easily update the rules (change the arrays of valid tags and attributes), but also validate the HTML itself - [according to the docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser#Error_handling), when the string is invalid, `parseFromString` returns an error document:

```xml
<parsererror xmlns="http://www.mozilla.org/newlayout/xml/parsererror.xml">
  (error description)
  <sourcetext>(a snippet of the source XML)</sourcetext>
</parsererror>
```

So, when checking it in the `for` loop, it will enter the first `if` (because the tag `parseerror` is not in the array of valid tags) and it'll return `false` as well.

---
You told you don't care if the HTML is valid, but even without that restriction, doing it with regex is - IMO - much worse. I could think of something like this:

```javascript
function validHtml(string) {
    // check if it has invalid tag
    let tags = /<(?!\b([piu]|span|br?)\b)[^>]*>/;
    // check if tag is valid, but with an invalid attribute
    let attributes = /<\b([piu]|span|br?)\b[^>\w]*(?!\b(href|style)\b=[^>]*)\w[^>]*>/;
    return (! tags.test(string)) || (! attributes.test(string));
}

console.log(validHtml('<p></span></p>>><br href="./></span>')); // true
console.log(validHtml('<p onclick="alert(\'hi\')"></p>')); // false
console.log(validHtml('<p style="padding: 2px">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // true
console.log(validHtml('<p class="whatever">Lorem <span>ipsum <b>dolor</b> sit <u>amet</u></span></p>')); // false
```

The only difference between this and the first example is the first case: the HTML is invalid, although it has all the valid tags.

But the problem here is - IMO - how easy/hard is to read, understand and maintain each one of the options. I think the first one with `DOMParser` is much easier - and you have more control over the structure (having DOM nodes, you can easily check whatever information they have, making it easier to change the criteria - such as check attribute values, comments, text nodes and so on).

And I haven't done extensive tests with those regexes, so I'm pretty sure there could be lots of corner cases that they don't catch - which are already handled by a HTML parser.

Regarding overhead, regex also has its own. If performance is an issue, you should benchmark it anyway. But I believe you should also consider how easy it is to maitain the code - IMO, the regexes above are not trivial to understand.

My conclusion is that using regex might work, but I wouldn't recommend it as "the best way".