Is it possible to detect change in orientation of the browser on the iPad or Galaxy Tab using javascript? I think it's possible using css media queries.
NOTE: orientationChange is deprecated
Instead use screen.orientation using the screenOrientation interface
var orientation = (screen.orientation || {}).type || screen.mozOrientation || screen.msOrientation;
if (orientation === "landscape-primary") {
console.log("That looks good.");
} else if (orientation === "landscape-secondary") {
console.log("Mmmh... the screen is upside down!");
} else if (orientation === "portrait-secondary" || orientation === "portrait-primary") {
console.log("Mmmh... you should rotate your device to landscape");
} else if (orientation === undefined) {
console.log("The orientation API isn't supported in this browser :(");
}
However note the support as of July 2022
The screen.orientation is not supported by Safari at all
Older answers
The older orientationChange should still work for Safari
window.addEventListener("orientationchange", function() {
alert(window.orientation);
}, false);
MDN:
window.addEventListener("orientationchange", function() {
alert("the orientation of the device is now " + screen.orientation.angle);
});
or jQuery mobile orientationchange
$(window).on("orientationchange", function( event ) {
$("#orientation").text( "This device is in " + event.orientation + " mode!");
});
Older answer
http://www.nczonline.net/blog/2010/04/06/ipad-web-development-tips/
Safari on the iPad does support the window.orientation property, so if necessary, you can use that to determine if the user is in horizontal or vertical mode. As reminder of this functionality:
window.orientation is 0 when being held vertically
window.orientation is 90 when rotated 90 degrees to the left (horizontal)
window.orientation is -90 when rotated 90 degrees to the right (horizontal)
There is also the orientationchange event that fires on the window object when the device is rotated.
You can also use CSS media queries to determine if the iPad is being held in vertical or horizontal orientation, such as:
<link rel="stylesheet" media="all and (orientation:portrait)" href="portrait.css">
<link rel="stylesheet" media="all and (orientation:landscape)" href="landscape.css">
http://www.htmlgoodies.com/beyond/webmaster/toolbox/article.php/3889591/Detect-and-Set-the-iPhone--iPads-Viewport-Orientation-Using-JavaScript-CSS-and-Meta-Tags.htm
<script type="text/javascript">
var updateLayout = function() {
if (window.innerWidth != currentWidth) {
currentWidth = window.innerWidth;
var orient = (currentWidth == 320) ? "profile" : "landscape";
document.body.setAttribute("orient", orient);
window.scrollTo(0, 1);
}
};
iPhone.DomLoad(updateLayout);
setInterval(updateLayout, 400);
</script>
You can use mediaMatch to evaluate CSS media queries, e.g.
window
.matchMedia('(orientation: portrait)')
.addListener(function (m) {
if (m.matches) {
// portrait
} else {
// landscape
}
});
CSS media query fires before the orientationchange. If you are looking to capture the end of the event (when the rotation has been completed), see mobile viewport height after orientation change.
In 2022, instead of adding a window orientationchange listener (listener not recommended due to deprecation) you should listen for a screen.orientation change event:
if (screen.orientation) { // Property doesn't exist on screen in IE11
screen.orientation.addEventListener("change", callback);
}
All browsers except IE and Safari now support it. (here is a screenshot of screen from IE11:
... notice that orientation is not a supported attribute of screen in IE11)
The Screen Orientation API is thoroughly documented. The main focus is the ScreenOrientation interface, which extends Screen. Here are 2 screenshots of the orientation attribute of Screen, which shows how the angle changes from 0 (portrait) to 90 (landscape) on an Android device:
You can use the orientationchange event like so:
window.addEventListener('orientationchange', function(event) {
/* update layout per new orientation */
});
I realized that nobody mentioned what happens when the device is held upside-down in this thread.
window.orientation returns -90 or 90 when held horizontal. It returns 0 or 180 when held vertical. Some devices do and some don't support being held upside-down. I recommend,
window.addEventListener("orientationchange", function() {
if ( window.orientation == 0 || window.orientation == 180) {
// WHEN IN PORTRAIT MODE
} else {
// WHEN IN LANDSCAPE MODE
}
}, false);
Also note that window.orientation returns undefined on desktops.
From "Cross-device, cross-browser portrait-landscape detection"
This is about finding out whether a mobile device is in portrait or landscape mode; you don't need to care about its orientation. For all you know, if you hold your iPad upside down, it's in portrait mode.
$(window).bind("resize", function(){
screenOrientation = ($(window).width() > $(window).height())? 90 : 0;
});
90 means landscape, 0 means portrait, cross browser, cross device.
The window.onresize event is available everywhere, and it's always fired at the right time; never too early, never too late. As a matter of fact, the size of the screen is always accurate as well.
The JavaScript version would be this, correct me please if I am wrong.
function getScreenOrientation() {
screenOrientation = window.outerWidth > window.outerHeight ? 90 : 0;
console.log("screenOrientation = " + screenOrientation);
}
window.addEventListener("resize", function(event) {
getScreenOrientation();
});
getScreenOrientation();
window.orientation is what you're looking for. there's also an onOrientationChange event
works for android, iphone and, i'm mostly sure, for ipad
Adding to the #mplungjan answer, I found better results using the webkit "native" (I don't really how to called it) event, 'deviceorientation'.
In the Mozilla Developer network they have a good explanation about how to normalize between webkit and Gecko that helped me to solve this problem.
An easy to use snippet :
function doOnOrientationChange()
{
switch(window.orientation)
{
case -90:
case 90:
// alert('landscape');
$('#portrait').css({display:'none'});
$('#landscape').css({display:'block'});
break;
default:
// alert('portrait');
$('#portrait').css({display:'block'});
$('#landscape').css({display:'none'});
break;
}
}
window.addEventListener('orientationchange', doOnOrientationChange);
// First launch
doOnOrientationChange();
orientationChange is deprecated and also not supported in some browsers,
innerHeight and outerHeight sometimes give inconsistent results in ios
so we can use document.documentElement to check orientation along with resize event
const { clientWidth, clientHeight } = document.documentElement;
if (clientHeight > clientWidth) {
setOrientation("portrait-secondary");
} else {
setOrientation("landscape-primary");
}
As of 2022
Once you get ready like this,
let theDeviceIsRotated;
function handlePortraitOrLandscape() {
setTimeout(afterAnUnnoticableDelay,100); // This solves the wrong-firing-order issue on Samsung Browser.
function afterAnUnnoticableDelay() {
if (screen.orientation) { // Mainly for Android (as of 2022)
// Returns 0 or 90 or 270 or 180
if (screen.orientation.angle == 0) { theDeviceIsRotated="no"; }
if (screen.orientation.angle == 90) { theDeviceIsRotated="toTheLeft"; }
if (screen.orientation.angle == 270) { theDeviceIsRotated="toTheRight"; }
if (screen.orientation.angle == 180) { theDeviceIsRotated="upsideDown"; }
} else { // Mainly for iOS (as of 2022)
// Returns 0 or 90 or -90 or 180
if (window.orientation == 0) { theDeviceIsRotated="no"; }
if (window.orientation == 90) { theDeviceIsRotated="toTheLeft"; }
if (window.orientation == -90) { theDeviceIsRotated="toTheRight"; }
if (window.orientation == 180) { theDeviceIsRotated="upsideDown"; }
}
}
}
handlePortraitOrLandscape(); // Set for the first time
window.addEventListener("resize",handlePortraitOrLandscape); // Update when change happens
you can
if (theDeviceIsRotated == "no") {
// Do your thing
} else if (theDeviceIsRotated == "toTheLeft") {
// Do your thing
} else if (theDeviceIsRotated == "toTheRight") {
// Do your thing
} else if (theDeviceIsRotated == "upsideDown") {
// Do your thing
} else {
// The mysterious 5th orientation nobody has ever seen yet
}
but note that
RESIZE does not fire when switching from 90 to 270 directly (without triggering a portrait view in between)
THEREFORE WE CANNOT RELY ON
window.addEventListener("resize",screenOrientationHasChanged);
AND THERE IS THE EXACT SAME PROBLEM WITH
window.screen.orientation.addEventListener('change',screenOrientationHasChanged);
ALSO WITH
window.addEventListener("orientationchange",screenOrientationHasChanged);
THIS SADLY MEANS THAT AS OF 2022 THERE IS NO RELIABLE WAY TO DETECT SCREEN ORIENTATION CHANGE even by using setInterval
BECAUSE neither screen.orientation.angle nor screen.orientation.type is updated when you go from 90 to 270 without triggering a portrait view in between.
So the following is not any better than resize on mobile devices
if (screen.orientation) {
window.screen.orientation.addEventListener('change',screenOrientationHasChanged); // https://whatwebcando.today/screen-orientation.html
} else {
window.addEventListener("orientationchange",screenOrientationHasChanged); // https://developer.mozilla.org/en-US/docs/Web/API/Window/orientationchange_event
}
You may try to lock the screen orientation to avoid errors but that does not work on iOS as of 2022 and it only works with fullscreen mode on Android.
So I have an application with navigation controller, the last one viewcontoller can be rotated to any orientation. And the others must be in portrait only. So I have troubles when we rotate the last viewController in landscape and then pressing back button on navigation controller. We go to previous one and see that layout are broken.
I thought the only way is to use something like this:
[[UIDevice currentDevice] performSelector:NSSelectorFromString(#"setOrientation:") withObject:(id)UIInterfaceOrientationPortrait]
(I know it's a hack and it would be preferred to avoid such things)
But wherever I tried it - it caused me with different layout problems. The usual methods like:
- (NSUInteger)supportedInterfaceOrientations
- (BOOL)shouldAutorotate
and so on for handle orientation are useless in this situation.
Thanks in advance.
I solved this problem for parent of those viewControllers who must be in portrait:
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskPortrait;
}
- (UIInterfaceOrientation)preferredInterfaceOrientationForPresentation{
return UIInterfaceOrientationPortrait;
}
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[[UIApplication sharedApplication] setStatusBarOrientation:UIInterfaceOrientationPortrait animated:YES];
}
For rotation viewController:
-(NSUInteger)supportedInterfaceOrientations{
return UIInterfaceOrientationMaskAll;
}
For Navigation Controller:
- (BOOL)shouldAutorotate {
BOOL result = self.topViewController.shouldAutorotate;
return result;
}
- (NSUInteger)supportedInterfaceOrientations {
NSUInteger result = self.topViewController.supportedInterfaceOrientations;
return result;
}
And added this to AppDelegate:
- (NSUInteger) application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(UIWindow *)window {
UINavigationController *navigationController = (UINavigationController *)self.window.rootViewController;
if ([navigationController.topViewController isKindOfClass:[RotationViewController class]])
return UIInterfaceOrientationMaskAll;
else
return UIInterfaceOrientationMaskPortrait;
}
I have a Monotouch 6.0.10 iPhone app using the 6.1 SDK, but targeted to iOS 4.0 and up, where I am trying unsuccessfully to force just one of the views to portrait orientation, using ShouldAutorotateToInterfaceOrientation. It's now deprecated I realize, but nevertheless necessary to support iOS4/iOS5 devices.
To try to isolate the problem I wrote a minimal test app. It is XIB-less and has a UITabBarController with one tab. The tab has a UINavigationController and the UINavigationController has a UIViewController (with a hello world button to click).
In AppDelegate I have:
tabController = new TabController();
window.RootViewController = tabController;
In the UITabBarController and in the UINavigationController I have:
public override bool ShouldAutorotateToInterfaceOrientation(UIInterfaceOrientation toInterfaceOrientation)
{
return true;
}
In the UIViewController I have:
public override bool ShouldAutorotateToInterfaceOrientation(UIInterfaceOrientation toInterfaceOrientation)
{
if ( toInterfaceOrientation == UIInterfaceOrientation.Portrait )
{
return true;
}
else
{
return false;
}
}
Well, on an iOS 6.1 device at least, those ShouldAutorotateToInterfaceOrientation's seem to be completely ignored. Breakpoints there don't get reached, and if I force them to return false in every case, rotations still happen.
My understanding is that ShouldAutomaticallyForwardRotationMethods defaults to true so that would not seem to offer a solution. Have combed the forums with no luck, except a suggestion from Glen Schmidt here: iOS 6 rotations: supportedInterfaceOrientations doesn´t work? but unfortunately I'm lost on how to translate that to MonoTouch:
QUOTE
If you want to replicate the pre-iOS 6 behaviour where all the views in the navigation stack / tab bar have to agree on an allowable set of orientations, put this in your subclass of UITabBarController or UINavigationController:
- (NSUInteger)supportedInterfaceOrientations
{
NSUInteger orientations = [super supportedInterfaceOrientations];
for (UIViewController *controller in self.viewControllers)
orientations = orientations & [controller supportedInterfaceOrientations];
return orientations;
}
UNQUOTE
I understand also that I can't even hope to solve it just for my iOS6 users via ShouldAutoRotate/SupportedInterfaceOrientations because this would cause iOS4/IOS5 rotations to fail.
Any suggestion much appreciated!
Bill.
Found a solution that works, after quite a bit of thrashing about. Somebody might be able to improve on this.
PROBLEM
My app, based on SDK 6.1, targets iOS4, iOS5 and iOS6 devices. It needs to allow all screens to rotate, except for one which must be fixed in portrait orientation. The app has a UITabBarController, most tabs have a UINavigationController with stacked views beneath.
SOLUTION
For iOS4/iOS5 users, the key is the deprecated ShouldAutorotateToInterfaceOrientation() method in the app's root view controller (tab bar controller in my case). This method is not called automatically in any other view controller, but of course the root view controller can invoke a method of the same name in the current view controller.
For iOS6 users, the key is ShouldAutorotate() / GetSupportedInterfaceOrientations() in the app's root view controller. Again, these methods are not called automatically in any other view controller but the root view controller can invoke methods of the same name in the current view controller.
For my simple test app described in the original post:
In AppDelegate/FinishedLaunching:
window.RootViewController = tabController;
In AppDelegate:
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations (UIApplication application, UIWindow forWindow)
{
return UIInterfaceOrientationMask.All;
}
In TabController:
public override bool ShouldAutorotateToInterfaceOrientation(UIInterfaceOrientation toInterfaceOrientation) // iOS4/iOS5 only
{
try
{
UINavigationController navController = (UINavigationController)SelectedViewController;
UIViewController targetController = navController.ViewControllers[0];
if ( targetController.Title == "Greetings")
{
if ( toInterfaceOrientation == UIInterfaceOrientation.Portrait )
{
return true;
}
else
{
return false;
}
}
}
catch
{
return true;
}
return true;
}
public override bool ShouldAutorotate() // iOS6+ only
{
return true;
}
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations() // iOS6+ only
{
try
{
UINavigationController navController = (UINavigationController)SelectedViewController;
UIViewController targetController = navController.ViewControllers[0];
return targetController.GetSupportedInterfaceOrientations();
}
catch
{
return UIInterfaceOrientationMask.All;
}
return UIInterfaceOrientationMask.All;
}
In the view controller that needs to be portrait:
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations() // iOS6+ only
{
return UIInterfaceOrientationMask.Portrait;
}
My actual app needs something slightly more elaborate but along the same lines. For example in the tabbarcontroller:
public override UIInterfaceOrientationMask GetSupportedInterfaceOrientations()
{
try
{
UINavigationController navController = (UINavigationController)SelectedViewController;
if ( navController.Title == "Tracking" ) // Are we on the Tracking tab?
{
// Yes. Looking for RedLaser's BarcodePickerController
UIViewController targetController = navController.ViewControllers[1]; // BarcodePicker would be second on nav stack
string controllerType = targetController.GetType().Name;
if ( controllerType == "BarcodePickerController" ) // Is this BarcodePicker?
{
return UIInterfaceOrientationMask.Portrait; // Yes, force portrait orientation
}
}
else
{
return UIInterfaceOrientationMask.All; // No, allow any orientation
}
}
catch
{
return UIInterfaceOrientationMask.All; // Not BarcodePicker, allow any orientation
}
return UIInterfaceOrientationMask.All;
}
I am using a UIPageViewController with transitionStyle UIPageViewControllerTransitionStyleScroll and navigationOrientation UIPageViewControllerNavigationOrientationVertical
I also have a UIPanGestureRecognizer on the view and I want to disable page scrolling when the pan gesture is active.
I am trying to set the following when the gesture begins:
pageViewController.view.userInteractionEnabled = NO;
This seems to have no effect, or it appears to work sporadically.
The only other way I have found to do it (which works) is to set the UIPageViewController dataSource to nil while the pan gesture is running, however this causes a huge delay when resetting the dataSource.
UIPageViewController uses some UIScrollView object to handle scrolling (at least for transitionStyle UIPageViewControllerTransitionStyleScroll). You can iterate by controller's subviews pageViewController.view.subviews to get it. Now, you can easly enable/disable scrolling:
- (void)setScrollEnabled:(BOOL)enabled forPageViewController:(UIPageViewController*)pageViewController
{
for (UIView *view in pageViewController.view.subviews) {
if ([view isKindOfClass:UIScrollView.class]) {
UIScrollView *scrollView = (UIScrollView *)view;
[scrollView setScrollEnabled:enabled];
return;
}
}
}
For those who are using swift instead of objective-c, here is Squikend's solution transposed.
func findScrollView(#enabled : Bool) {
for view in self.view.subviews {
if view is UIScrollView {
let scrollView = view as UIScrollView
scrollView.scrollEnabled = enabled;
} else {
println("UIScrollView does not exist on this View")
}
}
}
Everyone is complicating this very very much.
This is the whole function you need to disable or enable the scroll.
func enableScroll(_ enable: Bool) {
dataSource = enable ? self : nil
}
Swift 4.2 Version of the answer
func findScrollView(enabled: Bool) {
for view in self.view.subviews {
if view is UIScrollView {
let scrollView = view as! UIScrollView
scrollView.isScrollEnabled = enabled
} else {
print("UIScrollView does not exist on this View")
}
}
}
then yourpagecontorller.findScrollView(enabled: false)
You may also disable gesture recognizers.
for (UIGestureRecognizer *recognizer in pageViewController.gestureRecognizers)
{
recognizer.enabled = NO;
}
Unlike the popular answer, which relies on existing of the inner UIScrollView, this one uses public gestureRecognizers array. The underlying scroll view may not exist if you use book-style paging.
I am attempting to work on a mobile site using the maps found at jVectorMap
http://jvectormap.owl-hollow.net/
I found that when viewing the page on an iphone in landscape mode, I needed to change the scale command to .4. However, when in portrait mode, it needs to be smaller, such as .2.
I am using this code to adjust the scale value found in the js library downloaded from jVectorMap. The commented out code is the original code that i modified in an attempt to fit an iphone screen
applyTransformParams: function(scale, transX, transY) {
if (this.mode == 'svg') {
this.rootGroup.setAttribute('transform', 'scale(.4) translate(30, '+transY+')');
//this.rootGroup.setAttribute('transform', 'scale('+scale+') translate('+transX+', '+transY+')');
} else {
this.rootGroup.coordorigin = (this.width-transX)+','+(this.height-transY);
this.rootGroup.coordsize = this.width/scale+','+this.height/scale;
}
}
My question is, is there a way I can determine the screen orientation via the js and have it update the scale numbers? Or perhaps there is a command for a best fit for mobile?
Thanks for any help
You can check if a browser supports the onorientationchange event (or fall back to onresize) like this:
var evt;
if ((typeof window.orientation !== 'undefined') && ('onorientationchange' in window)) {
evt = 'orientationchange';
} else {
evt = 'resize';
}
You can always get the orientation like this:
var getOrientation = function () {
if (evt === 'orientationchange') {
return = (window.orientation === 0) ? 'portrait' : 'landscape';
} else {
return = (window.innerHeight > window.innerWidth) ? 'portrait' : 'landscape';
}
};
Then you can subscribe to that event & do your scaling there.
var originalOrientation = getOrientation;
window.addEventListener(evt, function () {
var orientation = getOrientation();
if (orientation !== originalOrientation) {
originalOrientation = orientation;
// do your scaling here...
}
});