4

Say I insert a rule like this:

var style = document.createElement("style")
style.appendChild(document.createTextNode(""))
head.appendChild(style)

// done multiple times throughout the code
var index = style.sheet.insertRule(".myClass { color: red; }", style.sheet.cssRules.length)
var myRule = style.sheet.cssRules[index]

The style.sheet.deleteRule function expects an index, and when I delete a rule, all the indexes will change. Therefore, just storing the rules' index, wont suffice.

How can I delete a certain myRule at any given time without looping through all the rules of the style.sheet.cssRules?

user237948
  • 121
  • 6

2 Answers2

1

First the index of a rule needs to be searched for, so here is a function I've created, named getStyleRuleIndexBySelector which can look at a certain stylesheet and return the indexes of the matching rules.

// create a dummy stylesheet which we'll search a key by value
var style = document.createElement("style")
style.appendChild(document.createTextNode(""))
document.head.appendChild(style);

// insert some rules
style.sheet.insertRule('.first{ color:red }', style.sheet.cssRules.length);
style.sheet.insertRule('.second{ color:green }', style.sheet.cssRules.length);
style.sheet.insertRule('div span a{ color:blue }', style.sheet.cssRules.length);
style.sheet.insertRule('.second{ display:none; left:1px; }', style.sheet.cssRules.length);

// get the rules
var rules = style.sheet.cssRules;

// print rules
console.log("Stylesheet rules: ", Array.from(rules).map(item => item.cssText ) );

function getStyleRuleIndexBySelector(rules, selector, prop){
  var result = [], i,
      value = (prop ? selector + "{" + prop + "}" : selector).replace(/\s/g, ''), // remove whitespaces
      s = prop ? "cssText" : "selectorText";
  
  for( i=0; i < rules.length; i++ )
    if( rules[i][s].replace(/\s/g, '') == value)
      result.push(i);
      
  return result;
}

console.log( "Rules's indexes with selector '.second':", getStyleRuleIndexBySelector(rules, '.second' )  );
console.log( "Rule index by specific selector & props:", getStyleRuleIndexBySelector(rules, '.second', 'display:none; left:1px;')  );

// delete a certain rule (by specificly stating the Selector & its properties
style.sheet.deleteRule( getStyleRuleIndexBySelector(rules, '.second', 'display:none; left:1px;')[0] );

// print the rules
console.log("Stylesheet rules after removal: ", Array.from(rules).map(item => item.cssText ) );

This is related to another answer of mine for this question: Get index of a CSS rule by name

Another related answer of mine: Changing pseudo-element style from javascript

vsync
  • 118,978
  • 58
  • 307
  • 400
1

Create a ruleIndexTracker Array which will serve as a key, translating the original index of the inserted rule to that rule's current index.

eg.

var ruleIndexTracker = [

    0,
    1,
    2,
    3,
    4
];

Every time you add a new rule to the style.sheet, add a value to ruleIndexTracker.

var nextIndex = (ruleIndexTracker[(ruleIndexTracker.length - 1)] + 1);
ruleIndexTracker.push(nextIndex);

So if you add a sixth and a seventh rule, you will get:

var ruleIndexTracker = [

    0,
    1,
    2,
    3,
    4,
    5,
    6
];

You can see that you will always have an array with exactly the same number of entries as rules you have added to style.sheet (regardless of whether those rules are still present or whether they have been subsequently deleted).

If you want to remove a rule, run the following function:

function deleteRule(originalIndex) {

    ruleIndexTracker[originalIndex] = -1;

    for (let i = (originalIndex + 1); i < ruleIndexTracker.length; i++) {

        ruleIndexTracker[i] = (ruleIndexTracker[i] - 1);
    }
}

If you now wish to remove the fourth rule you added (which always corresponds to ruleIndexTracker[3] then running the function deleteRule(3) above will result in the following:

var ruleIndexTracker = [

    0,
    1,
    2,
   -1,
    3,
    4,
    5
];

Every time you need to delete a rule from the style.sheet, you will always be able to find that rule using the following:

ruleIndexTracker[originalIndex]

This will always reveal the current index of that rule in style.sheet.

Rounin
  • 27,134
  • 9
  • 83
  • 108