135

So I'm trying to move a "close" button to the left side when the user is on Mac and the right side when the user is on PC. Now I'm doing it by examining the user agent, but it can be too easily spoofed for reliable OS detection. Is there a surefire way to detect whether the OS on which the browser is running is Mac OS X or Windows? If not, what's better than user agent sniffing?

Louis
  • 138,753
  • 27
  • 264
  • 309
alt
  • 12,593
  • 18
  • 75
  • 118
  • 27
    If the user manipulates the useragent, isn't that his or her problem? I'd worry about it when it hurts *you* for them to have an invalid useragent (e.g. when it gives them access to something you don't want them to have), but for something like this, why are you stressing? Let them shoot themselves in the foot and have to deal with the consequences - no sweat off your back, mate. – Mahmoud Al-Qudsi May 10 '12 at 05:29
  • well, more like a tip than an answer. You can detect IE with conditional comments. this is +1 to the windows detection arsenal. but this would fail if IE were run in an emulator in another OS (like Wine on Linux). By the way, how about linux? – Joseph May 10 '12 at 05:30
  • @MahmoudAl-Qudsi Even without spoofing, mobile Firefox often pretends it's Safari, Opera often pretends it's firefox in some versions. Without spoofing the user agent is still VERY unreliable. – alt May 10 '12 at 05:33
  • Possible duplicate: http://stackoverflow.com/q/7044944/55209 – Artem Koshelev May 10 '12 at 05:34
  • But that question's answer is just "user agents". – alt May 10 '12 at 05:35
  • You worry someone could be spoofing the useragent and get the button on the wrong side? – FrancescoMM Jan 13 '20 at 17:27

7 Answers7

224

The window.navigator.platform property is not spoofed when the userAgent string is changed. I tested on my Mac if I change the userAgent to iPhone or Chrome Windows, navigator.platform remains MacIntel.

navigator.platform is not spoofed when the userAgent string is changed

The property is also read-only

navigator.platform is read-only


I could came up with the following table

Mac Computers

Mac68K Macintosh 68K system.
MacPPC Macintosh PowerPC system.
MacIntel Macintosh Intel system.
MacIntel Apple Silicon (ARM)

iOS Devices

iPhone iPhone.
iPod iPod Touch.
iPad iPad.


Modern macs returns navigator.platform == "MacIntel" but to give some "future proof" don't use exact matching, hopefully they will change to something like MacARM or MacQuantum in future.

var isMac = navigator.platform.toUpperCase().indexOf('MAC')>=0;

To include iOS that also use the "left side"

var isMacLike = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
var isIOS = /(iPhone|iPod|iPad)/i.test(navigator.platform);

var is_OSX = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);
var is_iOS = /(iPhone|iPod|iPad)/i.test(navigator.platform);

var is_Mac = navigator.platform.toUpperCase().indexOf('MAC') >= 0;
var is_iPhone = navigator.platform == "iPhone";
var is_iPod = navigator.platform == "iPod";
var is_iPad = navigator.platform == "iPad";

/* Output */
var out = document.getElementById('out');
if (!is_OSX) out.innerHTML += "This NOT a Mac or an iOS Device!";
if (is_Mac) out.innerHTML += "This is a Mac Computer!\n";
if (is_iOS) out.innerHTML += "You're using an iOS Device!\n";
if (is_iPhone) out.innerHTML += "This is an iPhone!";
if (is_iPod) out.innerHTML += "This is an iPod Touch!";
if (is_iPad) out.innerHTML += "This is an iPad!";
out.innerHTML += "\nPlatform: " + navigator.platform;
<pre id="out"></pre>

Since most O.S. use the close button on the right, you can just move the close button to the left when the user is on a MacLike O.S., otherwise isn't a problem if you put it on the most common side, the right.

setTimeout(test, 1000); //delay for demonstration

function test() {

  var mac = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);

  if (mac) {
    document.getElementById('close').classList.add("left");
  }
}
#window {
  position: absolute;
  margin: 1em;
  width: 300px;
  padding: 10px;
  border: 1px solid gray;
  background-color: #DDD;
  text-align: center;
  box-shadow: 0px 1px 3px #000;
}
#close {
  position: absolute;
  top: 0px;
  right: 0px;
  width: 22px;
  height: 22px;
  margin: -12px;
  box-shadow: 0px 1px 3px #000;
  background-color: #000;
  border: 2px solid #FFF;
  border-radius: 22px;
  color: #FFF;
  text-align: center;
  font: 14px"Comic Sans MS", Monaco;
}
#close.left{
  left: 0px;
}
<div id="window">
  <div id="close">x</div>
  <p>Hello!</p>
  <p>If the "close button" change to the left side</p>
  <p>you're on a Mac like system!</p>
</div>

http://www.nczonline.net/blog/2007/12/17/don-t-forget-navigator-platform/

Vitim.us
  • 18,445
  • 15
  • 88
  • 102
  • 4
    @Vitim.us thanks a lot for the detailed answer, you really saved my day :) – vivekkupadhyay Sep 28 '16 at 09:38
  • 4
    MacQuantum made my day. – Íhor Mé Apr 30 '17 at 17:22
  • The platform property is read-only, but it is still possible to spoof it: https://stackoverflow.com/questions/2166540/how-can-i-fool-a-site-that-looks-at-the-javascript-object-navigator-to-see-tha – Ryan Burbidge Feb 15 '18 at 23:13
  • Just a note: `x?true:false` can (and should) be replaced with `Boolean(x)`. – Qix - MONICA WAS MISTREATED Dec 26 '18 at 07:21
  • @Qix: ... or simply with `!!x`. Or `match() != null` – biziclop Jan 17 '19 at 16:31
  • @biziclop `!!x` is slow, and violates a lot of OSS standard style guides (for good reason). Also `!=` is a non-strict equality operator and also violets most OSS style guides. `Boolean(x)` is the _most performant_ way to coerce a value to a boolean in Javascript. – Qix - MONICA WAS MISTREATED Jan 18 '19 at 14:21
  • @Qix AFAIK Boolean() is slower the last time I checked, idk if thats hold up to today. Either way this is something you should check just once, so any optimization is regarding speed is a waste of time, you should go for clarity, in that case Boolean makes sense. Also if you minify your code it will most likely be replaced with `!!x` – Vitim.us Jan 18 '19 at 17:56
  • @Qix: generally I use `===` everywhere, *except* when comparing to `null`/`undefined` because they `==` only with each other. – biziclop Jan 18 '19 at 19:13
  • @Vitim.us Don't write minified code though. It helps no one. We write code for humans, not for machines. – Qix - MONICA WAS MISTREATED Jan 19 '19 at 16:09
  • @Qix that's not what I meant, I agree to write code for humans, I'm just saying that changing one style over another for performance is most likely wasted effort, because your decision will be overwritten by build tools, unless you don't minify or carefully tweak your compiler/minifier. – Vitim.us Jan 21 '19 at 12:44
  • 1
    @Qix An even better improvement would be to replace `str.match(regexp) ? true : false` with `regexp.test(string)`. The [`RegExp.prototype.test()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test) method natively returns a boolean. Thus, my preferred code is `const platformIsMacLike = /(Mac|iPhone|iPod|iPad)/i.test(navigator.platform);`. – Rory O'Kane Apr 02 '19 at 20:33
  • @RoryO'Kane ty for the suggestion, edited. Aside I just noticed SO syntax highlighting gets it wrong `/a/i.test()` leaves the modifier unhighlighted. – Vitim.us Apr 13 '19 at 19:27
  • 2
    Since 2019 iPads (i.e. iPadOS) think they are a `MacIntel` :rolleyes: – frnhr Dec 15 '19 at 20:09
  • @frnhr that's because they want the iPad to look like a computer. And they have a good reason to do that, because developers chose to make less capable websites for "mobile", having to switch to a "real computer" to be able to do something is utterly bad experience. – Vitim.us Dec 17 '19 at 14:34
  • @Vitim.us yeah sure, but still sometimes we *really* need to know if it's an iPad or not... – frnhr Dec 17 '19 at 19:22
  • If anyone's interested in what the new M1 macs report, see here: https://stackoverflow.com/questions/64853342/whats-the-value-of-navigator-platform-on-arm-macs --- it looks like (at least for now) they still return `MacIntel` – Toastrackenigma Nov 29 '20 at 02:22
  • 14
    Note that this property is [now considered obsolete](https://developer.mozilla.org/en-US/docs/Web/API/Navigator/platform#browser_compatibility) and is slated for removal at any time by any browser, so this is not longer the right answer and as a highly-upvoted accepted answer should probably be updated to explain how to detect the platform in a world where `navigator.platform` no longer exists. – Mike 'Pomax' Kamermans Sep 06 '21 at 16:16
69

It's as simple as that:

function isMacintosh() {
  return navigator.platform.indexOf('Mac') > -1
}

function isWindows() {
  return navigator.platform.indexOf('Win') > -1
}
barhatsor
  • 1,635
  • 5
  • 19
Benny Neugebauer
  • 46,191
  • 22
  • 218
  • 190
  • 1
    Well, that covers _two_ operating systems. Here's a more comprehensive (but possibly still incomplete) list, which includes Linux, BSD, Android, Palm, Sony Playstations, etc.: http://stackoverflow.com/questions/19877924/what-is-the-list-of-possible-values-for-navigator-platform-as-of-today – Michael Scheper Mar 22 '16 at 22:36
  • If you want to cover more operating systems, then you should go for a library like `Platform.js`: https://github.com/bestiejs/platform.js/ – Benny Neugebauer Mar 23 '16 at 09:15
  • @BennyNeugebauer `isPC` should not equal to `!isMacintosh();`. What if the user is on linux or any other different platform? It will detect that they are not on mac and think that they are on PC. – Mystical Jan 06 '19 at 17:08
  • @EternalDarknessThat's why I called it `isPC` (and not `isWindows` or `isLinux`) because Linux runs on PCs but macOS only runs on Macs. – Benny Neugebauer Jan 10 '19 at 15:25
  • why `indexOf('Mac')` instead of `startsWith('Mac')`? – Dror Bar Dec 02 '20 at 17:03
  • 1
    @DrorBar the `startsWith` method was introduced with ECMAScript 2015 (6th Edition). If you want the check to work in browsers which don't support ES6, you will have to use `indexOf` (it has a wider compatibility). – Benny Neugebauer Dec 03 '20 at 12:39
  • 1
    Note that `navigator.platform` is no longer part of the web standard, so this probably needs an edit to explain that while this worked back in 2015, it's no longer the right solution. – Mike 'Pomax' Kamermans Sep 06 '21 at 16:18
6

Is this what you are looking for? Otherwise, let me know and I will remove this post.

Try this jQuery plugin: http://archive.plugins.jquery.com/project/client-detect

Demo: http://www.stoimen.com/jquery.client.plugin/

This is based on quirksmode BrowserDetect a wrap for jQuery browser/os detection plugin.

For keen readers:
http://www.stoimen.com/blog/2009/07/16/jquery-browser-and-os-detection-plugin/
http://www.quirksmode.org/js/support.html

And more code around the plugin resides here: http://www.stoimen.com/jquery.client.plugin/jquery.client.js

Tot Zam
  • 7,839
  • 9
  • 49
  • 72
Tats_innit
  • 33,645
  • 9
  • 68
  • 76
  • 2
    DUDE! I spoofed my user agent and it still detected me on Safari for Mac! Thanks BRUV. – alt May 10 '12 at 05:37
  • 1
    @JacksonGariety No worries bruv :)) read further details here has the whole code resinding :) http://www.stoimen.com/jquery.client.plugin/jquery.client.js Have a nice one brosniac :) cheers! – Tats_innit May 10 '12 at 05:40
  • @Derek hiya bruv - it uses quirkmode browser plugin and detects for the browser/os see here http://www.quirksmode.org/js/detect.html and further see http://www.stoimen.com/jquery.client.plugin/jquery.client.js & http://www.quirksmode.org/js/support.html hope this make sense bruv :) – Tats_innit May 10 '12 at 05:45
  • Nevermind, this appears to just be using user agents... that kinda sucks. – alt May 10 '12 at 08:05
  • 1
    Not really the answer I'm looking for. Is it even possible? – alt May 10 '12 at 08:19
  • @JacksonGariety Hiya do you wanna chat I am happy to help you if I can, I assume this plugin do work for you though probably good idea might be see how this plugin does browser and os detect? have a nice one and cheerios! – Tats_innit May 10 '12 at 08:24
  • Link only answers are discouraged since links expire and break, like the first link in this answer. Instead, include the main points from the links in the actual answer, in addition to the links. – Tot Zam Aug 21 '17 at 04:46
4

You can test this:

function getOS() {
  let userAgent = window.navigator.userAgent.toLowerCase(),
    macosPlatforms = /(macintosh|macintel|macppc|mac68k|macos)/i,
    windowsPlatforms = /(win32|win64|windows|wince)/i,
    iosPlatforms = /(iphone|ipad|ipod)/i,
    os = null;

  if (macosPlatforms.test(userAgent)) {
    os = "macos";
  } else if (iosPlatforms.test(userAgent)) {
    os = "ios";
  } else if (windowsPlatforms.test(userAgent)) {
    os = "windows";
  } else if (/android/.test(userAgent)) {
    os = "android";
  } else if (!os && /linux/.test(userAgent)) {
    os = "linux";
  }

  return os;
}

document.getElementById('your-os').textContent = getOS();
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
        <h1 id="your-os"></h1>
  </body>
</html>
Tomerikoo
  • 15,737
  • 15
  • 35
  • 52
3

For completion: Some browsers support navigator.userAgentData.platform, which is a read-only property.

console.log(navigator.userAgentData.platform);
// macOs

Please be aware that Navigator.platform is deprecated.

Niekes
  • 341
  • 1
  • 3
  • 11
1

Let me know if this works. Way to detect an Apple device (Mac computers, iPhones, etc.) with help from StackOverflow.com:
What is the list of possible values for navigator.platform as of today?

var deviceDetect = navigator.platform;
var appleDevicesArr = ['MacIntel', 'MacPPC', 'Mac68K', 'Macintosh', 'iPhone', 
'iPod', 'iPad', 'iPhone Simulator', 'iPod Simulator', 'iPad Simulator', 'Pike 
v7.6 release 92', 'Pike v7.8 release 517'];

// If on Apple device
if(appleDevicesArr.includes(deviceDetect)) {
    // Execute code
}
// If NOT on Apple device
else {
    // Execute code
}
0

I think all Chrome or Chromium-based browser will return MacIntel on the macOS platform regardless of the hardware architecture.

Check the Chromium Source code for further details.

Subhajit
  • 371
  • 3
  • 18