react calculating window sizes wrong on resize [duplicate] - reactjs

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.

Related

Can addEventListener fire event for mobile display?

Not good with js at all. I am using jquery TimeCircles (http://plugins.jquery.com/timecircles/). And I am trying to 'rebuild' (http://git.wimbarelds.nl/TimeCircles/readme.php) the circles when the view / window goes from portrait to landscape.
The div that has the circles is:
<div id="DateCountdown" data-date="2015-10-08 00:00:00" ></div>
My goal is like i mentioned above, would like to have the circles rebuild themselves when view changes from landscape to portrait or vice versa.
I have this right before the closing body tag:
<script>
// Find matches
var mql = window.matchMedia("(orientation: portrait)");
// If there are matches, we're in portrait
if(mql.matches) {
// Portrait orientation
} else {
// Landscape orientation
}
// Add a media query change listener
mql.addListener(function(m) {
if(m.matches) {
$(".datecountdown").TimeCircles().rebuild();
}
else {
}
});
</script>

angularjs directive tutorials

I am new to angularjs and I would like to understand what the directives do but I can't find a tutorial with different example by complexity and I was curios if I could move the following code in a directive.
// hide the url bar
var page = document.getElementById('page'),
ua = navigator.userAgent,
iphone = ~ua.indexOf('iPhone') || ~ua.indexOf('iPod'),
ipad = ~ua.indexOf('iPad'),
ios = iphone || ipad,
// Detect if this is running as a fullscreen app from the homescreen
fullscreen = window.navigator.standalone,
android = ~ua.indexOf('Android'),
lastWidth = 0;
if (android) {
// Android's browser adds the scroll position to the innerHeight.
// Thus, once we are scrolled, the page height value needs to be corrected in case the page is loaded
// when already scrolled down. The pageYOffset is of no use, since it always
// returns 0 while the address bar is displayed.
window.onscroll = function () {
page.style.height = window.innerHeight + 'px'
}
}
var setupScroll = window.onload = function () {
// Start out by adding the height of the location bar to the width, so that
// we can scroll past it
if (ios) {
// iOS reliably returns the innerWindow size for documentElement.clientHeight
// but window.innerHeight is sometimes the wrong value after rotating
// the orientation
var height = document.documentElement.clientHeight;
// Only add extra padding to the height on iphone / ipod, since the ipad
// browser doesn't scroll off the location bar.
if (iphone && !fullscreen) height += 60;
page.style.height = height + 'px';
} else if (android) {
// The stock Android browser has a location bar height of 56 pixels, but
// this very likely could be broken in other Android browsers.
page.style.height = (window.innerHeight + 56) + 'px'
}
// Scroll after a timeout, since iOS will scroll to the top of the page
// after it fires the onload event
setTimeout(scrollTo, 0, 0, 1);
};
(window.onresize = function () {
var pageWidth = page.offsetWidth;
// Android doesn't support orientation change, so check for when the width
// changes to figure out when the orientation changes
if (lastWidth == pageWidth) return;
lastWidth = pageWidth;
setupScroll();
})();
I wrote a blog entry not too long ago about the basics of directives if you're interested in that.
As far as converting what you have there into a directive, it's not too crazy.
All you would do is use the code you already have, but inject $window instead of using window. (Mostly for testing purposes). I also added a check to make sure it didn't get applied twice.
So it would look a little something like this:
app.directive('windowResizeThingy', function($window) {
return {
restrict: 'A',
link: function(scope, elem, attr) {
// make sure this doesn't get applied twice.
if($window.windowResizeThingyApplied) return;
$window.windowResizeThingyApplied = true;
// hide the url bar
var page = elem[0],
ua = $window.navigator.userAgent,
iphone = ~ua.indexOf('iPhone') || ~ua.indexOf('iPod'),
ipad = ~ua.indexOf('iPad'),
ios = iphone || ipad,
// Detect if this is running as a fullscreen app from the homescreen
fullscreen = $window.navigator.standalone,
android = ~ua.indexOf('Android'),
lastWidth = 0;
if (android) {
// Android's browser adds the scroll position to the innerHeight.
// Thus, once we are scrolled, the page height value needs to be corrected in case the page is loaded
// when already scrolled down. The pageYOffset is of no use, since it always
// returns 0 while the address bar is displayed.
window.onscroll = function () {
page.style.height = window.innerHeight + 'px'
}
}
var setupScroll = $window.onload = function () {
// Start out by adding the height of the location bar to the width, so that
// we can scroll past it
if (ios) {
// iOS reliably returns the innerWindow size for documentElement.clientHeight
// but window.innerHeight is sometimes the wrong value after rotating
// the orientation
var height = document.documentElement.clientHeight;
// Only add extra padding to the height on iphone / ipod, since the ipad
// browser doesn't scroll off the location bar.
if (iphone && !fullscreen) height += 60;
page.style.height = height + 'px';
} else if (android) {
// The stock Android browser has a location bar height of 56 pixels, but
// this very likely could be broken in other Android browsers.
page.style.height = (window.innerHeight + 56) + 'px'
}
// Scroll after a timeout, since iOS will scroll to the top of the page
// after it fires the onload event
setTimeout(scrollTo, 0, 0, 1);
};
($window.onresize = function () {
var pageWidth = page.offsetWidth;
// Android doesn't support orientation change, so check for when the width
// changes to figure out when the orientation changes
if (lastWidth == pageWidth) return;
lastWidth = pageWidth;
setupScroll();
})();
}
};
});
And to apply it, you'd find your #page element you were applying it to before:
<div id="page" window-resize-thingy></div>
... and that should be it really. Presuming the code you have works, it should be run pretty much the same way.

Sencha Touch 2.0 - How to set scrolling inside a textarea for Mobile Safari?

In my mobile safari project, i need to create a message posting feature. it is requires scrolling inside a textarea when lines of texts exceed the max rows of the text area. i couldn't find 'scrollable' property in Ext.field.textarea, any idea how?
Cheers!
There is a bug in touch 2.0.x such that the framework explicitly prevents the scroll action. Supposedly a fix will be in 2.1, though I didn't see that officially, just from a guy on a forum.
Until then, there is kind of a solution for touch1 here http://www.sencha.com/forum/showthread.php?180207-TextArea-scroll-on-iOS-not-working that you can port to V2. It basically involves adding an eventlistener to the actual textarea field (not the sencha object) and then calling preventdefault if it's a valid scrollevent.
The full code is at that link, but the salient bits are here.
Grab the <textarea> field (not the Sencha Touch object) directly and use addListener to apply
'handleTouch' on touchstart and 'handleMove' on touchmove
handleTouch: function(e) {
this.lastY = e.pageY;
},
handleMove: function(e) {
var textArea = e.target;
var top = textArea.scrollTop <= 0;
var bottom = textArea.scrollTop + textArea.clientHeight >= textArea.scrollHeight;
var up = e.pageY > this.lastY;
var down = e.pageY < this.lastY;
this.lastY = e.pageY;
// default (mobile safari) action when dragging past the top or bottom of a scrollable
// textarea is to scroll the containing div, so prevent that.
if((top && up) || (bottom && down)) {
e.preventDefault();
e.stopPropagation(); // this tops scroll going to parent
}
// Sencha disables textarea scrolling on iOS by default,
// so stop propagating the event to delegate to iOS.
if(!(top && bottom)) {
e.stopPropagation(); // this tops scroll going to parent
}
}
Ext.define('Aspen.util.TextArea', {
override: 'Ext.form.TextArea',
adjustHeight: Ext.Function.createBuffered(function (textarea) {
var textAreaEl = textarea.getComponent().input;
if (textAreaEl) {
textAreaEl.dom.style.height = 'auto';
textAreaEl.dom.style.height = textAreaEl.dom.scrollHeight + "px";
}
}, 200, this),
constructor: function () {
this.callParent(arguments);
this.on({
scope: this,
keyup: function (textarea) {
textarea.adjustHeight(textarea);
},
change: function (textarea, newValue) {
textarea.adjustHeight(textarea);
}
});
}
});

Twitter Bootstrap Popover/Tooltip Bug with Mobile?

I am working with Twitter Bootstrap and ran into something I could not fix when testing on iPad and iPhone. On mobile (at least those devices) you need to click to engage the tip or popover (as expected). The issue is that you can never close it once you do. I added a listener to close it if you click it again, but I find it hard to believe that the default behavior would not be to click to remove it. Is this a bug in Bootstrap popover and tooltip?? My code is below - it seems to work, but ONLY if you click the same item that created the tip or popover - not anywhere on the page (could not get that to work).
Code to fire:
$(function () {
//Remove the title bar (adjust the template)
$(".Example").popover({
offset: 10,
animate: false,
html: true,
placement: 'top',
template: '<div class="popover"><div class="arrow"></div><div class="popover-inner"><div class="popover-content"><p></p></div></div></div>'
//<h3 class="popover-title"></h3>
//Need to have this click check since the tooltip will not close on mobile
}).click(function(e) {
jQuery(document).one("click", function() {
$('.Example').popover('hide')
});
});
});
HTML:
<a href="javascript:void(0);" class="Example" rel="popover" data-content="This is the Data Content" data-original-title="This is the title (hidden in this example)">
Thanks in advance!
Dennis
I tried dozens of solutions posted to stackoverflow and other various corners of the web, and the following is the only one that worked for me!
Explanation
As noted here, you can a CSS-directive the element in order to make it touch-device-clickable. I can't tell you why that works or what's going on there, but that seems to be the case. So, I want to make the entire document aka body clickable on mobile devices, which will allow me to touch anywhere to dismiss the popover.
Popover JS
$(function () {
$('[data-toggle="popover"]').popover({ trigger: "hover"}})
});
Directions
1. Install Modernizr
I'm using rails, so I used the gem.
gem 'modernizr-rails'
2. Create a touch class with a css-directive
Add the following to your CSS:
.touch {
cursor: pointer
}
3. On touch devices only, add the touch class to the body
If you want other elements to be clickable, instead of the entire body, add the touch class to them.
if (Modernizr.touch) {
$( "body" ).addClass( "touch" );
}
That's it! Now, you can use your popover normally on desktop (even with hover-trigger) and it will be touch-dismissible on mobile.
I had the same problem with my IPad. But in browser it works fine. Solution for me was adding listeners for all possible element that i can hide tooltip:
$('*').bind('touchend', function(e){
if ($(e.target).attr('rel') !== 'tooltip' && ($('div.tooltip.in').length > 0)){
$('[rel=tooltip]').mouseleave();
e.stopPropagation();
} else {
$(e.target).mouseenter();
}
});
Yes, it's small overhead to send event for all tooltips, but you can't define which element tooltip is showing.
Main concept is that make popover manually on mobile device
$(document).ready(function() {
if ('ontouchstart' in window) {
$('[data-toggle="popover"]').popover({
'trigger': 'manual'
});
}
});
Refer following code snippet to get it works:
$('[data-toggle="popover"]').popover();
$('body').on('click', function (e) {
$('[data-toggle="popover"]').each(function () {
//the 'is' for buttons that trigger popups
//the 'has' for icons within a button that triggers a popup
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
$(this).popover('hide');
}
});
});
This is the easiest way of detecting clicks on the body and close all the tooltips on the page.
You can check the live example here
Solution on this jsfiddle,
test on iOS (iPad and iPhone), Android and Windows.
$(document).ready(function(){
var toolOptions;
var toolOptions2;
var isOS = /iPad|iPhone|iPod/.test(navigator.platform);
var isAndroid = /(android)/i.test(navigator.userAgent);
///////////////////////////////////////// if OS
if (isOS){
toolOptions = {
animation: false,
placement:"bottom",
container:"body"
};
$('.customtooltip').tooltip(toolOptions);
$('.customtooltip').css( 'cursor', 'pointer' );
$('body').on("touchstart", function(e){
$(".customtooltip").each(function () {
// hide any open tooltips when the anywhere else in the body is clicked
if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.tooltip').has(e.target).length === 0) {
$(this).tooltip('hide');
}////end if
});
});
///////////////////////////////////////// if Android
} else if(isAndroid){
toolOptions = {
animation: false,
placement:"bottom",
container:"body"
};
toolOptions2 = {
animation: false,
placement:"left",
container:"body"
};
$('.c_tool1').tooltip(toolOptions);
$('.c_tool2').tooltip(toolOptions);
$('.c_tool3').tooltip(toolOptions2);
///////////////////////////////////////// if another system
} else {
toolOptions = {
animation: true,
placement:"bottom",
container:"body"
};
$('.customtooltip').tooltip(toolOptions);
}//end if system
document.getElementById("demo").innerHTML = "Sys: "+navigator.platform+" - isOS: "+isOS+" - isAndroid: "+isAndroid;
});
<h6>
first tooltip
Second tooltip
third tooltip
</h6>
<p id="demo"></p>
Bootstap-tooltip v3.3.7
Actual: tooltip on hover doesn't work with touch devices in our project
Solution: Subscribe to tooltip's show event and call mouseenter
$body = $('body');
$body.tooltip({selector: '.js-tooltip'});
// fix for touch device.
if (Modernizr.touch) { // to detect you can use https://modernizr.com
var hideTooltip = function(e) {
tooltipClicked = !!$(e.target).closest('.tooltip').length;
if (tooltipClicked) { return; }
$('.js-tooltip').tooltip('hide');
}
var emulateClickOnTooltip = function(e) {
tooltipsVisible = !!$('.tooltip.in').length;
if (tooltipsVisible) { return; }
$(e.target).mouseenter();
}
var onTooltipShow = function(e) {
tooltipClicked = !!$(e.target).closest('.tooltip').length;
if (tooltipClicked) { return; }
$body.on('touchend', hideTooltip);
}
var onTooltipHide = function() {
$body.off('touchend', hideTooltip);
}
$body
.on('touchend', '.js-tooltip', emulateClickOnTooltip)
.on('show.bs.tooltip', onTooltipShow)
.on('hide.bs.tooltip', onTooltipHide);
}

JQM Orientation SVG Scale

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...
}
});

Resources