1

Question / TLDR

How would I name a CSS property so that

  • it can be picked up by javascript (e.g. window.getComputedStyle(element), or jQuery.fn.css()) as the currently active style on an element, but
  • it has no effect on how content is displayed?
  • (EDIT: Ideally it should have good cross-browser support and feel natural / not too arbitrary.)

Background / use case

I am programming a responsive tabs / accordion component. The component should ship with js and css. But the break point should be defined in site-specific CSS, that is, a CSS file outside of the package.

The idea:

Site-specific CSS contains a rule like this:

@media only screen and (max-width: 768px) {
  // I want this CSS to feel natural, semantic, and memorable.
  .supertabs {
    __accordion: 1;  /* This is ignored by the browser, see "Problem" below. */
  }
}

In javascript I would pick this up like so:

// Use jQuery to make this code look simpler.
var $ = jQuery;
$(window).resize(function () {
  $('.supertabs').each(function () {
    // Won't work, because '__accordion' is not a known CSS property name.
    if (window.getComputedStyle(this).__accordion) {
      // Enable accordion.
      $(this).addClass('supertabs-accordion');
      $(this).removeClass('supertabs-tabs');
    }
    else {
      // Enable tabs.
      $(this).removeClass('supertabs-accordion');
      $(this).addClass('supertabs-tabs');
    }
  });
});

Then the package CSS can pick this up and apply the change that distinguish accordion vs tabs behavior.

Problem:

The property name "__accordion" is unknown, ignored by the browser, and not accessible with getComputedStyle().

Alternatives I considered

I could use a known property name, but this might have an undesired effect.

I could use the "variable" syntax (--accordion), but technically this also has an effect, it is not picked up by getComputedStyle(), and I am not sure at all what Internet Explorer will do with it (we are supporting IE11, fyi).

I could introduce a hidden element and come up with a made-up convention, e.g. if the hidden element is position:absolute, then show the accordion, otherwise show tabs. The idea is similar to this article, https://www.lullabot.com/articles/importing-css-breakpoints-into-javascript.

The hidden element would work. But I was looking for something less arbitrary, to make this more suitable for a package to be published.

14
  • I would consider a property that only have effect in a particular context ... for example grid-gap is only considered if your element is set display:grid. If you are sure you will never use CSS grid then you can use it. There is also other properties related to flexbox Commented Dec 13, 2019 at 0:55
  • 1
    also for CSS variable you need something like this; window.getComputedStyle(div).getPropertyValue('--example-var') Commented Dec 13, 2019 at 0:57
  • @TemaniAfif The "grid-gap" feels quite arbitrary, and there is a tiny-tiny chance that in some niche case it might actually have an effect. Also the more obscure the property, the higher the chance it won't work in an older browser.. Commented Dec 13, 2019 at 1:02
  • @TemaniAfif Funny how ['property-name'] works only for known regular properties, .getPropertyValue() works for known properties and variables, and unknown non-variables are not accessible with either method. Commented Dec 13, 2019 at 1:04
  • 1
    here is a example of using content with an element: stackoverflow.com/a/58953323/8620333 Commented Dec 13, 2019 at 1:21

2 Answers 2

1

Update: the support of SVG related property is worse than CSS variables. The below will not work on Firefox.

As I commented you can consider the use of properties that are defined only in a particular context like ones related to Flexbox, CSS grid if you are sure that you will not be using any of them or consider properties related to SVG elements that have no effect on common elements like x/y/cx/cy/rx/ry/d, etc

$('.supertabs').each(function() {
  console.log(window.getComputedStyle(this).getPropertyValue('cx') +" "+ window.getComputedStyle(this).x)
});
.supertabs {
  cx: 1;
  x: 2;
}

.alt {
  cx: 0;
  x: 5;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="supertabs"></div>
<div class="supertabs"></div>
<div class="supertabs alt"></div>
<div class="supertabs"></div>

As a side note with CSS variables you need to use window.getComputedStyle().getPropertyValue('--var')

$('.supertabs').each(function() {
  console.log(window.getComputedStyle(this).getPropertyValue('--var'))
});
.supertabs {
  --var:1;
}

.alt {
  --var:0;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="supertabs"></div>
<div class="supertabs"></div>
<div class="supertabs alt"></div>
<div class="supertabs"></div>

Sign up to request clarification or add additional context in comments.

7 Comments

I wonder if variables are found in IE11 with getComputedStyle(). I know the variable won't work as a variable in IE11, but will it be detected?
@donquixote you can always try. From the CSS perspective they aren't supported but I don't know from the JS perspective and I cannot test ..
Your solution will work, and I think I am going to accept this eventually. It still feels arbitrary and not semantic or memorable though. Users of the library would have to check the manual each time to remember which property name is being used.
@donquixote you can always use CSS variables as main solution and have the SVG one as a fallback
SVG attributes like cx and x as presentation attributes and hence accessible to CSS is a very recent thing of SVG2, and for instance FF has begun support it only last few versions, not a good idea to use it as a fallback ;-) (+ it doesn't actually works there, since it shouldn't have any effect on this HTMLelement, they always return the default value '0px'.
|
1

I did some testing myself.

The goal was to have consistent behavior across browsers, and to have something that does not feel too arbitrary.

https://jsfiddle.net/dxsmub5g/ Detailed output below.

Solution I chose

The best solution seems to be element::before {content: '...';}, to be read with `window.getComputedStyle(ELEMENT, '::before').getPropertyValue('content');

The ::before can get a display: none; to make sure the content has no effect.

Or even better, put this on a nested div which itself is display: none;.

This is also what was suggested in https://www.lullabot.com/articles/importing-css-breakpoints-into-javascript, except they do it directly on the body element.

Something to keep in mind: The value will be a string value in double quotes. Even if you use single quotes in the original CSS, the value will be in double quotes. In most cases JSON.parse(value) should remove the quotes, but I am not sure if this is 100% universal, e.g. if you want to encode complex objects within the content property.

Options that I discarded

  • content: on a non-pseudo element
    • IE11 does not detect string value.
  • --varname: (css variable):
    • not detected by IE11.
  • unknown properties
    • generally not working in any browser.
  • svg properties ('cx'):
    • might not work on some older browsers
    • IE11 needs ['cx'] syntax instead of .getPropertyValue('cx')
    • not semantic, feels arbitrary.
    • Discarded by Firefox if the element is not SVG.
  • other obscure properties
    • not semantic, feels arbitrary.

Testing details

(I think this won't work on this website, but posting it anyway)

(function ($) {
  var names = [];
  var rules = document.styleSheets[1].rules;
  for (var iRule = 0; iRule < rules.length; ++iRule) {
    console.log(rules[iRule]);
    var styles = rules[iRule].style;
    for (var iStyle = 0; iStyle < styles.length; ++iStyle) {
      names.push(styles[iStyle]);
    }
  }
  names.push('varname');
  names.push('--varname');
  names.push('cx');
  names.push('unknownprop');
  names.push('transition');
  names.push('transition-duration');
  var values = {};
  var $div = $('div');
  var div = $div[0];
  var style = window.getComputedStyle(div);
  for (var i = 0; i < names.length; ++i) {
    var name = names[i];
    try {
    	var valueJQ = $div.css(name);
    }
    catch (e) {
      valueJQ = e;
    }
    var valueGCSOffset = style[name];
    var valueGCSgPV = style.getPropertyValue(name);
  	values[name] = [valueJQ, valueGCSOffset, valueGCSgPV];
  }
  
  values['div::before {content}'] = window.getComputedStyle(div, ':before').getPropertyValue('content');
  
  $('div').html('<pre>' + JSON.stringify(values, null, 2) + '</pre>');
  console.log(names, values);
})(jQuery || null);
div {
  content: 'xyz';
  --varname: 'abc';
  cx: 0;
  unknwownprop: 'uuu';
  transition: max-height 3s;
}

div::before {
  display: none;
  content: 'content before';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

IE11:

{
  "content": [
    "normal",
    "normal",
    "normal"
  ],
  "transition-property": [
    "max-height",
    "max-height",
    "max-height"
  ],
  "transition-duration": [
    "3s",
    "3s",
    "3s"
  ],
  "transition-timing-function": [
    "cubic-bezier(0.25, 0.1, 0.25, 1)",
    "cubic-bezier(0.25, 0.1, 0.25, 1)",
    "cubic-bezier(0.25, 0.1, 0.25, 1)"
  ],
  "transition-delay": [
    "0s",
    "0s",
    "0s"
  ],
  "display": [
    "block",
    "block",
    "block"
  ],
  "varname": [
    null,
    null,
    ""
  ],
  "--varname": [
    null,
    null,
    ""
  ],
  "cx": [
    "0",
    "0",
    ""
  ],
  "unknownprop": [
    null,
    null,
    ""
  ],
  "transition": [
    "",
    "",
    ""
  ],
  "div::before {content}": "\"content before\""
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.