1

I'm trying to code a regular expression that will subtract a class name out of a CSS rule (through a capturing group), if the CSS rule contains certain property.

For instance:

var test = `
    .myUglyClass {
        propOne: abc;
        notSpecialProp: def;
        propThree: ghi;
    }
    .myClass {
        propOne: abc;
        specialProp: def;
        propThree: ghi;
    }
`;

If .myClass has property specialProp (in this example, that is the case), then subtract .myClass from the CSS rule structure, but only the class name of the class that contains the property. I have tried this, which uses a positive lookahead:

test.match(/(\.\w*)(?=\s*{[^]+specialProp:[^]+})/);

It kind of works, but if the property specialProp is contained within any class, it will match all class names.

How can I make it so that it only matches the class name that contains the special property, when there are other class names on the string? I guess I could think of another way of achieving the same thing, but I feel I'm kind of close to the regex solution.

Thank you.

3 Answers3

0

If you replace \s*.* with [\s\S]* you will get a multiline match.

But in general, this is not a very good approach. What if that property name comes in the class rules as part of some other text? For example:

var test = `
    .myClass {
        background: http://example.com/specialProp:/image.gif;
    }

    .secondClass {
        content: 'specialProp:';
    }
`;

Regex is too low level to for this kind of checks.

Also, multiline regex does not know when one class body ends and the next one starts. And finding that boundary with regex is non-trivial.

If you want to parse CSS better look at an existing the JavaScript CSS parser or use CSS DOM if you want to query CSS already parsed by the browser.

Community
  • 1
  • 1
Illia Bobyr
  • 730
  • 6
  • 16
  • Hmm, well, I can't use the native CSS DOM interpretation of the stylesheet, since what I'm trying to do is write a polyfill which will detect what classes in a stylesheet have an X unsupported property, and then try to render that property accordingly, through JavaScript. I can't do that since browsers discard CSS properties that they do not support, so they don't appear in the DOM interpretation of the CSS. I guess I can take a look at the JavaScript parsers, but I really want to do this myself. –  Sep 26 '15 at 02:42
  • I'm thinking that maybe another approach would be to get the indexes of where the property lies in the raw CSS text, and write some kind of function that explores the characters around those indexes, till they get to the class name. What do you think? Perhaps I'm just over-complicating things. The problem of the word being in some other string that is not a property still remains, though, like you pointed out. –  Sep 26 '15 at 02:44
  • What exactly is the difference between "myself" and using an existing parser? Except, of cause, that you would end up writing a parser yourself ;) – Illia Bobyr Sep 26 '15 at 02:44
  • CSS is a rather complicated language. Regex is not expressible enough to parse it. The only way to cover all the possible cases is to write a proper parser. Considering there are already parsers available, why waste time? – Illia Bobyr Sep 26 '15 at 02:45
  • Haha, I just find it fun. I'm not really a professional coder, and just want to crack this problem on my free time. But yeah, I don't know much about parsers, I've looked at JSLint, and understand some of it, but I guess that's still way beyond my level. I also wonder how `prefix-free` does it (it adds prefixes to non-prefixed properties), it certainly doesn't contain a parser, but I can't really understand all of the code very well either. Maybe I'll end up using an existing parser and continue on with the easier stuff, if my head starts aching. Ha, thanks. –  Sep 26 '15 at 02:48
  • Here is an article that starts with almost the same regex you asked about: https://medium.com/jotform-form-builder/writing-a-css-parser-in-javascript-3ecaa1719a43 It ends with a reference to a github with an implementation having almost 600 lines. And that might be not the most feature reach implementation. – Illia Bobyr Sep 26 '15 at 02:50
  • That's crazy. I'll use that only if I can't achieve it myself, in a few days. Maybe I can write something smaller since what I'm trying to do is more specific. Thank for the link, it is very useful. I'll mark this as the answer. –  Sep 26 '15 at 02:56
0

You could use CSS feature detection

@supports <condition> { /* rules */ }

For example:

@supports (text-shadow: 0 0 5px #000) { .blur-text { color:
 transparent; text-shadow: 0 0 5px #000; } }

Ref: http://www.sitepoint.com/supports-native-css-feature-detection/

It isn't supported across all browsers, though: ref: http://caniuse.com/#search=%40support

My suggestion is to use a CSS preprocessor like Stylus or less.

user2182349
  • 9,569
  • 3
  • 29
  • 41
0

To match a CSS rule containing property:, use the ? modifier to make a shortest match, {[^}]*?, before the closing brace:

/\.\w+\s*{[^}]*?property:[^}]*?}/

More generally:

new RegExp('\\.\\w+\\s*{[^}]*?' + property + ':[^}]*?}');

The following snippet demonstrates this approach:

function removeClassWithProperty(property, text) {
  var re = new RegExp('\\.\\w+\\s*{[^}]*?' + property + ':[^}]*?}');
  return text.replace(re, '');
}

function print(s) {
  document.write(s + '<br><br>');
}

var test = `
    .myUglyClass {
        propOne: abc;
        notSpecialProp: def;
        propThree: ghi;
    }
    .myClass {
        propOne: abc;
        specialProp: def;
        propThree: ghi;
    }
`;

print(removeClassWithProperty('fakeProperty', test));

print(removeClassWithProperty('specialProp', test));

print(removeClassWithProperty('propThree', test));
body {
  font-family: sans-serif;
}
Michael Laszlo
  • 12,009
  • 2
  • 29
  • 47
  • Hmm, one problem still remains, from what I can see: if the property name is part of any string, it will still match the class. For instance, if you want to match `test`, and the class has a string that contains `test` anywhere in between the brackets, such as `dfgdftesto`, it will still match the class. http://regexr.com/3bs9a –  Sep 26 '15 at 03:07
  • That's true. You can add a colon to reduce the likelihood of a false match: `/\.\w+\s*{[^}]*?property:[^}]*?}/` In theory you can still have `property:` inside a `url()` or whatever. If you want a foolproof solution, you'll need a context-free grammar. I'm showing you what's possible with regexes. – Michael Laszlo Sep 26 '15 at 03:14