Reputation: 5511
How would I name a CSS property so that
window.getComputedStyle(element)
, or jQuery.fn.css()
) as the currently active style on an element, butI 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()
.
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.
Upvotes: 1
Views: 1108
Reputation: 5511
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.
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.
content:
on a non-pseudo element
--varname:
(css variable):
['cx']
syntax instead of .getPropertyValue('cx')
(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\""
}
Upvotes: 1
Reputation: 274318
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>
Upvotes: 1