skip to Main Content

Abstract

I’m trying to figure out a way to use JavaScript to dynamically create CSS rules on a page, modify them, and disable them. The rules would come in "packages" with an ID and a group of one or more CSS rules which may overlap.

Example and Data

/*ID: something*/
div#foobar {display:none;}

/*ID: blah*/
input {background:red;}
div.password {width:100px;}

/*ID: test*/
div.password {width:200px;}

The rules are in an associative-array that contains the data, such as:

myrules["something"] = "div#foobar {display:none;}";
myrules["blah"]      = "div.password {width:100px;} input {background:red;}";
myrules["test"]      = "div.password {width:200px;}";

“Question”

Now I need a way to add the defined rules to the page with a way to toggle them using the IDs.

Requirements

The main issues the current attempts (below) have run into are:

  • Adding CSS rules via CSS syntax (ie, not JavaScript object names, so background-color, not .backgroundColor)
  • Being able to enable and disable the rules
  • Being able to access the rules via a string ID, not a numeric index
  • The rules should be bunched in a single stylesheet object or element since there can be a LOT of rules, so creating a separate stylesheet element for each would be impractical

Attempts (what I’ve already tried)

I’ve looked at several different ways from document.styleSheets to .sheet.cssRules[], from .innerHTML to insertRule(). I’ve become dizzy from trying to figure out what’s what; it’s such a quagmire, with poor examples. Sometimes I manage to use one technique to accomplish one aspect of it, but then another aspect won’t work. I can’t find a solution that satisfies all of the aforementioned requirements.

And searches are difficult because of the ambiguous nature of phrasing leading to incorrect search-results.

Surely there has to be an efficient way to do this, right? 🤨

2

Answers


  1. You could create a <style> element inside the <head> tag and change its content as needed. There may be other ways to do it but, if I understand your requirements correctly, below example may help:

    // Your CSS rules
    let myRules = {
      something: "div#foobar {display:none;}",
      blah: "div.password {width:100px;} input {background:red;}",
      test: "div.password {width:200px;}"
    }
    
    // Create the style element
    let style = document.createElement("style")
    // Give it a unique id in case there are other style elements
    style.id = "myStyle"
    // Append the created style element to <head>
    document.head.appendChild(style)
    
    // Example function to change the contents of the new style tag by passing the needed style values as arguments
    function changeStyle(...args) {
      const myStyle = document.head.querySelector('style#myStyle')
      
      myStyle.innerText = args
    }
    
    document.getElementById("changeStyleButton1").addEventListener('click', () => {
      changeStyle(myRules["test"])
    })
    
    document.getElementById("changeStyleButton2").addEventListener('click', () => {
      changeStyle(myRules["blah"])
    })
    
    document.getElementById("changeStyleButton3").addEventListener('click', () => {
      changeStyle(myRules["something"] + myRules["test"])
    })
    
    changeStyle(myRules["something"] + myRules["blah"])
    <html>
    <head>
    </head>
    <body>
    
    <div id="foobar">foobar div</div>
    <div class="password">
      <input type="password">
    </div>
    <p>
    <button id="changeStyleButton1">Change Style 1</button>
    <button id="changeStyleButton2">Change Style 2</button>
    <button id="changeStyleButton3">Change Style 2</button>
    </body>
    </html>
    Login or Signup to reply.
  2. It really depends on how you want to manage the CSS rules.

    Here’s an example (see the toggleRule function):

    const allRules =
    {
      'myCustomRule': '#CustomSection .CustomTitle { text-decoration: underline; }',
    };
    
    const myRules = document.createElement('style');
    {
      myRules.type = 'text/css';
      myRules.id = 'myRules';
      
      document.head.appendChild(myRules);
    }
    
    function toggleRule ( ruleText )
    {
      let found = false;
      
      for ( const sheet of document.styleSheets )
      {
        for ( const ruleIndex in sheet.cssRules )
        {
          const rule = sheet.cssRules[ruleIndex];
          
          if ( rule.cssText == ruleText )
          {
            found = true;
            sheet.removeRule(ruleIndex);
          }
        }
      }
      
      if ( ! found )
      {
        Array.from(document.styleSheets).at(-1).insertRule(ruleText);
      }
    }
    
    ///// TESTS \\
    document.querySelector('#CustomSection').addEventListener
    (
      'click', clickEvent =>
      {
        toggleRule(allRules['myCustomRule']);
      }
    );
    #CustomSection
    {
      background-color: #EEE;
      margin: 0;
      padding: 5px;
    }
    
    #CustomSection span
    {
      font-size: 18px;
    }
    
    #CustomSection .CustomTitle
    {
      color: #00C;
    }
    <div id="CustomSection">
      Click to: <span class="CustomTitle">Toggle Underline</span>
    </div>

    The function basically verify if there are any rules that match the given text. If they match, then the rule is excluded. If there is no matching rule, then the rule is inserted in the last style sheet (which should be the appended myRules). Which should make the rule override any other rule set before.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search