How to set vendor prefixed CSS values (NOT property names) | client-side

*爱你&永不变心* 提交于 2020-01-13 08:33:11

问题


<edit> I actually guessed this would happen but just some seconds after posting I got a flag for "possible duplicate" which is not appropriate! This question is about CSS values and NOT about CSS property names and so it's not a dup of this or this question!!! Its also not a dup of this one because I'm asking for a generic solution.
If you are still not convinced or unsure about what this post is not about, maybe you take a look at the bottom of this question: "What I'm NOT Looking For" and "Who Is NOT Getting The Job Done" </edit>

Is there a way to set an appropriate vendor-prefixed CSS value client-side via JavaScript if needed?

What I'm Looking For

for example: background: -prefix-linear-gradient{...}

I would love to get a generic solution on how to set vendor-prefixed CSS values client-side via JavaScript. Besides this the question is about how to do this client-side and not as a part of the build process (eg POSTcss).

But I also appreciate any hints on

  • JavaScript / jQuery plugins that get the job done or
  • additional resources that could let me figure it out on my own.

As you can see I already gave an answer on my own. But I'm still looking for better solutions as Autoprefixer comes along with a heavy payload of about 626 KB!


Use Case Scenario

/*
Unprefixed version of "linear-gradient" will only work for
browsers: IE10+, FF16+, Chrome26+, Opera12+, Safari7+.
So how to generate a prefixed version on the fly if necessary?
*/

var aVal = ['linear-gradient(to bottom, #fefefe 0%,#aaaaaa 100%)', 'linear-gradient(to bottom, #aaaaaa 0%,#fefefe 100%']
    style = document.getElementsByTagName('BODY')[0].style,
    i = 0;
(function toggle () {

  if ( i++ ) { i = 0; }
  style.background = aVal[ i ];
  /* here we need something like:
  style.background = parseForPrefix( aVal[ i ] );
  */
  setTimeout(toggle, 2000)

})();
* {
  height: 100%;
  margin: 0;
  padding: 0;
  width: 100%;
}
Unprefixed version of "linear-gradient" will only work for<br>
browsers: IE10+, FF16+, Chrome26+, Opera12+, Safari7+.<br>
So how to generate a prefixed version on the fly if nessecary?

Or imagine something like

jQuery('head').append('<style>body{background:linear-gradient(...)}</style>')

which should be something like

jQuery('head').append('<style>'
    + parseForPrefix('body{background:linear-gradient(...)}') +
'</style>')

instead.


What I'm NOT Looking For

for example: -prefix-transform: translate{...}

The topic how to use vendor prefixes on CSS property names is discussed enough (and not what I'm after).
NOTE: I'm also totally aware of pre-&post-processors used as part of the build process. My whole CSS workflow is based on "Grunt : SASS : Autoprefixer" so no need to give any suggestions on that!


Who Is NOT Getting The Job Done

  • -prefix-free is doing a pretty good job on vendor-prefixed CSS property names but doesn't take care of vendor-prefixed CSS values.
  • Unfortunately, this is also the case with jQuery.

回答1:


maybe Modernizr can fix this, like

// returns: -webkit-linear-gradient(left, red, red)
Modernizr.prefixedCSSValue('background', 'linear-gradient(left, red, red)')

How it works:

// prefixedCSSValue is a way test for prefixed css properties (e.g. display: -webkit-flex)
// @credits modernizr v3.6.0 | Build https://modernizr.com/download?-prefixedcssvalue-dontmin
Modernizr.prototype.prefixedCSSValue = function(prop, value) {
    var result = false;
    var elem = createElement('div'); // basically: document.createElement.apply(document, ['div'])
    var style = elem.style;

    if (prop in style) {
        var i = domPrefixes.length; // domPrefixes === [ "moz", "o", "ms", "webkit" ] or []

        style[prop] = value;
        result = style[prop];

        while (i-- && !result) {
            style[prop] = '-' + domPrefixes[i] + '-' + value;
            result = style[prop];
        }
    }

    if (result === '') {
        result = false;
    }

    return result;
};



回答2:


In order to do what you're asking, you'd need a reference to compare the browser that's currently being used against what prefixes are needed; like caniuse. Or you could make some mixins with the CSS @supports rule, but that might be more trouble than it's worth.

There is an existing solution, autoprefixer, but it would require you to use postcss. The README has examples of various build tool plugins. I use SCSS and autoprefixer, and I'm living the dream.




回答3:


You can iterate document.styleSheets, get "cssRules" and "style" property of CSSStyleDeclaration, if the value of the property matches a variable, for example, a RegExp, use .replace() to prepend prefix to value

const [values, prefix, re = new RegExp(values.join("|"))] = [
  ["linear-gradient","flex"], "-webkit-"
];

[...document.styleSheets]
.forEach(({cssRules}) => {
  const [[{style}], styles = Object.entries(style)] = [cssRules];
  if (cssRules.length && styles.some(([, prop]) => 
      re.test(prop))) {
    console.log(cssRules[0].cssText);
    styles
    .forEach(([key, prop]) => {
      if (re.test(prop)) style[key] = prop.replace(prop, prefix + prop)});
    console.log(cssRules[0].cssText);
  }
})
body {
  background: linear-gradient(to bottom, #fefefe 0%, #aaaaaa 100%);
  display: flex;
  flex-direction: row;
}



回答4:


Unlike elem.style.background = 'linear-gradient{...}'

you can use elem.style.cssText = 'background:linear-gradient{...}; ...'

This approach let you add a several styles at once to element, of course inline. Only the property that the browser understands will be written. So, just take a current inline styles elem.getAttribute('style') (string, not a style object) or create empty string || '' and add your own.

let elem = document.getElementById('elem');
let styleAdd = 'background: -prefix-linear-gradient{...}; background: linear-gradient{...};';
elem.style.cssText = (elem.getAttribute('style') || '') + styleAdd;



回答5:


If you are unsure if this topic is something that is relevant for you, please read section "Recommendation | Important Notes For Newbies" at the bottom of this answer first.


Credits to Ari for his answer that at least pointed me in the right direction. This solution makes use of Autoprefixer which most of you probably use together with a task runner as part of your build setup (which is also true for me).

Unfortunately, I was not able to get information about how to use Autoprefixer as a client-side, standalone version. So I simply took a look at websites from which I know that they are doing the same task that I also want to achieve (namely Autoprefixer | UI, CodePen, Sassmeister and JS Bin).

The best resource for doing so was the official Autoprefixer | UI and all in all - actually it wasn't a big deal. So here we go with a very basic mockup that is able to illustrate...

How To Use Autoprefixer Client-Side

Actually autoprefixer.process( sInput ).css is all we need.

But lets bootstrap this to a more real life-like use case scenario.

// Autoprefixer | ready to rumble
// http://autoprefixer.github.io/assets/code.js?v=1499371172398732683
var sInput  = document.getElementById("AutoprefixerIn").innerHTML,
    sOutput = autoprefixer.process( sInput, {}, {browsers: ["> 0%"]} ).css;

document.getElementById("AutoprefixerOut").innerHTML = sOutput;
document.getElementById("console-AutoprefixerIn").innerHTML += sInput;
document.getElementById("console-AutoprefixerOut").innerHTML += sOutput;
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Autoprefixer.js | Client-Side</title>
<!-- give snippet a better appearance -->
    <style>*{margin:0;padding:0;height:100%;width:100%;}pre{border:2px solid #fff;box-sizing:border-box;overflow:auto;width:50%;}</style>

<!-- Autoprefixer | source, input -->
<!-- IE10+, FF16+, Chrome26+, Opera12+, Safari7+ | prefix for others -->
    <style id="AutoprefixerIn">
        body {
            background: linear-gradient(to bottom, #fefefe 0%,#aaaaaa 100%);
            display: flex;
        }
    </style>
<!-- Autoprefixer | destination, output -->
    <style id="AutoprefixerOut"></style>

<!-- Autoprefixer | load library -->
<!-- view-source:http://autoprefixer.github.io/ | line: 140 -->
    <script src="https://rawgit.com/ai/autoprefixer-rails/master/vendor/autoprefixer.js" crossorigin="anonymous"></script>
    <script> // online fallback in case github is off
        window.autoprefixer || document.write('<script src="https://wzrd.in/standalone/autoprefixer@latest">\x3C/script>');
    </script>
</head>
<body>
<pre id="console-AutoprefixerIn">
    /* display unprefixed original input coming from "style#AutoprefixerIn" */
</pre>
<pre id="console-AutoprefixerOut">
    /* display dynamically generated and prefixed output coming from "style#AutoprefixerOut" */
</pre>

Pro

  • Autoprefixer comes along with 27 special hacks which probably makes it the most bulletproof cross-browser solution that is available by now.

Contra

  • Autoprefixer comes along with a heavy payload which is 626 KB (minified).

Recommendation | Important Notes For Newbies

  • This solution is only relevant to developers that must add CSS to a website "on the fly". And even then only if the CSS contains property values that have to be vendor-prefixed (eg linear-gradient, radial-gradient, etc.) and not CSS property names (eg background). Much likely this would be the case for plugin authors that rely on cross-browser CSS3 features.
  • Another scenario would be if you are not able to control the actual markup other then client-side via JavaScript.
  • In most use cases it is enough to take care for vendor-prefixed CSS property names (jQuery css method does this as well).
  • For any CSS that is delivered directly to the client (the browser) it is strongly recommended to prepare the CSS in your build setup BEFORE IT IS DELIVERED.
  • If you are still not sure it is most likely that there are much better solutions out there for you.

PS: After double checking all resources I got aware that https://github.com/postcss/autoprefixer#javascript also points at the right direction.



来源:https://stackoverflow.com/questions/46309768/how-to-set-vendor-prefixed-css-values-not-property-names-client-side

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!