Согласно caniuse.com , из текущих браузеров только IE, Edge (более старые версии) и Opera Mini не поддерживают переменные CSS.Этот полифил, по-видимому, действительно хорошо работает со всеми тремя.
Это попытка создать очень простой CSS-файл переменных (пользовательских свойств).На самом деле это скорее частичное заполнение, так как оно не будет охватывать переменные внутри переменных, область видимости DOM или что-либо еще «причудливое».Просто беру переменные, объявленные где-нибудь в CSS, а затем повторно анализирую CSS для операторов var () и заменяю их в браузерах, которые изначально не поддерживают CSS-переменные.
Я пытаюсь протестировать этот полифиль в IE 11 иПохоже, это работает с ним.
/*!
* css-var-polyfill.js - v1.0.0
*
* Copyright (c) 2018 Aaron Barker <http://aaronbarker.net>
* Released under the MIT license
*
* Date: 2018-03-09
*/
let cssVarPoly = {
init: function() {
// first lets see if the browser supports CSS variables
// No version of IE supports window.CSS.supports, so if that isn't supported in the first place we know CSS variables is not supported
// Edge supports supports, so check for actual variable support
if (window.CSS && window.CSS.supports && window.CSS.supports('(--foo: red)')) {
// this browser does support variables, abort
console.log('your browser supports CSS variables, aborting and letting the native support handle things.');
return;
} else {
// edge barfs on console statements if the console is not open... lame!
console.log('no support for you! polyfill all (some of) the things!!');
document.querySelector('body').classList.add('cssvars-polyfilled');
}
cssVarPoly.ratifiedVars = {};
cssVarPoly.varsByBlock = {};
cssVarPoly.oldCSS = {};
// start things off
cssVarPoly.findCSS();
cssVarPoly.updateCSS();
},
// find all the css blocks, save off the content, and look for variables
findCSS: function() {
let styleBlocks = document.querySelectorAll('style:not(.inserted),link[rel="stylesheet"]');
// we need to track the order of the style/link elements when we save off the CSS, set a counter
let counter = 1;
// loop through all CSS blocks looking for CSS variables being set
[].forEach.call(styleBlocks, function(block) {
// console.log(block.nodeName);
let theCSS;
if (block.nodeName === 'STYLE') {
// console.log("style");
theCSS = block.innerHTML;
cssVarPoly.findSetters(theCSS, counter);
} else if (block.nodeName === 'LINK') {
// console.log("link");
cssVarPoly.getLink(block.getAttribute('href'), counter, function(counter, request) {
cssVarPoly.findSetters(request.responseText, counter);
cssVarPoly.oldCSS[counter] = request.responseText;
cssVarPoly.updateCSS();
});
theCSS = '';
}
// save off the CSS to parse through again later. the value may be empty for links that are waiting for their ajax return, but this will maintain the order
cssVarPoly.oldCSS[counter] = theCSS;
counter++;
});
},
// find all the "--variable: value" matches in a provided block of CSS and add them to the master list
findSetters: function(theCSS, counter) {
// console.log(theCSS);
cssVarPoly.varsByBlock[counter] = theCSS.match(/(--.+:.+;)/g) || [];
},
// run through all the CSS blocks to update the variables and then inject on the page
updateCSS: function() {
// first lets loop through all the variables to make sure later vars trump earlier vars
cssVarPoly.ratifySetters(cssVarPoly.varsByBlock);
// loop through the css blocks (styles and links)
for (let curCSSID in cssVarPoly.oldCSS) {
// console.log("curCSS:",oldCSS[curCSSID]);
let newCSS = cssVarPoly.replaceGetters(cssVarPoly.oldCSS[curCSSID], cssVarPoly.ratifiedVars);
// put it back into the page
// first check to see if this block exists already
if (document.querySelector('#inserted' + curCSSID)) {
// console.log("updating")
document.querySelector('#inserted' + curCSSID).innerHTML = newCSS;
} else {
// console.log("adding");
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = newCSS;
style.classList.add('inserted');
style.id = 'inserted' + curCSSID;
document.getElementsByTagName('head')[0].appendChild(style);
}
};
},
// parse a provided block of CSS looking for a provided list of variables and replace the --var-name with the correct value
replaceGetters: function(curCSS, varList) {
// console.log(varList);
for (let theVar in varList) {
// console.log(theVar);
// match the variable with the actual variable name
let getterRegex = new RegExp('var\\(\\s*' + theVar + '\\s*\\)', 'g');
// console.log(getterRegex);
// console.log(curCSS);
curCSS = curCSS.replace(getterRegex, varList[theVar]);
// now check for any getters that are left that have fallbacks
let getterRegex2 = new RegExp('var\\(\\s*.+\\s*,\\s*(.+)\\)', 'g');
// console.log(getterRegex);
// console.log(curCSS);
let matches = curCSS.match(getterRegex2);
if (matches) {
// console.log("matches",matches);
matches.forEach(function(match) {
// console.log(match.match(/var\(.+,\s*(.+)\)/))
// find the fallback within the getter
curCSS = curCSS.replace(match, match.match(/var\(.+,\s*(.+)\)/)[1]);
});
}
// curCSS = curCSS.replace(getterRegex2,varList[theVar]);
};
// console.log(curCSS);
return curCSS;
},
// determine the css variable name value pair and track the latest
ratifySetters: function(varList) {
// console.log("varList:",varList);
// loop through each block in order, to maintain order specificity
for (let curBlock in varList) {
let curVars = varList[curBlock];
// console.log("curVars:",curVars);
// loop through each var in the block
curVars.forEach(function(theVar) {
// console.log(theVar);
// split on the name value pair separator
let matches = theVar.split(/:\s*/);
// console.log(matches);
// put it in an object based on the varName. Each time we do this it will override a previous use and so will always have the last set be the winner
// 0 = the name, 1 = the value, strip off the ; if it is there
cssVarPoly.ratifiedVars[matches[0]] = matches[1].replace(/;/, '');
});
};
// console.log(ratifiedVars);
},
// get the CSS file (same domain for now)
getLink: function(url, counter, success) {
var request = new XMLHttpRequest();
request.open('GET', url, true);
request.overrideMimeType('text/css;');
request.onload = function() {
if (request.status >= 200 && request.status < 400) {
// Success!
// console.log(request.responseText);
if (typeof success === 'function') {
success(counter, request);
}
} else {
// We reached our target server, but it returned an error
console.warn('an error was returned from:', url);
}
};
request.onerror = function() {
// There was a connection error of some sort
console.warn('we could not get anything from:', url);
};
request.send();
}
};
cssVarPoly.init();
:root {
--externalcolor: red;
--samename: orange;
--samename: #0f0;
--foo: green;
--FOO: #0f0;
--halfsuccess: orange;
--success: green;
--success2: #0f0;
}
html {
font-family: var(--fontsans);
}
.success {
color: green;
}
.fail {
color: red;
}
span {
display: inline-block;
margin: 5px;
}
.samename {
color: var(--samename);
}
.demo1 {
color: #f00;
color: var(--success);
}
.demo2 {
color: #f00;
color: var( --success2);
}
.demo3 {
color: #f00;
color: var(--halfsuccess);
color: var(--success);
}
.demo4 {
color: red;
border-color: #f00;
}
.inlineoverlink {
color: #f00;
}
p {
padding: var(--spacing-l);
}
.lower {
color: var(--foo);
}
.upper {
color: var(--FOO);
}
.externalcolor {
color: var(--externalcolor);
}
.fallback {
color: #f00;
color: var(--wrongname, green);
}
// for the top documentation
.supports {
color: green;
.no {
display:none;
}
}
.showforpolyfill {
display:none;
}
.cssvars-polyfilled {
.supports {
color: red;
.no {
display:inline;
}
}
.showforpolyfill {
display:inline;
}
.hideforpolyfill {
display:none;
}
}
.hide,
.hide-the-docs .documentation {
display:none;
}
/* declare some font-family stuff at bottom of file to reflect on stuff above it*/
:root {
--fontsans: arial;
}
<!-- Copy below for codepen update -->
<h1>CSS Variables Polyfill</h1>
<p>This is now managed (and available for PRs) at <a href="https://github.com/aaronbarker/css-variables-polyfill">https://github.com/aaronbarker/css-variables-polyfill</a>.</p>
<p>
This is an attempt at a very basic <a href="https://drafts.csswg.org/css-variables/">CSS variables (custom properties)</a> polyfil. In reality this is more of a <em>partial</em> polyfill as it will not cover variables inside of variables, DOM scoping or anything else "fancy". Just taking variables declared anywhere in the CSS and
then re-parsing the CSS for var() statements and replacing them in browsers that don't natively support CSS variables.
</p>
<p>According to <a href="http://caniuse.com/#feat=css-variables">caniuse.com</a>, of current browsers only IE, Edge and Opera Mini do not support CSS variables. This polyfil appears to work on all three really well. I don't see why this wouldn't work on older browsers as well, but I haven't been able to test it on them yet.</p>
<p>As far as we can tell your browser <span class="supports">does <span class="no">not</span> support</span> native CSS variables. <span class="showforpolyfill">That means if you see green tests results below, it is thanks to the polyfill :).</span> <span class="hideforpolyfill">All the green test results below are actually native CSS Variable support. Good job using a good browser :)</span></p>
<h3>Does this work on externally CSS files?</h3>
<p>Yes!</p>
<h3>Even ones loaded from another domain?</h3>
<p>To go across domain, CSS needs to be served up with <code>Access-Control-Allow-Origin:*</code> headers.</p>
</div>
<a href="#d" class="hide-docs">Toggle documentation</a> (for Opera Mini vs Codepen issue)
<style>
:root {
--newcolor: #0f0;
}
.inlineoverlink {
color: var(--success2);
}
</style>
<h2>Tests</h2>
<p>On mosts tests (unless otherwise noted) success will be green text. We start with a <code>color:red;</code> and then override it with a <code>color:var(--success);</code> (or similar) which is green.</p>
<ul>
<li><span class="samename">declare same variable over and over</span></li>
<li><span class="demo1">no whitespace on var() calls</span></li>
<li><span class="demo2">whitespace on var() calls</span></li>
<li><span class="demo3">Multiple variables in same call. orange means first var worked, green var worked</span></li>
<li><span class="inlineoverlink">orange if link won, green if style after link won</span></li>
<li><span class="lower">--foo: lowercase foo</span></li>
<li><span class="upper">--FOO: uppercase FOO</span></li>
<li><span class="fallback">uses fallback <code>--var(--wrongname, green)</code></span></li>
<li><span class="demo-import">css declared in an <code>@import</code></span> - not polyfilled yet. <a href="https://gist.github.com/stramel/91d05253f801f771da38b3bc7d3c765f#gistcomment-2258818">Identfied with a suggested fix</a>, but will require a bit of a re-write (to use document.styleSheets), so haven't done it yet.</li>
</ul>
<h2>Tests on external, cross-domain file</h2>
<div class="documentation">
<p><strong>Edge</strong> appears to be working well on Edge 13. Edge 12 was having some problems.</p>
<p><strong>Opera mini</strong> seems to work well too. This demo fails because not all the page is displayed, but I think that is a codepen issue, not a polyfill issue. When the upper documentation is removed, all tests display well.</p>
<p><strong>IE 11</strong> seems to do fine.</p>
</div>
<ul>
<li><span class="demo4">Gets stuff from external .css file. Should start red and change to green on LINK load. border proves the CSS loaded, missing colors means script didn't get parsed and reinserted</span></li>
<li><span class="externalcolor">--externalcolor: should start red and change to green on LINK load</span></li>
<li><span class="externalfallback">uses fallback. should be green</span></li>
</ul>
<p>Another set of text under the test for Opera Mini testing.</p>
<!-- Copy above for codepen update -->
Результат тестирования:
Ссылки:
(1) Пример кода Codepen
(2) aaronbarker / css-variable-polyfill