Your first regex does indeed match nested parentheses (but not escaped parentheses). Is that desirable?
Without nesting or escaping, these become much simpler.
Here's a variant of your first regex that ignores nesting possibilities:
counter\([^)]*\)
It matches a literal counter(
and then zero or more non-close-parentheses, then finally a close parenthesis. (Full explanations of your first regex and my simpler version at regex101.)
I believe that answers your first question, though if you're literally looking for a "regex to match [a] single parenthesis," that's just [()]
, which will match either an open or a close parenthesis character. You could alternatively explicitly match \(
or \)
if you know which one you want to match.
Matching quotes (without regard to nesting or escaped quotes) is similarly easy:
"[^"]*"
This matches a literal double quote character ("
), then zero or more non-doublequote characters, then another literal double quote character.
Your second request was for a "single regex that will match the counter, attribute , url and string content in the given order (order is important because i want to replace them later with evaluated values)."
I'm not sure how you intend to get the CSS content
property's value, given how that's typically in an ::after
or ::before
pseudo-class, which are not available from the DOM, but here's some dummy code populating it so we can manipulate it:
var css = `content: counter(item)" " attr(data) "" counter(item1,decimal) url('test.jpeg') "hi" attr(xyz); color:red;`;
// harvest last `content` property (this is tricked by `content: "content: blah"`)
var content = css.match(/.*\bcontent:\s*([^;"']*(?:"[^"]*"[^;"']*|'[^']*'[^;"']*)*)/);
if (content) {
var part_re = /(?:"([^"]*)"|'([^']*)'|(?:counter|attr|url)\(([^)]*)\))/g;
while ( part = part_re.exec(content[1]) ) { // parse on just the value
if (part[0].match(/^"/)) { /* do stuff to part[1] */ }
else if (part[0].match(/^'/)) { /* do stuff to part[2] */ }
else if (part[0].match(/^counter/)) { /* do stuff to part[3] */ }
else if (part[0].match(/^attr/)) { /* do stuff to part[3] */ }
else if (part[0].match(/^url/)) { /* do stuff to part[3] */ }
// silently skips other values, like `open-quote` or `counters(name, string)`
}
}
The first regex (line 4) extracts the last content
property from the CSS (last because it'll override previous instances, though note the fact that this'll stupidly extract content: blah
from content: "content: blah"
). After finding the last instance of a word break and then content:
, it absorbs any whitespace and then matches the rest of the line until a semicolon, double quote, or single quote. A non-capture group allows for any content between double quotes or a single quote, much in the same way we matched quotes near the top of this answer. (Full explanation of this CSS content regex at regex101.)
The second regex (line 7, assigned to part_re
) is in a while loop so we can work on each individual value in the content
property in order. It matches double-quoted strings or single-quoted strings or certain named values (counter or attr or url). See the conditionals and comments for where the values' data are stored. Full explanation of this value parsing regex at regex101 (see "Match Information" in the middle of the right column to see how I'm storing the values' data).