Detect Metro UI Version of IE

2020-01-29 04:46发布

问题:

What's the fastest method, to detect User-Agent as metro UI version of internet-explorer >=10 ?

回答1:

So, there doesn't appear to be a definitive test to identify Metro IE vs Desktop IE, but there does seem to be a few different pieces of data you can attempt to use to assume that it is Metro. Unfortunately, none of the items I have found can't be explained away by other settings. In other words, for all the "feature" tests I have found, Desktop IE could be configured in a way to trick the tests into thinking it was running on Metro.

Is ActiveX disabled (Metro doesn't allow any activex content, but desktop IE can have it set to disabled as well):

function isActivexEnabled() {
    var supported = null;        
    try {
        supported = !!new ActiveXObject("htmlfile");
    } catch (e) {
        supported = false;
    }

    return supported;
}

User Agent string check (Metro will always run in 64bit mode, but won't on a 32bit machine, and Desktop IE can be configured to run in 64bit mode as well not sure how popular either of those options will be)

function isWin64() {
    return navigator.platform == "Win64";
}

Full screen check (Metro will always be in full screen mode, however Desktop IE can also run in full screen mode, but this could be used as supporting evidence of Metro mode)

function isFullScreen() {
   return (window.innerWidth == screen.width && 
           window.innerHeight == screen.height);
}

In short, I think you have to try to check a bunch of features, and then guess, there is no definitive way. Or you could just accept that MS doesn't want you to do this, and use feature detection for the features you want to use.

For those that want to try to provide UI to refer to the containing browser UI (to indicate how to Pin the web page for example), keep in mind that other Metro apps can embed the IE10 Metro browser as a control, so even if you could identify the browser as Metro vs desktop, the UI might not be where you'd attempt to refer to it, so this can end up being a pretty tricky situation to get right 100% of the time. So either, don't try, or you could attempt the other detection techniques and accept that there are use cases that you could be displaying the wrong UI.



回答2:

I don't believe there is a way to determine if a request is coming from the Metro version of IE10 versus the regular desktop mode version. Metro IE has no unique HTTP headers or user-agent string to identify it by.

Internet Explorer 10 User Agent Strings On Windows 8 64bit

Metro IE10 is a 64-bit application, so you'll see "Win64" in the user-agent string. Regular desktop IE10, by default, is 32-bit and will show "WOW64". You could sort-of make an educated guess based on this, but you would falsely identify anyone who chose to run the 64-bit version of IE10 in desktop mode. As you can see from that chart, the user-agent strings are identical. [edit: as noted in the comments, this won't work for 32-bit PCs.]

I could see why you may want to detect Metro IE, but this type of "browser sniffing" is generally discouraged because it's so cumbersome and error-prone. It would be best to feature test for certain browser functionality that you require using something like Modernizr, if you can.



回答3:

Thanks John Rajakumar and Alexis Pigeon. I was able to use your Metro IE check as the basis to load a separate CSS style sheet for Metro tablets (MS Surface). To add a bit more certainty in my mind, I used cssuseragent to detect IE10 first, and then combined both tests in a final yepnope test.

var IEmetrotest = ((cssua.userAgent.ie > 9) && (metrotest()));
yepnope({
    test: IEmetrotest,
    yep : ['ie_metro.css']
    });

Tested it in Browserstack and it's working as expected. (I would have up-ticked you, but don't have enough points yet.)

// Minor revisions to isBrowserSupportPlugin() previously posted
// Renamed function to metrotest() and inverted the returned value. 
var errorName = null;
function metrotest() {
    var supported = null; 
    try {
        new ActiveXObject("");
    }
    catch (e) {
        // FF has ReferenceError here
        errorName = e.name; 
    }     
    try {
        supported = !!new ActiveXObject("htmlfile");
    } catch (e) {
        supported = false;
    }
    if(errorName != 'ReferenceError' && supported==false){
        supported = false;
    }else{
        supported =true;
    }
    return !supported;
}


回答4:

METRO IE never supports ActiveX or any plugin objects. Based on that the following script is tested & working fine.

//---------------------------Metro IE check-------------------------
    var errorName = null;
    function isBrowserSupportPlugin() {
        var supported = null; 
        try {
            new ActiveXObject("");
        }
        catch (e) {
            // FF has ReferenceError here
            errorName = e.name; 
        }     
        try {
            supported = !!new ActiveXObject("htmlfile");
        } catch (e) {
            supported = false;
        }
        if(errorName != 'ReferenceError' && supported==false){
            supported = false;
        }else{
            supported =true;
        }
        return supported;
    }
//----------------------------------------------------------------   


回答5:

Here is a consistent method, with no ActiveX check dependency, as tested in IE10 and IE11 specifically.

if (window.screenY === 0 && (window.innerHeight+1) !== window.outerHeight){ //IE metro/modern UI }

The first rule detect IE's full screen or modern view (which may or can also be true on IEMobile). Maximized mode being historically always associated with positive or negative screenX and screenY.

And the last rule excludes [Full screen/F11] mode, which for some reason constantly shows a 1px discrepancy between outerHeight and innerHeight as tested on IE10/Win8 and IE11/Win8.1

PS: I voluntarily do not declare window.screenX === 0. Only use screenY in order to cover Modern/Metro in snap-mode with a window context snapped to the right (i.e. screenX !== 0).



回答6:

Reliable Modern/Metro detection!

I have found a way to detect Modern versus Desktop which you can try here: http://jsbin.com/genac/2 (edit using Chrome or Firefox not IE http://jsbin.com/genac/2/edit).

It seems to be a fairly robust detection because:

  • it works even if pinch-zoomed
  • it works even if two windows snapped side by side on Modern(Metro)
  • it works even if page zoomed using ctrl-+ and ctrl--
  • it works even if desktop is fullscreen using F11 key on Desktop (autohiding taskbar)

The trick is that the page scrollbars on Modern do not make the client window size smaller (they appear above the page), but the page scrollbars on Desktop do affect client window size (usable window size is smaller).

The main downside is that the test requires that you have a scrollbar on the page to sniff the difference. Does the same difference in scrollbars happen in an iframe or autoscrollable div?

Edit: I would test that the browser is IE11 before using this code, because Windows 10 doesn't have Metro/Modern and Edge acts slightly differently.

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=EDGE" />
  <title>Metro width detector - robocat</title> 
  <script>
    function show() {
      var div = document.createElement('div');
      var s = [];
      s.push('IS METRO? ' + ((tester.offsetWidth == window.outerWidth) ? 'YES' : 'NO'));
      s.push('document.documentElement.clientWidth: ' + document.documentElement.clientWidth);
      s.push('document.documentElement.offsetWidth: ' + document.documentElement.offsetWidth);
      s.push('window.innerWidth: ' + window.innerWidth);
      s.push('window.outerWidth: ' + window.outerWidth);
      s.push('screen.width: ' + screen.width);
      s.push('screen.availWidth: ' + screen.availWidth);
      s.push('document.documentElement.getBoundingClientRect().width: ' + document.documentElement.getBoundingClientRect().width);
      s.push('tester.offsetWidth: ' + tester.offsetWidth);
      div.innerHTML = s.join('<br>');
      document.body.insertBefore(div, document.body.firstChild);
    }
    window.onresize = function() {
      show();
    }
  </script>
</head>
  <body onload="show()" style="margin:0">
    <div id="tester" style="position:absolute;left:0;right:0;"></div>
  <div style="height:10000px;width:10px;background-color:blue;"></div>
</body>
</html>

You can look at page/window dimensions using: http://jsbin.com/AbimiQup/10 (edit using Chrome or Firefox not IE http://jsbin.com/AbimiQup/10/edit)

PS: There might be some way to sniff the scrollbar difference from the runtimeStyle for body or document.documentElement because maybe -ms-overflow-style: -ms-autohiding-scrollbar is used (e.g. document.documentElement.runtimeStyle.msOverflowStyle) but I couldn't find anything.

PPS: The ActiveX method given in other answers is not particularly reliable (e.g. users can disable) and it causes UI uglyness because in IE11 it shows a "ActiveX" icon next to the url.



回答7:

By the sounds of it you are looking to do something along the lines of Pinning a site to the start screen within Metro. To do that you can check if a site can be pinned by using this code that was brought in with IE9 and can be done by doing something like this...

function doChecks_Browser() {    
  // full pin abilities == can pin && Win7+    
  if (pinnedSiteDetection.hasFullPinAbilityBrowser)
}

More info on that here.

From this this post on MSDN...

Windows 8 Release Preview implements pinned sites by using tiles on the Start screen. When a user clicks the tile of a pinned site, the site opens in Windows Internet Explorer 10 Release Preview in the Windows Metro style UI environment.

Hope that helps!



回答8:

I tested the following and it works, a jsfiddle showing this can be found here.


This is a bit of long shot, but seems quite a sensible solution:

Why not check whether the browser is full screen1 and depending on that call the fullscreen one metro. I have windows 8 running on my computer at work, so I will try to check tomorrow whether there are any GUI's still around (don't think I remember any) and if so, you would need to manually subtract them. True, it's not the most beautiful solution, but as far as I know there won't be any 'beautiful' solution and this could prove to be a pretty solid hack, as the GUI of metro IE can't be changed with any toolbars or similar software (as far as I know). True, it could lead to a misidentification of the desktop IE, but even that is within reason, as a fullscreen desktop IE will still give a similar experience.


1 Giving something along the lines of:

if(window.innerWidth == screen.width && window.innerHeight == screen.height) {
  // metro
} else {
  // desktop
}


回答9:

It tried out the code below and it seems to work.
It doesn't catch if the user goes InternetOptions->CustomLevel->ScriptActivexControlsMarkedSafeForScripting=false on you though. If the user does this the code believes it's a Metro/Modern.
Presently I see no way around this by checking OS, FullScreen and whatnot.

isWinModern checks for Metro/Modern.
isWinDesktop checks for Windows as Desktop (=non modern in this case)

The code below is not guaranteed to work.

    function isWin8Modern() {
        if (!!window.ActiveXObject) {
            try {
                !!new window.ActiveXObject('htmlfile');
                return false;
            } catch (exc) {
                return true;
            }
        } else {
            return false;
        }
    }

    function isWinDesktop() {
        if (window.ActiveXObject) {
            try {
                new window.ActiveXObject('htmlfile');
                return true;
            } catch (exc) {
                return false;
            }
        } else {
            return false;
        }
    }


回答10:

var _ua = window.navigator.userAgent;
var isIDevice = (/iphone|ipod|ipad/i).test(_ua);
var isMobileChrome = (_ua.indexOf('Android') > -1 && (/Chrome\/[.0-9]*/).test(_ua) && _ua.indexOf("Version") == -1);
var isMobileIE = _ua.indexOf('Windows Phone') > -1;
function isActivexEnabled()
{
    var supported = null;        
    try
    {
        supported = !!new ActiveXObject("htmlfile");
    } 
    catch (e) 
    {
        supported = false;
    }
    return supported;
}
if (!isIDevice && !isMobileChrome && !isMobileIE &&  _ua.toLowerCase().indexOf("wow") == -1 && !isActivexEnabled())
{ 
    //IE Metro UI
}

This Work For Me..



回答11:

I spent some time on this and figured out a way. I thought about what is missing from IE10 Metro and only one thing that is documented isn't supported in the Metro UI. The developer tools. Well, those tools have objects in the window object. One of these is "__IE_DEVTOOLBAR_CONSOLE_COMMAND_LINE".

If you do something like

if (window.__IE_DEVTOOLBAR_CONSOLE_COMMAND_LINE)
{
    // In IE 10 Standard UI
}
else
{
    // In IE 10 Metro UI
}

I've tested it in a few different environments and it appears to work most places. The only hiccup I can think of is if the developer tools were somehow disabled on the user's browser.