Instead of using prefixes I want to ask site visit

2019-01-02 18:10发布

I'm re-building a site using CSS flexbox.

In checking browser compatibility, I see that flexbox is supported by all modern browsers, except that Safari 8 and IE 10 require vendor prefixes.

In checking Google Analytics, I see that 96% of site visitors in the last 6 months use browsers that fully support flexbox. The remaining 4% use browsers that require prefixes or provide no support.

Since we're talking about 4% of users, and the number will keep getting smaller, (and I like to keep my code as clean and simple as possible), I'm considering not using prefixes, and instead asking users to upgrade their browsers.

How can I target older browsers in order to display a message to users asking them to update their browser?

Here's what I have so far:

<!--[if IE]>
    <div class="browserupgrade">
        <p>You are using an outdated browser. Please <a href="http://browsehappy.com/">
           upgrade your browser</a> to improve your experience.</p>
    </div>
<![endif]-->

This IE conditional comment covers me for IE versions 6, 7, 8 and 9.

These visitors will get an alert with a link to download a current browser. However, Microsoft discontinued support for conditional comments starting with IE10.

Now I need something similar for:

  • IE 10
  • Safari 7-8
  • Opera Mini < 8
  • UC Browser for Android
  • Android Browser < 4.4

Is there a simple JS/jQuery script to handle this job? Or another lightweight method?


Solution

Thanks for all the answers. Clearly there are many ways to tackle this problem (Modernizr, PHP, jQuery functions, plain Javascript, CSS, browser-update.org, etc.) Many of these methods will do the job completely and effectively.

I went with the simplest one: CSS (credit @LGSon).

This CSS covers essentially all targeted browsers, except for IE <= 7.

.browserupgrade { display: block; }
_:-ms-fullscreen, :root .browserupgrade { display: none; }
:-o-prefocus, .browserupgrade { display: none; }
@supports (display: flex) { .browserupgrade { display: none; }}

See the answer for details.

And for those relatively few visitors using IE <= 7, a conditional comment in the HTML:

<!--[if lte IE 7]>
    <div style=" ... inline styles here ...">
        browser upgrade message here
    </div>
<![endif]-->

15条回答
像晚风撩人
2楼-- · 2019-01-02 18:46

You can also insert prefixes into stylesheet. Check if the browsers style property actually supports the style properties either already present, or dynamically inserted later. Composed it primarily for animations, though could be modified to test for any css, style property in the browser. If props already present in stylesheet, vendor prefixes are inserted into stylesheet. If props are later dynamically inserted, vendor prefixes are also inserted, or attached onto those props.

For example, adding prefixes for animation, backface-visibility, border-radius, box-shadow, transform, transform-style, transition; also setting prefix at @keyframes

(function prefix() {
    // var el = $(selector).get(0), i.e.g, $("body").get(0), $("#animated").get(0)
    var prefixes = {"MozAnimation": "-moz-","webkitAnimation": "-webkit-"
                   ,"msAnimation": "-ms-","oAnimation": "-o-"};
    var props = ["animation", "backface-visibility", "border-radius"
                , "box-shadow", "transform", "transform-style", "transition"];
    $.each(prefixes, function(key, val) {
        $("style")
        .attr({"data-prefix": val,"class": String(val).replace(/-/g, "")});
        return !(key in el.style);
    });
    $.each(props, function(index, value) {
        var cssPrefix = $("style").attr("data-prefix");
        if (value in el.style === false) {
            $.fn.pfx = function(prop, pfxprop, q) {
                return $("style").html($("style").text()
                       .replace(new RegExp(prop, "g"), q 
                       + $("style").attr("data-prefix") + pfxprop))
            };
            $("style").pfx("@keyframes", "keyframes", "@")
            .pfx(value, value, "");
        };
    });
}());

github https://github.com/guest271314/prefix/blob/master/prefix.1.1.min.js

查看更多
步步皆殇っ
3楼-- · 2019-01-02 18:47

A more thorough approach.

You can get the user-agent strings for each browser you are interested and create an array of all the ones you want to exclude.

Use navigator.userAgent to get the visitor's user-agent and check if it is in your not-supported-browsers array using jQuery inArray()

A simple javascript alert could be thrown for visitors using a browser that is a match.

Here is an example of getting browser related information (example source:w3schools.com)

var txt = "";
txt += "<p>User-agent header: " + navigator.userAgent + "</p>";
txt += "<p>Browser CodeName: " + navigator.appCodeName + "</p>";
txt += "<p>Browser Name: " + navigator.appName + "</p>";
txt += "<p>Browser Version: " + navigator.appVersion + "</p>";
txt += "<p>Cookies Enabled: " + navigator.cookieEnabled + "</p>";
txt += "<p>Browser Language: " + navigator.language + "</p>";
txt += "<p>Browser Online: " + navigator.onLine + "</p>";
txt += "<p>Platform: " + navigator.platform + "</p>";

document.getElementById("demo").innerHTML = txt;
<div id="demo"></div>

UPDATE

This is a very simple approach, no 3rd party API involved. Simply use UAparser.js to filter results. All you need to check from your part is browser name and version. Check the example below

var parser = new UAParser();
 var thisBrowser = parser.getBrowser();
 var thisOS = parser.getOS();
  switch(true) {
    case (thisBrowser.name=="IE" && thisBrowser.version<=10) :
      alert("IE versions older than v11 are not supported");
    break;
    case (thisBrowser.name=="Safari" && (thisBrowser.version>=7 || thisBrowser.version<9)):
      alert("Safari versions 7 and 8 are not supported");
    break;
    case (thisBrowser.name=="Android Webkit Browser" && thisBrowser.version<4.4):
      alert("Default browser of Android OS versions older than v4.4 are not supported.");
    break;
    case (thisBrowser.name=="UC Browser" && thisOS.name=="Android"):
      alert("UC browser versions for Android are not supported.");
    break;
    case (thisBrowser.name=="Opera Mini" && thisBrowser.version<8):
      alert("Default browser of Android OS versions older than v4.4 are not supported.");
    break;
    default:
    alert("Great this is a supported browser");
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/UAParser.js/0.7.10/ua-parser.min.js"></script>

查看更多
浮光初槿花落
4楼-- · 2019-01-02 18:50

You can use modernizr.js for feature detection.

And then write some JavaScript to re-direct the users to the URL above if the feature isn't found.

查看更多
永恒的永恒
5楼-- · 2019-01-02 18:51

Yes we have a solution for this...

first of all download a custom build Modernizr from (click here --) https://modernizr.com/ (for flexbox property) then include latest Jquery to your page, add some stylesheet and you are done!

(Note: It is not necessary to download a custom build Modernizr, you can use whole complete build directly from Modernizr CDN but as you need to check Flexbox property only; thats why I told you to download a custom build)

Check this fiddle flexbox support fiddle

Your entire html page (with css, jquery ) will be like this...

<!doctype html>
<html class="no-js" lang="en">
<head>
    <meta charset="utf-8">
    <title>BroswerCheck</title>
    <style type="text/css">
        #browserupgrade{
            display:none;
            text-align:center;
            background:#F1070B;
            color:#FFFFFF;
            font-family:Segoe, "Segoe UI", Verdana, sans-serif;
            font-size:15px;
            padding:10px;}

        #browserupgrade a{
            color:#FFFFFF;
            background:#000000;
            text-decoration:none;
            padding:5px 15px;
            border-radius:25px;}
    </style>
    <script src="modernizr.js"></script>
</head>

<body>
    <div id="browserupgrade">
        <p>You are using an outdated browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
    </div>

    <script src="jquery-2.1.1.js"></script>
    <script type="text/javascript">
        $(document).ready(function () {
            if (!Modernizr.flexbox) { $("#browserupgrade").show(); }
        });
    </script>
</body>
</html>

Your steps will be,

1. Download custom Modernizr to detect flexbox property

2. create a html page with class="no-js" added in html tag

3. create a div for displaying a message and hide it

4. add jquery and Modernizr to your page

5. show a div, when a browser doesn't support flexbox-property

(for that purpose use 'Modernizr.flexbox' property as shown in above script)

or if you dont want to use Jquery then use following script (thanks to @Shaggy) as,

$(document).ready(function () { 
    if(!Modernizr.flexbox)document.getElementByID("browserupgrade").style.display="‌​block";
});
查看更多
旧时光的记忆
6楼-- · 2019-01-02 18:54

PREMISE

The JavaScript style property returns a complete collection of CSS properties that the browser supports for the specified element, which can be tested using the following Snippet:

for(var x in document.body.style)
    console.log(x);

This is true whether or not a particular property is explicitly set for the specified element. This can be tested by running the following Snippet in Chrome, for example; the first line will return false as Chrome does not yet support the unprefixed appearance property while the second line will return true.

console.log("appearance" in document.body.style);
console.log("-webkit-appearance" in document.body.style);
body{
    appearance:none;
}

However, it should be noted that if an unsupported property is set on an element using JavaScript, via the style property then checking for the presence of that property will return true:

document.body.style.appearance="none";
console.log("appearance" in document.body.style);

Therefore we will be using document.createElement() to create a temporary element so we can be sure none of the properties we are checking for have been set in this manner. (Thanks to Gothdo for this suggestion, which removed the need for any assumptions to be made.)


REQUIREMENTS

Looking at the list of browsers to be targeted:

  • Internet Explorer 10 supported flexbox with the -ms- prefix.
  • Safari 7 & 8 supported flexbox with the -webkit- prefix.
  • Opera Mini, according to caniuse.com, has supported flexbox without prefixing since version 5, however I cannot attest to the accuracy of that and am looking for someone to confirm it.
  • UC Browser currently (v9.9) supports flexbox with the -webkit- prefix.
  • Prior to v4.4, Android Browser supported flexbox with the -webkit- prefix.

SOLUTION

We can see from that list that all the browsers to be targeted require prefixing of the flexbox properties so we can target them simply by checking for the presence of any of the unprefixed properties in the temporary element's style collection. The following Snippet will return true when run in any of the above browsers:

console.log(!("flex" in document.createElement("p").style));

Armed with this knowledge we can now create a working solution to display the browser upgrade message to the necessary browsers.

if(!("flex" in document.createElement("p").style))
    document.getElementById("browserupgrade").style.display="block";
#browserupgrade{
    display:none;
    font-family:sans-serif;
}
<div id="browserupgrade">
    <p>You are using an outdated browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</div>


NOTES

Old Syntax

There was a version of the current flexbox spec that used a slightly different syntax with one of the differences being that the order property was named flex-order. To the best of my knowledge, the only browser that ever supported that syntax was IE10 which required prefixing so should, therefore, be targeted by the solution above. For the sake of completeness, though, in case there were ever any browsers that supported that syntax without prefixing, you can include an additional check for the presence of the flex-order property while also checking that the order property isn't present in case there are any browsers that support both versions of the syntax.

var checkstyle=document.createElement("p").style;
if(!("flex" in checkstyle)||("flex-order" in checkstyle&&!("order" in checkstyle)))
    document.getElementById("browserupgrade").style.display="block";
#browserupgrade{
    display:none;
    font-family:sans-serif;
}
<div id="browserupgrade">
    <p>You are using an outdated browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</div>

Firefox

Firefox added support for unprefixed flexbox properties in version 22 but did not support either the flex-flow or flex-wrap properties until version 28 so to add v22-27 to our targeting, we should instead check for the presence of one of those properties.

var checkstyle=document.createElement("p").style;
if(!("flex-flow" in checkstyle)||("flex-order" in checkstyle&&!("order" in checkstyle)))
    document.getElementById("browserupgrade").style.display="block";
#browserupgrade{
    display:none;
    font-family:sans-serif;
}
<div id="browserupgrade">
    <p>You are using an outdated browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</div>

Internet Explorer 11

Despite not requiring prefixing, IE11's support for flexbox is still extremely buggy so you might want to consider targeting it as well. There is no way of doing so using any of the flexbox properties, however IE 7-11 (sort of) supported the image-rendering property via the non-standard -ms-interpolation-mode property, with support later dropped in Edge/v12+ so we can instead check for that. There is no guarantee, though, that this property won't be added back in to a future version of Edge so that should be monitored.

var checkstyle=document.createElement("p").style;
if(!("flex-flow" in checkstyle)||("flex-order" in checkstyle&&!("order" in checkstyle))||"-ms-interpolation-mode" in checkstyle)
    document.getElementById("browserupgrade").style.display="block";
#browserupgrade{
    display:none;
    font-family:sans-serif;
}
<div id="browserupgrade">
    <p>You are using an outdated browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</div>

Opera Mini

Although caniuse.com claims full support for flexbox has existed in Opera Mini since version 5 and is widely considered to be an authoritative source for such information, if you still wish to target it, you can do so by checking for the presence of one of the myriad of properties it doesn't support. To keep things concise, though, I'd suggest replacing the -ms-interpolation-mode check with a check for transform-style which is not supported by Opera Mini nor by pre-Edge versions of IE. As with the previous check, this one should be monitored as future versions of Opera Mini which you might not want to target may continue not to support the transform-style property.

var checkstyle=document.createElement("p").style;
if(!("flex-flow" in checkstyle)||("flex-order" in checkstyle&&!("order" in checkstyle))||!("transform-style" in checkstyle))
    document.getElementById("browserupgrade").style.display="block";
#browserupgrade{
    display:none;
    font-family:sans-serif;
}
<div id="browserupgrade">
    <p>You are using an outdated browser. Please <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
</div>


UPDATES

13/05/16: Added additional checks to target more browsers after question was updated.
16/05/16: Added more detailed explanations of the proposed solution based on feedback provided in the comments and to make it worthy of the bounty on offer. Removed checks for old flexbox spec as they were redundant.
17/05/16: Added a temporary element to run the checks on, rather than using document.body, following Gothdo's suggestion, which led to the removal of suggesting the use of getElementsByTagName() for wider browser support.
23/05/16: Added notes with suggested checks for Internet Explorer 11 & Opera Mini.

查看更多
牵手、夕阳
7楼-- · 2019-01-02 18:56

EDIT:

There are two viable approaches: Modernizr or Feature Queries. Modernizr is the current stable solution, but feature queries @support, when fully supported by all browsers, will be the best pure CSS solution for css feature detection.

Currently you need to add some prefixes to target those browsers that do not support feature queries, ie. see LGSon's answer.

Modernizr approach:

You can create a flexbox custom Modernizr build pretty easy, as previously recommended: Modernizr Custom Flexbox Build. Reference your custom modernizr javascript file in your website and then add some Html markup and Css classes , something like this:

NOTE: Open the snippet in different browsers to check support.

TESTS:

No flexbox support: Internet Explorer 10

Flexbox support: Chrome 50

/*! modernizr 3.3.1 (Custom Build) | MIT *
 * https://modernizr.com/download/?-flexbox-setclasses !*/
! function(e, n, t) {
  function r(e, n) {
    return typeof e === n
  }

  function o() {
    var e, n, t, o, s, i, a;
    for (var l in C)
      if (C.hasOwnProperty(l)) {
        if (e = [], n = C[l], n.name && (e.push(n.name.toLowerCase()), n.options && n.options.aliases && n.options.aliases.length))
          for (t = 0; t < n.options.aliases.length; t++) e.push(n.options.aliases[t].toLowerCase());
        for (o = r(n.fn, "function") ? n.fn() : n.fn, s = 0; s < e.length; s++) i = e[s], a = i.split("."), 1 === a.length ? Modernizr[a[0]] = o : (!Modernizr[a[0]] || Modernizr[a[0]] instanceof Boolean || (Modernizr[a[0]] = new Boolean(Modernizr[a[0]])), Modernizr[a[0]][a[1]] = o), g.push((o ? "" : "no-") + a.join("-"))
      }
  }

  function s(e) {
    var n = x.className,
      t = Modernizr._config.classPrefix || "";
    if (_ && (n = n.baseVal), Modernizr._config.enableJSClass) {
      var r = new RegExp("(^|\\s)" + t + "no-js(\\s|$)");
      n = n.replace(r, "$1" + t + "js$2")
    }
    Modernizr._config.enableClasses && (n += " " + t + e.join(" " + t), _ ? x.className.baseVal = n : x.className = n)
  }

  function i(e, n) {
    return !!~("" + e).indexOf(n)
  }

  function a() {
    return "function" != typeof n.createElement ? n.createElement(arguments[0]) : _ ? n.createElementNS.call(n, "http://www.w3.org/2000/svg", arguments[0]) : n.createElement.apply(n, arguments)
  }

  function l(e) {
    return e.replace(/([a-z])-([a-z])/g, function(e, n, t) {
      return n + t.toUpperCase()
    }).replace(/^-/, "")
  }

  function f(e, n) {
    return function() {
      return e.apply(n, arguments)
    }
  }

  function u(e, n, t) {
    var o;
    for (var s in e)
      if (e[s] in n) return t === !1 ? e[s] : (o = n[e[s]], r(o, "function") ? f(o, t || n) : o);
    return !1
  }

  function d(e) {
    return e.replace(/([A-Z])/g, function(e, n) {
      return "-" + n.toLowerCase()
    }).replace(/^ms-/, "-ms-")
  }

  function p() {
    var e = n.body;
    return e || (e = a(_ ? "svg" : "body"), e.fake = !0), e
  }

  function c(e, t, r, o) {
    var s, i, l, f, u = "modernizr",
      d = a("div"),
      c = p();
    if (parseInt(r, 10))
      for (; r--;) l = a("div"), l.id = o ? o[r] : u + (r + 1), d.appendChild(l);
    return s = a("style"), s.type = "text/css", s.id = "s" + u, (c.fake ? c : d).appendChild(s), c.appendChild(d), s.styleSheet ? s.styleSheet.cssText = e : s.appendChild(n.createTextNode(e)), d.id = u, c.fake && (c.style.background = "", c.style.overflow = "hidden", f = x.style.overflow, x.style.overflow = "hidden", x.appendChild(c)), i = t(d, e), c.fake ? (c.parentNode.removeChild(c), x.style.overflow = f, x.offsetHeight) : d.parentNode.removeChild(d), !!i
  }

  function m(n, r) {
    var o = n.length;
    if ("CSS" in e && "supports" in e.CSS) {
      for (; o--;)
        if (e.CSS.supports(d(n[o]), r)) return !0;
      return !1
    }
    if ("CSSSupportsRule" in e) {
      for (var s = []; o--;) s.push("(" + d(n[o]) + ":" + r + ")");
      return s = s.join(" or "), c("@supports (" + s + ") { #modernizr { position: absolute; } }", function(e) {
        return "absolute" == getComputedStyle(e, null).position
      })
    }
    return t
  }

  function h(e, n, o, s) {
    function f() {
      d && (delete z.style, delete z.modElem)
    }
    if (s = r(s, "undefined") ? !1 : s, !r(o, "undefined")) {
      var u = m(e, o);
      if (!r(u, "undefined")) return u
    }
    for (var d, p, c, h, v, y = ["modernizr", "tspan", "samp"]; !z.style && y.length;) d = !0, z.modElem = a(y.shift()), z.style = z.modElem.style;
    for (c = e.length, p = 0; c > p; p++)
      if (h = e[p], v = z.style[h], i(h, "-") && (h = l(h)), z.style[h] !== t) {
        if (s || r(o, "undefined")) return f(), "pfx" == n ? h : !0;
        try {
          z.style[h] = o
        } catch (g) {}
        if (z.style[h] != v) return f(), "pfx" == n ? h : !0
      }
    return f(), !1
  }

  function v(e, n, t, o, s) {
    var i = e.charAt(0).toUpperCase() + e.slice(1),
      a = (e + " " + b.join(i + " ") + i).split(" ");
    return r(n, "string") || r(n, "undefined") ? h(a, n, o, s) : (a = (e + " " + E.join(i + " ") + i).split(" "), u(a, n, t))
  }

  function y(e, n, r) {
    return v(e, t, t, n, r)
  }
  var g = [],
    C = [],
    w = {
      _version: "3.3.1",
      _config: {
        classPrefix: "",
        enableClasses: !0,
        enableJSClass: !0,
        usePrefixes: !0
      },
      _q: [],
      on: function(e, n) {
        var t = this;
        setTimeout(function() {
          n(t[e])
        }, 0)
      },
      addTest: function(e, n, t) {
        C.push({
          name: e,
          fn: n,
          options: t
        })
      },
      addAsyncTest: function(e) {
        C.push({
          name: null,
          fn: e
        })
      }
    },
    Modernizr = function() {};
  Modernizr.prototype = w, Modernizr = new Modernizr;
  var x = n.documentElement,
    _ = "svg" === x.nodeName.toLowerCase(),
    S = "Moz O ms Webkit",
    b = w._config.usePrefixes ? S.split(" ") : [];
  w._cssomPrefixes = b;
  var E = w._config.usePrefixes ? S.toLowerCase().split(" ") : [];
  w._domPrefixes = E;
  var P = {
    elem: a("modernizr")
  };
  Modernizr._q.push(function() {
    delete P.elem
  });
  var z = {
    style: P.elem.style
  };
  Modernizr._q.unshift(function() {
    delete z.style
  }), w.testAllProps = v, w.testAllProps = y, Modernizr.addTest("flexbox", y("flexBasis", "1px", !0)), o(), s(g), delete w.addTest, delete w.addAsyncTest;
  for (var N = 0; N < Modernizr._q.length; N++) Modernizr._q[N]();
  e.Modernizr = Modernizr
}(window, document);
.support-container {
  display: none;
}
.no-flexbox .support-container {
  display: block;
  position: fixed;
  top: 0;
  left: 0;
  width: 100vw;
  height: 100vh;
  background-color: #CE3426;
  padding: 100px;
}
.support-container__update-browser {
  color: #ffffff;
  font-size: 2em;
}
.support-container__can-i-use {
  font-size: 1.2em;
  font-style: italic;
  color: #ffffdffffd;
}
.support-container__update-browser a,
.support-container__can-i-use a {
  background-color: #ffffff;
  text-decoration: underline;
  padding: 0 3px;
  border-radius: 4px;
  border-bottom: 2px solid rgba(0, 0, 0, 0.3);
}
<div class="support-container">
  <div id="browserupgrade" class="support-container__update-browser">
    <p>Dear user, you are using an outdated browser. Please
      <a href="http://browsehappy.com/">upgrade your browser</a> to improve your experience.</p>
  </div>
  <div class="support-container__can-i-use">
    <p>
      For more browser support info click
      <a href="http://caniuse.com/#feat=flexbox">here</a>
      .
    </p>
  </div>
</div>

FURTHER EXPLANATION:

Modernizr checks if the browser supports your custom feature in this case flexbox. If the browser does not support it, Modernizr adds a class to your <html> tag. Like this: <html class="no-flexbox>. Which you can use at your convenience in your stylesheet.


Additionally, you can add a function to detect the user's browser and add it to your update browser message for a better UX. Credit: kennebec

navigator.sayswho= (function(){
    var ua= navigator.userAgent, tem,
    M= ua.match(/(opera|chrome|safari|firefox|msie|trident(?=\/))\/?\s*(\d+)/i) || [];
    if(/trident/i.test(M[1])){
        tem=  /\brv[ :]+(\d+)/g.exec(ua) || [];
        return 'IE '+(tem[1] || '');
    }
    if(M[1]=== 'Chrome'){
        tem= ua.match(/\b(OPR|Edge)\/(\d+)/);
        if(tem!= null) return tem.slice(1).join(' ').replace('OPR', 'Opera');
    }
    M= M[2]? [M[1], M[2]]: [navigator.appName, navigator.appVersion, '-?'];
    if((tem= ua.match(/version\/(\d+)/i))!= null) M.splice(1, 1, tem[1]);
    return M.join(' ');
})();
查看更多
登录 后发表回答