Remove HTML comments with Regex, in Javascript

41,464

Solution 1

The regex /<!--[\s\S]*?-->/g should work.

You're going to kill escaping text spans in CDATA blocks.

E.g.

<script><!-- notACommentHere() --></script>

and literal text in formatted code blocks

<xmp>I'm demoing HTML <!-- comments --></xmp>

<textarea><!-- Not a comment either --></textarea>

EDIT:

This also won't prevent new comments from being introduced as in

<!-<!-- A comment -->- not comment text -->

which after one round of that regexp would become

<!-- not comment text -->

If this is a problem, you can escape < that are not part of a comment or tag (complicated to get right) or you can loop and replace as above until the string settles down.


Here's a regex that will match comments including psuedo-comments and unclosed comments per the HTML-5 spec. The CDATA section are only strictly allowed in foreign XML. This suffers the same caveats as above.

var COMMENT_PSEUDO_COMMENT_OR_LT_BANG = new RegExp(
    '<!--[\\s\\S]*?(?:-->)?'
    + '<!---+>?'  // A comment with no body
    + '|<!(?![dD][oO][cC][tT][yY][pP][eE]|\\[CDATA\\[)[^>]*>?'
    + '|<[?][^>]*>?',  // A pseudo-comment
    'g');

Solution 2

This is based off Aurielle Perlmann's answer, it supports all cases (single-line, multi-line, un-terminated, and nested comments):

/(<!--.*?-->)|(<!--[\S\s]+?-->)|(<!--[\S\s]*?$)/g

https://regex101.com/r/az8Lu6/1

regex101 output

Solution 3

this works also for multiline - (<!--.*?-->)|(<!--[\w\W\n\s]+?-->)

enter image description here

Solution 4

You should use the /s modifier

html = html.replace(/<!--.*?-->/sg, "")

Tested in perl:

use strict;
use warnings;

my $str = 'hello <!--[if gte mso 9]><xml> <o:OfficeDocumentSettings> <o:RelyOnVML/> <o:AllowPNG/> </o:OfficeDocumentSettings> </xml><![endif]--><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:TrackMoves/> <w:TrackFormatting/> <w:HyphenationZone>21</w:HyphenationZone> <w:PunctuationKerning/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>NO-BOK</w:LidThemeOther> <w:LidThemeAsian>X-NONE</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:SplitPgBreakAndParaMark/> <w:EnableOpenTypeKerning/> <w:DontFlipMirrorIndents/> <w:OverrideTableStyleHps/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="&#45;-"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument> </xml><![endif]-->world!';

$str =~ s/<!--.*?-->//sg;
print $str;

Output:
hello world!

Solution 5

I recently needed to do this very thing (i.e. Remove all comments from a html file). Some things that these other answers don't take into consideration;

  1. An html file can have css and JS inline, which, well I wanted to strip at least
  2. Comment syntax while inside a string or regex is totally valid. (My string/regex exclusion pattern is based on: https://stackoverflow.com/a/23667311/3799617)

TLDR: (I just want the regex that removes all the comments, plz)

/\\\/|\/\s*(?:\\\/|[^\/\*\n])+\/|\\"|"(?:\\"|[^"])*"|\\'|'(?:\\'|[^'])*'|\\`|`(?:\\`|[^`])*`|(\/\/[\s\S]*?$|(?:<!--|\/\s*\*)\s*[\s\S]*?\s*(?:-->|\*\s*\/))/gm

And here is a simple demo: https://www.regexr.com/5fjlu


I don't hate reading, show me the rest:

I also needed to do various other matching that took into account valid strings containing things that otherwise appear as valid targets. So I made a class to handle my variety of uses.

class StringAwareRegExp extends RegExp {
    static get [Symbol.species]() { return RegExp; }

    constructor(regex, flags){
        if(regex instanceof RegExp) regex = StringAwareRegExp.prototype.regExpToInnerRegexString(regex);

        regex = super(`${StringAwareRegExp.prototype.disqualifyStringsRegExp}(${regex})`, flags);

        return regex;
    }

    stringReplace(sourceString, replaceString = ''){
        return sourceString.replace(this, (match, group1) => { return group1 === undefined ? match : replaceString; });
    }
}

StringAwareRegExp.prototype.regExpToInnerRegexString = function(regExp){ return regExp.toString().replace(/^\/|\/[gimsuy]*$/g, ''); };
Object.defineProperty(StringAwareRegExp.prototype, 'disqualifyStringsRegExp', {
    get: function(){
        return StringAwareRegExp.prototype.regExpToInnerRegexString(/\\\/|\/\s*(?:\\\/|[^\/\*\n])+\/|\\"|"(?:\\"|[^"])*"|\\'|'(?:\\'|[^'])*'|\\`|`(?:\\`|[^`])*`|/);
    }
});

From this I created two more classes to hone in on the 2 major types of matches I needed:

class CommentRegExp extends StringAwareRegExp {
    constructor(regex, flags){
        if(regex instanceof RegExp) regex = StringAwareRegExp.prototype.regExpToInnerRegexString(regex);

        return super(`\\/\\/${regex}$|(?:<!--|\\/\\s*\\*)\\s*${regex}\\s*(?:-->|\\*\\s*\\/)`, flags);
    }
}

class StatementRegExp extends StringAwareRegExp {
    constructor(regex, flags){
        if(regex instanceof RegExp) regex = StringAwareRegExp.prototype.regExpToInnerRegexString(regex);

        return super(`${regex}\\s*;?\\s*?`, flags);
    }
}

And finally (however useful it may be to whomever) the regex created from this:

const allCommentsRegex = new CommentRegExp(/[\s\S]*?/, 'gm');
const enableBabelRegex = new CommentRegExp(/enable-?_?\s?babel/, 'gmi');
const disableBabelRegex = new CommentRegExp(/disable-?_?\s?babel/, 'gmi');
const includeRegex = new CommentRegExp(/\s*(?:includes?|imports?|requires?)\s+(.+?)/, 'gm');
const importRegex = new StatementRegExp(/import\s+(?:(?:\w+|{(?:\s*\w\s*,?\s*)+})\s+from)?\s*['"`](.+?)['"`]/, 'gm');
const requireRegex = new StatementRegExp(/(?:var|let|const)\s+(?:(?:\w+|{(?:\s*\w\s*,?\s*)+}))\s*=\s*require\s*\(\s*['"`](.+?)['"`]\s*\)/, 'gm');
const atImportRegex = new StatementRegExp(/@import\s*['"`](.+?)['"`]/, 'gm');

And lastly, if anyone cares to see it in use. Here's the project I used it in (..My personal projects are always a WIP..): https://github.com/fatlard1993/page-compiler

Share:
41,464

Related videos on Youtube

rodbv
Author by

rodbv

Updated on November 05, 2020

Comments

  • rodbv
    rodbv over 3 years

    I've got some ugly HTML generated from Word, from which I want to strip all HTML comments.

    The HTML looks like this:

    <!--[if gte mso 9]><xml> <o:OfficeDocumentSettings> <o:RelyOnVML/> <o:AllowPNG/> </o:OfficeDocumentSettings> </xml><![endif]--><!--[if gte mso 9]><xml> <w:WordDocument> <w:View>Normal</w:View> <w:Zoom>0</w:Zoom> <w:TrackMoves/> <w:TrackFormatting/> <w:HyphenationZone>21</w:HyphenationZone> <w:PunctuationKerning/> <w:ValidateAgainstSchemas/> <w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> <w:IgnoreMixedContent>false</w:IgnoreMixedContent> <w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> <w:DoNotPromoteQF/> <w:LidThemeOther>NO-BOK</w:LidThemeOther> <w:LidThemeAsian>X-NONE</w:LidThemeAsian> <w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> <w:Compatibility> <w:BreakWrappedTables/> <w:SnapToGridInCell/> <w:WrapTextWithPunct/> <w:UseAsianBreakRules/> <w:DontGrowAutofit/> <w:SplitPgBreakAndParaMark/> <w:EnableOpenTypeKerning/> <w:DontFlipMirrorIndents/> <w:OverrideTableStyleHps/> </w:Compatibility> <m:mathPr> <m:mathFont m:val="Cambria Math"/> <m:brkBin m:val="before"/> <m:brkBinSub m:val="&#45;-"/> <m:smallFrac m:val="off"/> <m:dispDef/> <m:lMargin m:val="0"/> <m:rMargin m:val="0"/> <m:defJc m:val="centerGroup"/> <m:wrapIndent m:val="1440"/> <m:intLim m:val="subSup"/> <m:naryLim m:val="undOvr"/> </m:mathPr></w:WordDocument> </xml><![endif]-->
    

    ..and the regex I am using is this one

    html = html.replace(/<!--(.*?)-->/gm, "")
    

    But there seems to be no match, the string is unchanged.

    What I am missing?

    • Chandu
      Chandu about 13 years
      Works for me. Check jsfiddle.net/aQ5qp
    • Chandu
      Chandu about 13 years
      The whole string is a comment hence everything is replaced by ""
    • Alan Moore
      Alan Moore about 13 years
      As @Cybernate says, the regex does work on that text, so what gives? All the responders are assuming there are newlines in the text, which would explain the problem, but I don't see any newlines.
    • Bohdan Lyzanets
      Bohdan Lyzanets almost 10 years
    • Iłya Bursov
      Iłya Bursov almost 7 years
    • Sujay U N
      Sujay U N over 6 years
      In / <> /gm, what does the m after g stands for
    • pery mimon
      pery mimon over 4 years
      2019: I write goode solution that remove js and html comment here
  • Mike Samuel
    Mike Samuel about 13 years
    JavaScript does not have an s modifier. Use [\s\S] instead of ..
  • Alan Moore
    Alan Moore about 13 years
    JavaScript doesn't support the s modifier, nor does it support inline-modifier syntax, like (?s).
  • guiomie
    guiomie almost 11 years
    How would you modify this to get the comments only, and remove the html ?
  • Mike Samuel
    Mike Samuel almost 11 years
    @guiomie, I don't understand your goal. Please explain in more detail?
  • Christian Semrau
    Christian Semrau about 8 years
    @MikeSamuel Your last code snippet misses two backslash escapes around the CDATA.
  • Mike Samuel
    Mike Samuel about 8 years
    @ChristianSemrau, Thanks. Fixed I think.
  • Kousher Alam
    Kousher Alam over 6 years
    Its very nice yo.
  • Vishal Kumar Sahu
    Vishal Kumar Sahu over 6 years
    Works perfectly in sublime regex too... Thanks
  • HerrimanCoder
    HerrimanCoder over 6 years
    Example usage please?
  • roman
    roman over 5 years
    This is a nice solution thanks. I am trying to modify it to catch comments that are spread across multiple lines. Any ideas on that? Worth a new question?
  • Mike Samuel
    Mike Samuel over 5 years
    @roman, It should work for comments that contain newlines. Do you mean something different?
  • mickmackusa
    mickmackusa almost 3 years
    Please explain why you are using the m pattern modifier.