1

I have this line in my loop:

var regex1 = new RegExp('' + myClass + '[:*].*');
var rule1 = string.match(regex1)

Where "string" is a string of class selectors, for example: .hb-border-top:before, .hb-border-left

and "myClass" is a class: .hb-border-top

As I cycle through strings, i need to match strings that have "myClass" in them, including :before and :hover but not including things like hb-border-top2.

My idea for this regex is to match hb-border-top and then :* to match none or more colons and then the rest of the string.

I need to match:

.hb-fill-top::before .hb-fill-top:hover::before .hb-fill-top .hb-fill-top:hover

but the above returns only:

.hb-fill-top::before .hb-fill-top:hover::before .hb-fill-top:hover

and doesn't return .hb-fill-top itself.

So, it has to match .hb-fill-top itself and then anything that follows as long as it starts with :

EDIT:

Picture below: my strings are the contents of {selectorText}. A string is either a single class or a class with a pseudo element, or a rule with few clases in it, divided by commas.

each string that contains .hb-fill-top ONLY or .hb-fill-top: + something (hover, after, etc) has to be selected. Class is gonna be in variable "myClass" hence my issue as I can't be too precise.

enter image description here

Varin
  • 2,354
  • 2
  • 20
  • 37

3 Answers3

1

I understand you want to get any CSS selector name that contains the value anywhere inside and has EITHER : and 0+ chars up to the end of string OR finish right there.

Then, to get matches for the .hb-fill-top value you need a solution like

/\.hb-fill-top(?::.*)?$/

and the following JS code to make it all work:

var key = ".hb-fill-top";
var rx = RegExp(key.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') + "(?::.*)?$");
var ss = ["something.hb-fill-top::before","something2.hb-fill-top:hover::before","something3.hb-fill-top",".hb-fill-top:hover",".hb-fill-top2:hover",".hb-fill-top-2:hover",".hb-fill-top-bg-br"];
var res = ss.filter(x => rx.test(x));
console.log(res);

Note that .replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&') code is necessary to escape the . that is a special regex metacharacter that matches any char but a line break char. See Is there a RegExp.escape function in Javascript?.

The ^ matches the start of a string.

(?::.*)?$ will match:

  • (?::.*)?$ - an optional (due to the last ? quantifier that matches 1 or 0 occurrences of the quantified subpattern) sequence ((?:...)? is a non-capturing group) of a
    • : - a colon
    • .* - any 0+ chars other than line break chars
  • $ - end of the string.
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
  • I will check it when I'm back from work, thanks for the answer. As you can see in the edited question, I have a list of strings that I have to select. String is either 1 (selected) or 0 (skipped) based on this regex. So if the string matches the "key" or "key" with hover, after elements etc, then it will be selected. I'm building a function which basically takes my whole stylesheet, cycles through all the rules and returns only the rules that apply to the class I have put in the "key". It has to return all :hover, :before rules and other chained rules that include "key" in some way. – Varin Aug 04 '17 at 09:52
  • When you use `^` to mark start of the string here : `RegExp("^" + key.replace ....` does this not mean that string will only match if it starts with "key" ? Will it work if "key" is somewhere in the middle of the string? – Varin Aug 04 '17 at 09:56
  • @Varin: The `^` anchors the match at the start of the string. Remove if you do not need that behavior. I edited the JS snippet to `filter` out the non-matching values. – Wiktor Stribiżew Aug 04 '17 at 10:01
  • I downloaded my git repo at work, pasted your regex and it works! It works without the replace string which I don't think I need in this case... also, could you please explain `(?::.*)?$` in bit more detail, especially the `?$` part? I'm trying to understand regex more and this would really help! Thanks so much for your help Wiktor! – Varin Aug 04 '17 at 10:41
  • 1
    @Varin I added some more explanations. – Wiktor Stribiżew Aug 04 '17 at 10:44
  • Thanks. This is clear to me now. One more thing, the replace string is there to escape the dots inside the "key"? I thought that if you pass a variable inside RegExp, the special characters are already escaped, no? – Varin Aug 04 '17 at 11:19
  • @Varin That is not the case in any language. In Ruby, you can pass an array to `Regexp.union` that will create an alternation pattern with `|` operators while escaping each value. And that is the only auto-escaping method I know. – Wiktor Stribiżew Aug 04 '17 at 11:25
  • It works in my code but for some reason doesn't in the regex tester... weird: https://regex101.com/r/rj8XZR/2 – Varin Aug 04 '17 at 19:15
  • @Varin You are testing against a multiline string. `$` matches at the end of string by default in PCRE/JS/.NET.... anywhere but Ruby and text editors. So, you need to add `m` *MULTILINE* modifier. See [this demo](https://regex101.com/r/rj8XZR/3). Mind you need to escape the dot. – Wiktor Stribiżew Aug 04 '17 at 19:30
  • I see, I'll remember about the flags. Thank you for taking time to educate me! I have a lot to learn when it comes to regex. – Varin Aug 04 '17 at 19:35
0
var regex1 = new RegExp('\\' + myClass + '(?::[^\s]*)?');
var rule1 = string.match(regex1)

This regex select my class, and everething after if it start with : and stop when it meets a whitespace character.

See the regex in action.

Notice also that I added '\\' at the beginning. This is in order to escape the dot in your className. Otherwise it would have matched something else like

ahb-fill-top
.some-other-hb-fill-top

Also be careful about .* it may match something else after (I don't know your set of strings). You might want to be more precise with :{1,2}[\w-()]+ in the last group. So:

var regex1 = new RegExp('\\' + myClass + '(?::{1,2}[\w-()]+)?');
Ulysse BN
  • 10,116
  • 7
  • 54
  • 82
  • This also returns things like: `.hb-fill-top-rev-bg` and doesn't select the rest of the string that follows after `.hb-fill-top:` – Varin Aug 03 '17 at 23:37
  • Yes it does, see my last edit :) Be sure to detail more your question: for instance add regex that you **don't** want to match. Then our answers will be more appropriate for sure. – Ulysse BN Aug 03 '17 at 23:39
  • Thanks, but this returns error in the console : `Range out of order in character class` – Varin Aug 03 '17 at 23:43
  • Doesn't for me.. I'll need more context if you want I to help you – Ulysse BN Aug 03 '17 at 23:47
  • I edited my question with a picture of a few strings and a further explanation – Varin Aug 03 '17 at 23:55
  • Basically, needs to select every string that has: `class`, `.class:hover`, `.class::hover`, `.class:after`, `.class:hover::after` etc. and all we have is a variable with `class`. All it has to do in order to work for me, is to be able to select strings that have `class` on their own as well as `class:` (and the rest of the string that follows after `:`) but not `class-2` or `class2` etc. – Varin Aug 04 '17 at 00:08
  • Sorry I left yesterday, but here is the new regex. – Ulysse BN Aug 04 '17 at 14:06
0

var regex1 = new RegExp(`^\\${myClass}(:{1,2}\\w+)*$`)

var passes = [
    '.hb-fill-top::before',
    '.hb-fill-top:hover::before',
    '.hb-fill-top',
    '.hb-fill-top:hover',
    '.hb-fill-top::before',
    '.hb-fill-top:hover::before',
    '.hb-fill-top:hover'
];

var fails = ['.hb-fill-top-bg-br'];

var myClass = '.hb-fill-top';
var regex = new RegExp(`^\\${myClass}(:{1,2}\\w+)*$`);

passes.forEach(p => console.log(regex.test(p)));
console.log('---');
fails.forEach(f => console.log(regex.test(f)));
Jack
  • 804
  • 7
  • 18
  • Returns things like `.hb-fill-top-bg-br` and it should only return `hb-fill-top` and `hb-fill-top:[rest of the string]` – Varin Aug 03 '17 at 23:39
  • Updated. That string now fails and the 7 in your post pass. Can you try again? – Jack Aug 04 '17 at 00:02