symlink
symlink

Reputation: 12209

Regex to select CSS declaration blocks that don't have certain color values

Let's say I have the following CSS. I need a regular expression that grabs entire declaration blocks (selector, opening bracket, contents, closing bracket) that do not have the folowing colors in them: #f518e2 and #22f1e6

.foo{
  color: #f518e2;
  box-spacing: border-box;
}

#bar{
  color: #e4e4e4;
  box-spacing: border-box;
}

biz.baz{
  background: #22f1e6;
  font-size: 30px;
}

So in this case, I would want to grab:

#bar{
  color: #e4e4e4;
  box-spacing: border-box;
}

Assumptions:

#bar{   color: #e4e4e4;   
box-spacing: border-box; }biz.baz
{   
  background: #22f1e6;   
font-size: 30px; }

I've tried something like this: /\w*[#.]?\w+\s*{[^\{\}]+}/g which selects entire declaration blocks, but when it comes to weeding out the ones with #f518e2 and #22f1e6 it's a little beyond what I know about regex.

Upvotes: 1

Views: 122

Answers (2)

The fourth bird
The fourth bird

Reputation: 163277

One option could be to match all lines where { is at the start of the line, and } at the end of the line and match all lines in between that do not have either #f518e2 or #22f1e6 using a negative lookahead.

Note that this pattern works for the given examples, but does not take any nesting or data structure into account.

^.*{(?:\r?\n(?!.*#(?:f518e2|22f1e6)\b).*)*\r?\n}

The pattern matches:

  • ^ Start of string
  • .*{ Match the whole line and { at the end
  • (?: Non capture group
    • \r?\n Match a newline
    • (?!.*#(?:f518e2|22f1e6)\b).* Assert that the line does not contain the colors and match the whole line
  • )* Close the non capture group and repeat 0+ times
  • \r?\n Match a newline
  • } Match {

Regex demo

Upvotes: 1

StackSlave
StackSlave

Reputation: 10627

You probably want to use JavaScript to do such a task. I've including an example for your studies:

//<[CDATA[
/* js/external.js */
function hexNum(x){
  switch(x.toUpperCase()){
    case 'A':
      return 10;
    case 'B':
      return 11;
    case 'C':
      return 12;
    case 'D':
      return 13;
    case 'E':
      return 14;
    case 'F':
      return 15;
    default:
      return +x;
  }
}
function hexToRGBArray(hex){
  let s, r = [];
  if(hex.match(/^#?[\dA-F]{6}$/i)){
    s = hex.replace(/^#/, '').split('');
    for(let i=0,q=1; i<6; i+=2,q+=2){
      r.push(hexNum(s[i])*16+hexNum(s[q]));
    }
  }
  else if(hex.match(/^#?[\dA-F]{3}/i)){
    s = hex.replace(/^#/, '').split('');
    for(let i=0,h; i<3; i++){
      h = hexNum(s[i]); r.push(h*16+h);
    }
  }
  else{
    throw new Error('invalid hexadecimal');
  }
  return r;
}
addEventListener('load', ()=>{
// stack overflow is including nodes you won't see in real world
const all = document.body.querySelectorAll('*');
const avoid1 = hexToRGBArray('#f518e2').join(), avoid2 = hexToRGBArray('#22f1e6').join();
let s;
for(let n of all){ // n being node
  s = getComputedStyle(n).color.replace(/^.+\(|\).*$/g, '').split(/\s*,\s*/).join();
  if(s !== avoid1 && s !== avoid2){
    console.log(n.textContent);
    // n is node
  }
}
}); // end load
//]]>
/* css/external.css */
div{
  color:#ab7722;
}
#test1{
  color:#f518e2;
}
#test4{
   color:#22f1e6;
}
<!DOCTYPE html>
<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>
  <head>
    <meta charset='UTF-8' /><meta name='viewport' content='width=device-width, height=device-height, initial-scale:1, user-scalable=no' />
    <title>Title Here</title>
    <link type='text/css' rel='stylesheet' href='css/external.css' />
    <script src='js/external.js'></script>
  </head>
<body>
  <div id='test1'>nope</div>
  <div id='test2'>got this one</div>
  <div id='test3'>and this one</div>
  <div id='test4'>not this one</div>
</body>
</html>

Note that in the real world document.body would only access that which is in the body. Stack Overflow must have something else in mind. They should fix it.

Upvotes: 1

Related Questions