How To Get All Custom Properties On A Page In

[ad_1]

We’re in a position to use JavaScript to get the value of a CSS {{custom}} property. Robin wrote up a detailed rationalization about this in Get a CSS Custom Property Value with JavaScript. To evaluation, let’s say we’ve declared a single {{custom}} property on the HTML element:

html {
  --color-accent: #00eb9b;
}

In JavaScript, we are going to entry the value with getComputedStyle and getPropertyValue:

const colorAccent = getComputedStyle(doc.documentElement)
  .getPropertyValue('--color-accent'); // #00eb9b

Good. Now we have got entry to our accent coloration in JavaScript. You notice what’s cool? If we alter that coloration in CSS, it updates in JavaScript as correctly! Helpful.

What happens, though, when it’s not just one property we wish entry to in JavaScript, nonetheless a complete bunch of them?

html {
  --color-accent: #00eb9b;
  --color-accent-secondary: #9db4ff;
  --color-accent-tertiary: #f2c0ea;
  --color-text: #292929;
  --color-divider: #d7d7d7;
}

We discover your self with JavaScript that seems like this:

const colorAccent = getComputedStyle(doc.documentElement).getPropertyValue('--color-accent'); // #00eb9b
const colorAccentSecondary = getComputedStyle(doc.documentElement).getPropertyValue('--color-accent-secondary'); // #9db4ff
const colorAccentTertiary = getComputedStyle(doc.documentElement).getPropertyValue('--color-accent-tertiary'); // #f2c0ea
const colorText = getComputedStyle(doc.documentElement).getPropertyValue('--color-text'); // #292929
const colorDivider = getComputedStyle(doc.documentElement).getPropertyValue('--color-text'); // #d7d7d7

We’re repeating ourselves a lot. We’d shorten each thought of one in all these strains by abstracting the widespread duties to a function.

const getCSSProp = (element, propName) => getComputedStyle(element).getPropertyValue(propName);
const colorAccent = getCSSProp(doc.documentElement, '--color-accent'); // #00eb9b
// repeat for each {{custom}} property...

That helps in the reduction of code repetition, nonetheless we nonetheless have a less-than-ideal state of affairs. Every time we add a {{custom}} property in CSS, we have got to write one different line of JavaScript to entry it. This will and does work good if we solely have a few {{custom}} properties. I’ve used this setup on manufacturing duties sooner than. Nonetheless, it’s moreover doable to automate this.

Let’s stroll through the tactic of automating it by making a working issue.

What are we making?

We’ll make a coloration palette, which is a widespread operate in pattern libraries. We’ll generate a grid of coloration swatches from our CSS {{custom}} properties. 

Proper right here’s the complete demo that we’ll assemble step-by-step.

A preview of our CSS custom property-driven color palette. Showing six cards, one for each color, including the custom property name and hex value in each card.
Proper right here’s what we’re aiming for.

Let’s set the stage. We’ll use an unordered report to present our palette. Each swatch is a <li> element that we’ll render with JavaScript. 

<ul class="colors"></ul>

The CSS for the grid format isn’t pertinent to the tactic in this submit, so we obtained’t have a have a look at in ingredient. It’s accessible in the CodePen demo.

Now that we have our HTML and CSS in place, we’ll focus on the JavaScript. Proper right here’s an summary of what we’ll do with our code:

  1. Get all stylesheets on a net web page, every exterior and inside
  2. Discard any stylesheets hosted on third-party domains
  3. Get all tips for the remaining stylesheets
  4. Discard any tips that aren’t basic sort tips
  5. Get the title and value of all CSS properties
  6. Discard non-custom CSS properties
  7. Assemble HTML to present the color swatches

Let’s get to it.

Step 1: Get all stylesheets on a net web page

The very very first thing we wish to do is get all exterior and inside stylesheets on the current net web page. Stylesheets could be discovered as members of the worldwide doc.

doc.styleSheets

That returns an array-like object. We want to use array methods, so we’ll convert it to an array. Let’s moreover put this in a function that we’ll use all by way of this submit.

const getCSSCustomPropIndex = () => [...document.styleSheets];

As soon as we invoke getCSSCustomPropIndex, we see an array of CSSStyleSheet objects, one for each exterior and inside stylesheet on the current net web page.

The output of getCSSCustomPropIndex, an array of CSSStyleSheet objects

Step 2: Discard third-party stylesheets

If our script is working on https://occasion.com any stylesheet we wish to look at ought to even be on https://occasion.com. That’s a security operate. From the MDN docs for CSSStyleSheet:

In some browsers, if a stylesheet is loaded from a utterly completely different space, accessing cssRules outcomes in SecurityError.

That signifies that if the current net web page hyperlinks to a stylesheet hosted on https://some-cdn.com, we are going to’t get {{custom}} properties — or any sorts — from it. The technique we’re taking proper right here solely works for stylesheets hosted on the current space.

CSSStyleSheet objects have an href property. Its value is the entire URL to the stylesheet, like https://occasion.com/sorts.css. Inside stylesheets have an href property, nonetheless the value is perhaps null.

Let’s write a function that discards third-party stylesheets. We’ll do that by evaluating the stylesheet’s href value to the current location.origin.

const isSameDomain = (styleSheet) => {
  if (!styleSheet.href) {
    return true;
  }


  return styleSheet.href.indexOf(window.location.origin) === 0;
};

Now we use isSameDomain as a filter ondoc.styleSheets.

const getCSSCustomPropIndex = () => [...document.styleSheets]
  .filter(isSameDomain);

With the third-party stylesheets discarded, we are going to look at the contents of those remaining.

Step 3: Get all tips for the remaining stylesheets

Our goal for getCSSCustomPropIndex is to produce an array of arrays. To get there, we’ll use a combination of array methods to loop through, uncover values we wish, and blend them. Let’s take a first step in that path by producing an array containing every sort rule.

const getCSSCustomPropIndex = () => [...document.styleSheets]
  .filter(isSameDomain)
  .in the reduction of((finalArr, sheet) => finalArr.concat(...sheet.cssRules), []);

We use in the reduction of and concat as a results of we wish to produce a flat array the place every first-level element is what we’re in. On this snippet, we iterate over explicit particular person CSSStyleSheet objects. For each thought of one in all them, we wish its cssRules. From the MDN docs:

The read-only CSSStyleSheet property cssRules returns a reside CSSRuleList which provides a real-time, up-to-date report of every CSS rule which incorporates the stylesheet. Each merchandise in the report is a CSSRule defining a single rule.

Each CSS rule is the selector, braces, and property declarations. We use the unfold operator ...sheet.cssRules to take every rule out of the cssRules object and place it in finalArr. As soon as we log the output of getCSSCustomPropIndex, we get a single-level array of CSSRule objects.

Example output of getCSSCustomPropIndex producing an array of CSSRule objects

This gives us all the CSS tips for all the stylesheets. We want to discard a few of those, so let’s switch on.

Step 4: Discard any tips that aren’t basic sort tips

CSS tips come in differing kinds. CSS specs define each of the classes with a mounted title and integer. The commonest sort of rule is the CSSStyleRule. One different sort of rule is the CSSMediaRule. We use these to define media queries, like @media (min-width: 400px) {}. Different types embrace CSSSupportsRule, CSSFontFaceRule, and CSSKeyframesRule. See the Type constants section of the MDN docs for CSSRule for the entire report.

We’re solely in tips the place we define {{custom}} properties and, for the wants in this submit, we’ll focus on CSSStyleRule. That does omit the CSSMediaRule rule form the place it’s reputable to define {{custom}} properties. We’d use an technique that’s associated to what we’re using to extract {{custom}} properties in this demo, nonetheless we’ll exclude this explicit rule form to limit the scope of the demo.

To slender our focus to sort tips, we’ll write one different array filter:

const isStyleRule = (rule) => rule.form === 1;

Every CSSRule has a form property that returns the integer for that sort mounted. We use isStyleRule to filter sheet.cssRules.

const getCSSCustomPropIndex = () => [...document.styleSheets]
  .filter(isSameDomain)
  .in the reduction of((finalArr, sheet) => finalArr.concat(
    [...sheet.cssRules].filter(isStyleRule)
  ), []);

One issue to remember is that we’re wrapping ...sheet.cssRules in brackets so we are going to use the array approach filter.

Our stylesheet solely had CSSStyleRules so the demo outcomes are the similar as sooner than. If our stylesheet had media queries or font-face declarations, isStyleRule would discard them.

Step 5: Get the title and value of all properties

Now that we have the foundations we wish, we are going to get the properties that make them up. CSSStyleRule objects have a sort property that is a CSSStyleDeclaration object. It’s made up of regular CSS properties, like coloration, font-family, and border-radius, plus {{custom}} properties. Let’s add that to our getCSSCustomPropIndex function in order that it seems at every rule, establishing an array of arrays alongside the best way in which:

const getCSSCustomPropIndex = () => [...document.styleSheets]
  .filter(isSameDomain)
  .in the reduction of((finalArr, sheet) => finalArr.concat(
    [...sheet.cssRules]
      .filter(isStyleRule)
      .in the reduction of((propValArr, rule) => {
        const props = []; /* TODO: further work wished proper right here */
        return [...propValArr, ...props];
      }, [])
  ), []);

If we invoke this now, we get an empty array. Now we have now further work to do, nonetheless this lays the muse. As a results of we wish to end up with an array, we start with an empty array by using the accumulator, which is the second parameter of in the reduction of. Throughout the physique of the in the reduction of callback function, we have got a placeholder variable, props, the place we’ll gather the properties. The return assertion combines the array from the sooner iteration — the accumulator — with the current props array.

Correct now, every are empty arrays. We would like to use rule.sort to populate props with an array for every property/value in the current rule:

const getCSSCustomPropIndex = () => [...document.styleSheets]
  .filter(isSameDomain)
  .in the reduction of((finalArr, sheet) => finalArr.concat(
    [...sheet.cssRules]
      .filter(isStyleRule)
      .in the reduction of((propValArr, rule) => {
        const props = [...rule.style].map((propName) => [
          propName.trim(),
          rule.style.getPropertyValue(propName).trim()
        ]);
        return [...propValArr, ...props];
      }, [])
  ), []);

rule.sort is array-like, so we use the unfold operator as soon as extra to put each member of it into an array that we loop over with map. Throughout the map callback, we return an array with two members. The first member is propName (which contains coloration, font-family, --color-accent, and so on.). The second member is the value of each property. To get that, we use the getPropertyValue strategy of CSSStyleDeclaration. It takes a single parameter, the string title of the CSS property. 

We use trim on every the title and value to make certain that we don’t embrace any important or trailing whitespace that typically will get left behind.

Now after we invoke getCSSCustomPropIndex, we get an array of arrays. Every child array accommodates a CSS property title and a value.

Output of getCSSCustomPropIndex showing an array of arrays containing every property name and value

That’s what we’re on the lookout for! Successfully, nearly. We’re getting every property in addition to {{custom}} properties. We would like one other filter to take away these regular properties as a results of all we wish are the {{custom}} properties.

Step 6: Discard non-custom properties

To seek out out if a property is {{custom}}, we may have a have a look at the title. Everyone knows {{custom}} properties ought to start with two dashes (--). That’s distinctive in the CSS world, so we are going to use that to write a filter function:

([propName]) => propName.indexOf("--") === 0)

Then we use it as a filter on the props array:

const getCSSCustomPropIndex = () =>
  [...document.styleSheets].filter(isSameDomain).in the reduction of(
    (finalArr, sheet) =>
      finalArr.concat(
        [...sheet.cssRules].filter(isStyleRule).in the reduction of((propValArr, rule) => {
          const props = [...rule.style]
            .map((propName) => [
              propName.trim(),
              rule.style.getPropertyValue(propName).trim()
            ])
            .filter(([propName]) => propName.indexOf("--") === 0);


          return [...propValArr, ...props];
        }, [])
      ),
    []
  );

Throughout the function signature, we have got ([propName]). There, we’re using array destructuring to entry the first member of every child array in props. From there, we do an indexOf take a look at on the title of the property. If -- is not going to be initially of the prop title, then we don’t embrace it in the props array.

As soon as we log the consequence, we have got the exact output we’re on the lookout for: An array of arrays for every {{custom}} property and its value with no completely different properties.

The output of getCSSCustomPropIndex showing an array of arrays containing every custom property and its value

Wanting further in direction of the long run, creating the property/value map doesn’t have to require a lot code. There’s an alternate in the CSS Typed Object Model Level 1 draft that makes use of CSSStyleRule.styleMap. The styleMap property is an array-like object of every property/value of a CSS rule. We don’t have it however, nonetheless If we did, we’d shorten our above code by eradicating the map:

// ...
const props = [...rule.styleMap.entries()].filter(/*comparable filter*/);
// ...

On the time of this writing, Chrome and Edge have implementations of styleMap nonetheless no completely different important browsers do. As a results of styleMap is in a draft, there’s no guarantee that we’ll actually get it, and there’s no sense using it for this demo. Nonetheless, it’s pleasurable to perceive it’s a future probability!

Now we have now the data building we wish. Now let’s use the data to present coloration swatches.

Step 7: Assemble HTML to present the color swatches

Getting the data into the exact type we wished was the exhausting work. We would like one other little little bit of JavaScript to render our beautiful coloration swatches. As a substitute of logging the output of getCSSCustomPropIndex, let’s retailer it in variable.

const cssCustomPropIndex = getCSSCustomPropIndex();

Proper right here’s the HTML we used to create our coloration swatch at first of this submit:

<ul class="colors"></ul>

We’ll use innerHTML to populate that report with a report merchandise for each coloration:

doc.querySelector(".colors").innerHTML = cssCustomPropIndex.in the reduction of(
  (str, [prop, val]) => `${str}<li class="coloration">
    <b class="color__swatch" sort="--color: ${val}"></b>
    <div class="color__details">
      <enter value="${prop}" readonly />
      <enter value="${val}" readonly />
    </div>
   </li>`,
  "");

We use in the reduction of to iterate over the {{custom}} prop index and assemble a single HTML-looking string for innerHTML. Nonetheless in the reduction of isn’t the one means to try this. We’d use a map and be a a part of or forEach. Any strategy of establishing the string will work proper right here. That’s merely my hottest means to do it.

I would love to highlight a couple explicit bits of code. Throughout the in the reduction of callback signature, we’re using array destructuring as soon as extra with [prop, val], this time to entry every members of each child array. We then use the prop and val variables in the physique of the function.

To point the occasion of each coloration, we use a b element with an inline sort:

<b class="color__swatch" sort="--color: ${val}"></b>

That means we discover your self with HTML that seems like:

<b class="color__swatch" sort="--color: #00eb9b"></b>

Nonetheless how does that set a background coloration? Throughout the full CSS we use the {{custom}} property --color as the value of  background-color for each .color__swatch. As a results of exterior CSS tips inherit from inline sorts, --color  is the value we set on the b element.

.color__swatch {
  background-color: var(--color);
  /* completely different properties */
}

We now have an HTML present of coloration swatches representing our CSS {{custom}} properties!


This demo focuses on colors, nonetheless the tactic isn’t restricted to {{custom}} coloration props. There’s no function we couldn’t broaden this technique to generate completely different sections of a pattern library, like fonts, spacing, grid settings, and so on. One thing that’s seemingly to be saved as a {{custom}} property could possibly be displayed on a net web page routinely using this technique.

[ad_2]

Source link

LEAVE A REPLY

Please enter your comment!
Please enter your name here