I have an app, and I'd like to redirect the users to different pages based on where they are navigating from.
If navigating from web clip, do not redirect.
If navigating from mobile Safari, redirect to safari.aspx.
If navigating from anywhere else, redirect to unavailable.aspx
I was able to use iPhone WebApps, is there a way to detect how it was loaded? Home Screen vs Safari? to determine if the user was navigating from a web clip, but I'm having trouble determining if the user navigated from mobile Safari on an iPhone or iPod.
Here's what I have:
if (window.navigator.standalone) {
// user navigated from web clip, don't redirect
}
else if (/*logic for mobile Safari*/) {
//user navigated from mobile Safari, redirect to safari page
window.location = "safari.aspx";
}
else {
//user navigated from some other browser, redirect to unavailable page
window.location = "unavailable.aspx";
}
See https://developer.chrome.com/multidevice/user-agent#chrome_for_ios_user_agent - the user agent strings for Safari on iOS and for Chrome on iOS are inconveniently similar:
Chrome
Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; en) AppleWebKit/534.46.0 (KHTML, like Gecko) CriOS/19.0.1084.60 Mobile/9B206 Safari/7534.48.3
Safari
Mozilla/5.0 (iPhone; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543 Safari/419.3
Looks like the best approach here is to first of all check for iOS as other answers have suggested and then filter on the stuff that makes the Safari UA unique, which I would suggest is best accomplished with "is AppleWebKit and is not CriOS":
var ua = window.navigator.userAgent;
var iOS = !!ua.match(/iPad/i) || !!ua.match(/iPhone/i);
var webkit = !!ua.match(/WebKit/i);
var iOSSafari = iOS && webkit && !ua.match(/CriOS/i);
UPDATE: This is a very old answer and I cannot delete it because the answer is accepted. Check unwitting's answer below for a better solution.
You should be able to check for the "iPad" or "iPhone" substring in the user agent string:
var userAgent = window.navigator.userAgent;
if (userAgent.match(/iPad/i) || userAgent.match(/iPhone/i)) {
// iPad or iPhone
}
else {
// Anything else
}
best practice is:
function isMobileSafari() {
return navigator.userAgent.match(/(iPod|iPhone|iPad)/) && navigator.userAgent.match(/AppleWebKit/)
}
Merged all the answers and comments.
This is the result.
function iOSSafari(userAgent) {
return /iP(ad|od|hone)/i.test(userAgent) &&
/WebKit/i.test(userAgent) &&
!(/(CriOS|FxiOS|OPiOS|mercury)/i.test(userAgent));
}
// Usage:
// if iOSSafari(window.navigator.userAgent) { /* iOS Safari code here */ }
// Testing:
var iPhoneSafari = [
"Mozilla/5.0 (iPhone; CPU iPhone OS 14_4_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (Apple-iPhone7C2/1202.466; U; CPU like Mac OS X; en) AppleWebKit/420+ (KHTML, like Gecko) Version/3.0 Mobile/1A543 Safari/419.3",
"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.34 (KHTML, like Gecko) Version/11.0 Mobile/15A5341f Safari/604.1",
"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A372 Safari/604.1",
"Mozilla/5.0 (iPhone; CPU iPhone OS 11_0 like Mac OS X) AppleWebKit/604.1.38 (KHTML, like Gecko) Version/11.0 Mobile/15A5370a Safari/604.1",
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/12.0 Mobile/15E148 Safari/604.1",
"Mozilla/5.0 (iPhone9,3; U; CPU iPhone OS 10_0_1 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A403 Safari/602.1",
"Mozilla/5.0 (iPhone9,4; U; CPU iPhone OS 10_0_1 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) Version/10.0 Mobile/14A403 Safari/602.1",
];
console.log("All true:", iPhoneSafari.map(iOSSafari));
var iPhoneNotSafari = [
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/69.0.3497.105 Mobile/15E148 Safari/605.1",
"Mozilla/5.0 (iPhone; CPU iPhone OS 12_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) FxiOS/13.2b11866 Mobile/16A366 Safari/605.1.15",
];
console.log("All false:", iPhoneNotSafari.map(iOSSafari));
Falling code only find mobile safari and nothing else (except dolphin and other small browsers)
(/(iPad|iPhone|iPod)/gi).test(userAgent) &&
!(/CriOS/).test(userAgent) &&
!(/FxiOS/).test(userAgent) &&
!(/OPiOS/).test(userAgent) &&
!(/mercury/).test(userAgent)
Seeing all the answers, here are some tips about the proposed RegExes:
AppleWebKit matches Desktop Safari too (not only mobile)
no need to call .match more than once for such simple regexes, and prefer the lighter .test method.
the g (global) regex flag is useless while the i (case insensitive) can be useful
no need for capture (parenthesis), we are just testing the string
I'm just using this since getting true for mobile Chrome is OK for me (same behavior):
/iPhone|iPad|iPod/i.test(navigator.userAgent)
(I just want to detect if the device is a target for an iOS app)
This regex works for me, clean and simple:
const isIOSSafari = !!window.navigator.userAgent.match(/Version\/[\d\.]+.*Safari/);
Actually, there isn't a silver bullet of detecting mobile safari. There are quite a few browsers may use the keywords of the user agent of mobile safari. Maybe you can try feature detection and keep updating the rule.
I know this is an old thread, but I'd like to share my solution with you guys.
I needed to detect when an user navigates from Desktop Safari (Because we're in middle 2017, and Apple hasn't give any support for input[type="date"] YET...
So, I made a fallback custom datepicker for it) . But only applies to safari in desktop because that input type works fine in mobile Safari. So, I made this Regex to only detect desktop Safari. I already tested it and it doesn't match with Opera, Chrome, Firefox or Safari Mobile.
Hope it may help some of you guys.
if(userAgent.match(/^(?!.*chrome).(?!.*mobile).(?!.*firefox).(?!.*iPad).(?!.*iPhone).*safari.*$/i)){
$('input[type="date"]').each(function(){
$(this).BitmallDatePicker();
})
}
I upvoted #unwitting 's answer, as it inevitably got me going. However, when rendering my SPA in an iOS Webview, I needed to tweak it a bit.
function is_iOS () {
/*
Returns (true/false) whether device agent is iOS Safari
*/
var ua = navigator.userAgent;
var iOS = !!ua.match(/iPad/i) || !!ua.match(/iPhone/i);
var webkitUa = !!ua.match(/WebKit/i);
return typeof webkitUa !== 'undefined' && iOS && webkitUa && !ua.match(/CriOS/i);
};
The main difference being, the renaming of webkit to webkitUa, so as to prevent clashing with the root webkit object used as a message handler between the SPA & UIView.
I was looking for this answer and I remembered I came across this before.
The most reliable way to detect Safari on iOS in JavaScript is
if (window.outerWidth === 0) {
// Code for Safari on iOS
}
or
if (window.outerHeight === 0) {
// Code for Safari on iOS
}
For some reason Safari on iOS returns 0 for window.outerHeight property and window.outerWidth property.
This is for all iPads and iPhones on all versions of iOS. Every other browser and device this property works normally.
Not sure if they intend to change this but for now it works well.
Based on unwitting answer here is regex if someone needs it.
/^(?=.*(iPhone|iPad|iPod))(?=.*AppleWebKit)(?!.*(criOS|fxiOS|opiOS|chrome|android)).*/i
It's been many years and Apple still hasn't fixed their bottom fixed positioning (100vh) bug on mobile Safari. Even though every other browser works fine.
So, There's also a vender property that'll say "Google, Inc" for Chrome and even Brave, other Webkit browsers if the userAgent is Webkit Safari unless it says "Apple Computer Inc." in which case it's Safari.
To avoid false positives in Desktop Safari, you need the maxTouchPoints property as well, which will be 5 for mobile and 0 for desktop. Horrifying, I know.
const isSafari =
navigator.userAgent.match(/safari/i) &&
navigator.vendor.match(/apple/i) &&
navigator.maxTouchPoints;
if (isSafari)
$("body").classList.add("mobile-safari");
Then make adjustments in the CSS using that mobile-safari class.
function isIOS {
var ua = window.navigator.userAgent;
return /(iPad|iPhone|iPod).*WebKit/.test(ua) && !/(CriOS|OPiOS)/.test(ua);
}
var isApple = false;
if(/iPhone|iPad|iPod/i.test(navigator.userAgent)) {
isApple = true;
}
Related
Chrome DevTools has the option to use the device emulation mode.
I know there's a way to test whether the mode is on or not. But I'd like to know when it's being activated or deactivated, on click.
Are there any events I could listen to, fired by the browser, that indicate the mode was turned on or off?
I ended up doing this:
$(window).on('orientationchange', function(e) {
if (e.tagret && e.target.devicePixelRatio > 1) {
// Emulation mode activated
} else {
// Emulation mode deactivated
}
});
Works for Google Chrome (my version: 58.0). Is it the bulletproof way? Not sure. It's enough for my needs, though.
orientationchange docs here.
My solution:
$(window).on('orientationchange', function(e) {
setTimeout(function() {
var emulationModeActivated = window.navigator.userAgent.indexOf('Mobile') !== -1;
}, 0);
});
Chrome adds Mobile to userAgent in device emulation mode, for example "Mozilla/5.0 (iPhone; CPU iPhone OS 10_3 like Mac OS X) AppleWebKit/602.1.50 (KHTML, like Gecko) CriOS/56.0.2924.75 Mobile/14E5239e Safari/602.1"
e.target.devicePixelRatio isn't usable on Mac with Retina Display as value is always > 1
I have a javascript like following acting bizarre. Following works fine on my development machine in all three browsers chrome, FF and safari. But when I checked on anyone else machine browsers this value update does not work at all. Does anyone know how would I able to replicate this issue on my local ?
my browser test
Browser name = Chrome
Full version = 51.0.2704.103
Major version = 51
navigator.appName = Netscape
navigator.userAgent = Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36
jQuery(function(){
$(document).on("click", "#history span", function() {
var field_id = $(this).attr('history-id');
$('#'+field_id).val('some new value');
})
});
You are missing an ending parenthesis
jQuery(function(){
$(document).on("click", "#body", function() {
console.log('sup world!')
}) // ending paren
});
Android device is a part of a user-agent string:
Mozilla/5.0 (Linux; U; Android 4.0.4; en-gb; GT-I9100 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
Opera/9.80 (Android 4.0.3; Linux; Opera Mobi/ADR-1212030829) Presto/2.11.355 Version/12.10
Mozilla/5.0 (Linux; U; Android 4.1.1; en-us; SAMSUNG-SGH-I317 Build/JRO03C) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30
However, the device might reside in different indexes, when splitting the string by ; (and sometimes it just isn't there). Is there an easy way to fetch the device name out in Javascript/Ruby?
Update: just to clarify: I am interested to fetch a device name, not an Android/version
If it isn't there, there's no way to get it is there.. If it is in the form of "Android x.y.z;", regular expressions are your friend
assuming "user_agent" contains the user-agent string:
Ruby, e.g.
user_agent[/Android (.*?);/]
Variable $1 will contain the version.
Javascript
var version = user_agent.match("Android (.*?);")[1];
If you just need a way to know whether it is an Android device or not, the Javascript code may be as simple as:
function isAndroid() {
var ua = navigator.userAgent.toLowerCase();
return (ua.indexOf("android") > -1);
}
If you are interested in version too, try this Javascript code:
function getAndroidVersion() {
var androidVersion = -1; // def value, for non-Android devices
var ua = navigator.userAgent.toLowerCase();
var androidIndex = ua.indexOf('android');
if(androidIndex != -1) {
var versionStartIndex = androidIndex + 8;
var versionEndIndex = ua.indexOf(';', versionStartIndex);
androidVersion = parseFloat(ua.slice(versionStartIndex, versionEndIndex));
}
return androidVersion;
}
I'm looking for the best way to detect a webOS tablet using plain JS and if it's any easier also using jQuery. The user agent of the tablet should look something like this:
User-Agent:Mozilla/5.0 (webOS/1.3; U; en-US) AppleWebKit/532.2 (KHTML, like Gecko) Version/1.0 Safari/532.2 Desktop/1.0
So an easy way would be:
var deviceAgent = navigator.userAgent.toLowerCase();
webOS = deviceAgent.match(/(webos)/);
Is that the best way to do it already? You're likely going to say detect the feature you need to make certain is present but that won't work for me because the feature I want is present but not working as it would on any desktop, so I really just want to know is this a webOS device or not.
Update: Just found that the tablet really uses another user agent:
Mozilla/5.0 (hp-tablet; Linux; hpwOS/3.0.0; U; xx-xx) AppleWebKit/534.6 (KHTML, like Gecko) wOSBrowser/233.48 Safari/534.6 TouchPad/1.0
So the above should probably rather be:
var deviceAgent = navigator.userAgent.toLowerCase();
webOS = deviceAgent.match(/(webos|hpwos)/);
Here's a function in PHP that will detect WebOS and any other mobile device you could need. Less than 1kb in code =)
function detectMobileDevice() {
if(preg_match('/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i', $_SERVER['HTTP_USER_AGENT'])) {
return true;
}
else {
return false;
}
}
if you want to do ONLY webOS, change line 2 to:
if(preg_match('/(webos)/i', $_SERVER['HTTP_USER_AGENT'])) {
to use:
if(detectMobileDevice()) {
// If mobile device detected, do something
}
else {
// Otherwise, do something else...
}
if you need more details, visit here: http://www.justindocanto.com/scripts/detect-a-mobile-device-in-php-using-detectmobiledevice
I don't know if you can do any feature detection that'll only identify WebOS. It's WebKit-based, so all other WebKit-based platforms will have the same features. Looking at Zepto.js' source, they're doing exactly the same as you are:
ua.match(/(webOS|hpwOS)[\s\/]([\d.]+)/)
(The 2nd capture is the version)
From detect.js
Is there a way to detect if the current user is using an iPad using jQuery/JavaScript?
iPad Detection
You should be able to detect an iPad user by taking a look at the userAgent property:
var is_iPad = navigator.userAgent.match(/iPad/i) != null;
iPhone/iPod Detection
Similarly, the platform property to check for devices like iPhones or iPods:
function is_iPhone_or_iPod(){
return navigator.platform.match(/i(Phone|Pod))/i)
}
Notes
While it works, you should generally avoid performing browser-specific detection as it can often be unreliable (and can be spoofed). It's preferred to use actual feature-detection in most cases, which can be done through a library like Modernizr.
As pointed out in Brennen's answer, issues can arise when performing this detection within the Facebook app. Please see his answer for handling this scenario.
Related Resources
jQuery HowTo: Detect iPad users using JavaScript
jQuery HowTo: Detecting & Redirecting iPad / iPhone Users
David Walsh's Blog : Detecting iPad use
Detecting iPad use via PHP
Although the accepted solution is correct for iPhones, it will incorrectly declare both isiPhone and isiPad to be true for users visiting your site on their iPad from the Facebook app.
The conventional wisdom is that iOS devices have a user agent for Safari and a user agent for the UIWebView. This assumption is incorrect as iOS apps can and do customize their user agent. The main offender here is Facebook.
Compare these user agent strings from iOS devices:
# iOS Safari
iPad: Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9B176 Safari/7534.48.3
iPhone: Mozilla/5.0 (iPhone; CPU iPhone OS 5_0 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3
# UIWebView
iPad: Mozilla/5.0 (iPad; CPU OS 5_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Mobile/98176
iPhone: Mozilla/5.0 (iPhone; U; CPU iPhone OS 4_1 like Mac OS X; en-us) AppleWebKit/532.9 (KHTML, like Gecko) Mobile/8B117
# Facebook UIWebView
iPad: Mozilla/5.0 (iPad; U; CPU iPhone OS 5_1_1 like Mac OS X; en_US) AppleWebKit (KHTML, like Gecko) Mobile [FBAN/FBForIPhone;FBAV/4.1.1;FBBV/4110.0;FBDV/iPad2,1;FBMD/iPad;FBSN/iPhone OS;FBSV/5.1.1;FBSS/1; FBCR/;FBID/tablet;FBLC/en_US;FBSF/1.0]
iPhone: Mozilla/5.0 (iPhone; U; CPU iPhone OS 5_1_1 like Mac OS X; ru_RU) AppleWebKit (KHTML, like Gecko) Mobile [FBAN/FBForIPhone;FBAV/4.1;FBBV/4100.0;FBDV/iPhone3,1;FBMD/iPhone;FBSN/iPhone OS;FBSV/5.1.1;FBSS/2; tablet;FBLC/en_US]
Note that on the iPad, the Facebook UIWebView's user agent string includes 'iPhone'.
The old way to identify iPhone / iPad in JavaScript:
IS_IPAD = navigator.userAgent.match(/iPad/i) != null;
IS_IPHONE = navigator.userAgent.match(/iPhone/i) != null) || (navigator.userAgent.match(/iPod/i) != null);
If you were to go with this approach for detecting iPhone and iPad, you would end up with IS_IPHONE and IS_IPAD both being true if a user comes from Facebook on an iPad. That could create some odd behavior!
The correct way to identify iPhone / iPad in JavaScript:
IS_IPAD = navigator.userAgent.match(/iPad/i) != null;
IS_IPHONE = (navigator.userAgent.match(/iPhone/i) != null) || (navigator.userAgent.match(/iPod/i) != null);
if (IS_IPAD) {
IS_IPHONE = false;
}
We declare IS_IPHONE to be false on iPads to cover for the bizarre Facebook UIWebView iPad user agent. This is one example of how user agent sniffing is unreliable. The more iOS apps that customize their user agent, the more issues user agent sniffing will have. If you can avoid user agent sniffing (hint: CSS Media Queries), DO IT.
I use this:
function fnIsAppleMobile()
{
if (navigator && navigator.userAgent && navigator.userAgent != null)
{
var strUserAgent = navigator.userAgent.toLowerCase();
var arrMatches = strUserAgent.match(/(iphone|ipod|ipad)/);
if (arrMatches != null)
return true;
} // End if (navigator && navigator.userAgent)
return false;
} // End Function fnIsAppleMobile
var bIsAppleMobile = fnIsAppleMobile(); // TODO: Write complaint to CrApple asking them why they don't update SquirrelFish with bugfixes, then remove
I use this:
//http://detectmobilebrowsers.com/ + tablets
(function(a) {
if(/android|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(ad|hone|od)|iris|kindle|lge |maemo|meego.+mobile|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino|playbook|silk/i.test(a)
||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(di|rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))
{
window.location="yourNewIndex.html"
}
})(navigator.userAgent||navigator.vendor||window.opera);