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

5

10

<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 : PostCSS : 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.
Adenoma answered 19/9, 2017 at 21:7 Comment(8)
https://mcmap.net/q/354958/-does-css-automatically-add-vendor-prefixesJonis
Dear @Jonis please take some time and read my question again. My question is not about property names but values. jQuery does not take care of this. I even emphasized this in my question - so you propably didn't read carefully (which is quite normal)!Adenoma
Jquery .css() will set the appropriate vendor prefix, automatically, if it is needed. It's not a pure JavaScript solution, but it is JavaScript.Jonis
@Jonis This is not true! jQuery prefixes property names like "background", "display", etc. but not values like "linear-gradient", "flex", etc. Don't you get it? css_selector { property-name: value }. AGAIN: "property-name": will be prefixed by "jQuery" and also "-prefix-free" | "value": will not be prefixed by jQuery nor -prefix-free.Adenoma
Perhaps you should re-submit your question to be more clear, then (not to mention less rude). No one else here seems to understand your intent, either.Jonis
@Jonis Apologies if my comments seems to be rude but to be honest I'm a bit dissappointent about "the community" (I guess with rude you meant my comments not my question - didn't you?). IMHO this question is quite interesting and worth to get an appropriate answer. Please tell me how can I be more clear then to saying explicitly in my title: "How to set vendor prefixed CSS values (NOT property names)" or also original quotes: "What I'm Looking For: background: -prefix-linear-gradient{...}", "What I'm NOT Looking For: -prefix-transform: translate{...}" - so HOW to be more clear?Adenoma
Regarding your example: linear-gradient() has changed its syntax (added 'to' keyword and "magic angles", changed the angle direction), so just adding the prefix to a modern value may not work in the target old browser (or, at least, not work as expected). Should the solution account such changes?Brotherly
@IlyaStreltsyn jepp sure. That's why Autoprefixer is the best solution so far... We should find the solution with the most possible cross-browser supportAdenoma
N
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;
};
Nostology answered 20/8, 2018 at 6:51 Comment(4)
This is funny. Just a very few days ago I got aware about Modernizrs prefixedCSSValue method. Just didn't have enough time to answer on my own; but it actually was on my ToDo-List.Adenoma
@Adenoma I'm gonna use emotion to handle dynamic style transform client side, it prefixes some css values automatically. And use Modernizr when emotion does notNostology
Thanx for sharing @Jess. I hope you are happy with my edit to enhance your answer. BTW did you have a look at my answer working with Autprefixer? It has a very havy payload but also some big advantages (eg 27 special hacks) over other solutions. Have a nice day :)Adenoma
@Adenoma Thx for your editing. I have read your answer with autoprefixer, it does a great job. as you pointed out, it has a large file size. since my pages are for normal real users, I can't use it in such situation. it suits best for internal systemsNostology
K
3

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.

Karat answered 19/9, 2017 at 21:39 Comment(4)
Jepp sure - I use "autoprefixer" in my "grunt | SASS" build process but I haven't thought about using it "post" instead of "pre". In addition: do you have a hint on a good tut on how to get it running quick.Adenoma
Honestly, I just set the browser versions and don't think about it. Good luck.Karat
Ah ok; we are not on the same track yet. What I mean is how to use autprefixer on an already deployed site not as part of the build setup... If you have time maybe you could have a look at my question again - see my update "Use Case Scenario" which should make it clear!Adenoma
Just for the record: this answer isn't a solution to the OP at all because it is not about a client-side solution.Adenoma
Z
2

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;
Zambia answered 20/9, 2019 at 19:19 Comment(1)
@quicklikerabbit thanks for the advice! I commented my thoughts.Zambia
W
1

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;
}
Warenne answered 30/9, 2017 at 21:16 Comment(3)
Looks really promising so far - had have thought in that direction also. Could you modify the snippet that it looks more like the one in my answer. Its not about the look but about comparing the original CSS with the modified CSS. Would be great.Adenoma
@Adenoma The rendering of the results within stacksnippets should not matter. The property values are set at the object. You can include console.log(console.log(cssRules[0].cssText)) before .filter() and .forEach() calls to display value before before set.Warenne
You are right. The rendering of the results within stacksnippets does not matter. But visual feedback is nice anyway.Adenoma
N
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;
};
Nostology answered 20/8, 2018 at 6:51 Comment(4)
This is funny. Just a very few days ago I got aware about Modernizrs prefixedCSSValue method. Just didn't have enough time to answer on my own; but it actually was on my ToDo-List.Adenoma
@Adenoma I'm gonna use emotion to handle dynamic style transform client side, it prefixes some css values automatically. And use Modernizr when emotion does notNostology
Thanx for sharing @Jess. I hope you are happy with my edit to enhance your answer. BTW did you have a look at my answer working with Autprefixer? It has a very havy payload but also some big advantages (eg 27 special hacks) over other solutions. Have a nice day :)Adenoma
@Adenoma Thx for your editing. I have read your answer with autoprefixer, it does a great job. as you pointed out, it has a large file size. since my pages are for normal real users, I can't use it in such situation. it suits best for internal systemsNostology
A
0

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


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

Adenoma answered 25/9, 2017 at 13:30 Comment(17)
Note, a library is not necessary, see stackoverflow.com/a/37364536Warenne
@Warenne thank you for you comment. Please correct me if I'm wrong but your solution is about prefixing CSS property names (like transform, border-radius, transition, etc) - isn't it? My question is about CSS values (like linear-gradient, flex, etc)!Adenoma
What does "I would love to get a generic solution on how to set vendor prefixed CSS values client-side via JavaScript." at Question mean?Warenne
This means that the solution can be used in any situation and is not limitted to just -for example- one browser-family, one browser-era or some CSS-value-groups (like the gradient group). If you have a look at my answer (maybe run the code snippet) you will see that this is a very generic solution. Autoprefixer even makes use of 27 special hacks that try to make the CSS "as cross-browser as possible". Actually I'm quite satisfied with this solution - if there would not be the heavy payload. Autoprefixer wasn't designed to run client-side (unless its able to).Adenoma
So the payload wasn't that important. So maybe someone knows a more lightweight tool that can do it similar. Or a special build of Autoprefixer itself would also be possible... But again @Warenne am I right that your solution isn't usefull for CSS-values but CSS-property-names (please clearify)???Adenoma
Unfortunately it seems to be not clear to a lot of people. Like you who gave a hint on "CSS property names" which is not at all what I'm looking for. I'm really thinking about how to make it more clear! And then I read my question again. Eg the title "How to set vendor prefixed CSS values (NOT property names) | client-side" - how can it be more clear??? But people writing about "build tool plugins" (which isn't client-side) or jQuery css method (which is about CSS property names and I even wrote explicitly in my question: "Who Is NOT Getting The Job Done: ' 'jQuery'").Adenoma
Actually I have the feeling that most of us don't take enough time to read carefully (I really don't want to be unpolite and don't want to be rude at all!). They 1. overfly the question, 2. looks like one of these standard questions you have seen 1000times before, 3. give some standard comments that where given 1000times before - but actually are not aware that its completely off-topic and that this is a very specific question that you eventually have not seen before (at least not in its specific context).Adenoma
Have no concern whether you consider your comments "rude" and "impolite" or not; those pleasantries have no relevance to clarity of OP. The actual text of OP "I would love to get a generic solution on how to set vendor prefixed CSS values client-side via JavaScript. Which specific vendor prefix (-webkit-, -ms-, -moz-, etc.) this may be is known very well and so not at all in scope of my question. This question is about how to do this client-side and not as a part of the build process." which linked code achieves. Am attempting to determine what the requirement is, if not the text at OPWarenne
"how to set vendor prefixed CSS values" the linked code does well on "CSS property names" but not at all on "CSS values" (correct me if I'm wrong...) For the clarification CSS in general: selector{ css-property-name: css-value }. The linke code is about selector{ PREFIXED-css-property-name: css-value } but not about selector{ css-property-name: PREFIXED-css-value } (which is what I'm after).Adenoma
Use the same pattern to set the prefixed valueWarenne
Please consider to adapt the pattern to set the prefixed value and post it as an answer. This would be awesome!Adenoma
Which specific CSS property values are you trying to add a vendor prefix to?Warenne
@Warenne lets stay with examples that are already in use in this thread: "flex", "linear-gradient"Adenoma
linear-gradient does not need to be prefixed. -webkit-flex is also defined at Firefox.Warenne
linear-gradient and flex both needs -webkit- in "android < 4.4", "blackberry browser 7" and others.Adenoma
flex is a property, which is outside of the scope of the requirement, yes? Or is requirement to set both CSS property and value?Warenne
no - of course its in scope. Referring to the OPs title: "How to set vendor prefixed CSS values (NOT property names)" flex is a CSS value that can be used to specify the CSS property name display.Adenoma

© 2022 - 2024 — McMap. All rights reserved.