This is for a web app, targeting any mobile browser but mainly Chrome and Safari for iOS10.
The browser opens the built-in keyboard when the user clicks on any input, which is fine, but I am trying to resize/relocate the items more relevant to the user at that point in time.
Is there anyway to calculate the height of the keyboard so that I can adjust items accordingly? Generic solution would be better, as inputs may open different types of keyboard (text, numeric...) but hard coding options are also a valid response at this stage.
I have tested around window.screen, window.innerHeight and so on to no avail...
Thanks!
There is a web standards to tackle this issue.
The Visual Viewport API, which also allows to listen to the resize event.
The visual viewport is the visual portion of a screen excluding on-screen keyboards, areas outside of a pinch-zoom area, or any other on-screen artifact that doesn't scale with the dimensions of a page.
visualViewport.addEventListener('resize', () => {
const h = event.target.height
…
});
Note: The credit of this answer fully goes to the user Alex k. However his solution has already been accepted by OP within
comments section and I guess these should have to be in answer section
as this is a very useful question.
To solve the problem, try CSS media queries like this:
var d = document.getElementById('d');
checkDimensions();
window.onresize = checkDimensions;
function checkDimensions(prevHeight) {
d.innerHTML = 'Window dimensions: ' + window.innerWidth + ' x ' + window.innerHeight;
}
.media-query-test {
display: none;
color: red;
}
#media all and (max-height: 700px) {
.media-query-test {
display: block;
}
}
<input type="number" />
<div id="d">
</div>
<div class="media-query-test">
Height is less than 700px!
</div>
And here's the JSFiddle: https://jsfiddle.net/t3yn2yz1/6/
As mentioned by Alex K in a comment:
I tried #media all and (max-height: 700px), which triggered when
opening the soft keyboard. Tested on Chrome browser on a Samsung S6,
Android 6.0.1. You may also have success with window.onresize
Related
I have been having an issue with resizing web pages to fit on mobile devices using JavaScript. Is there a certain width that most programmers use to start changing the resize for mobile devices? Can anyone recommend any guidelines that I should or need to follow when working with mobile devices? I am now being instructed to make sure all web pages are "mobile friendly".
function adjustStyle() {
var width = 0;
// get the width.. more cross-browser issues
if (window.innerHeight) {
width = window.innerWidth;
} else if (document.documentElement && document.documentElement.clientHeight) {
width = document.documentElement.clientWidth;
} else if (document.body) {
width = document.body.clientWidth;
}
// now we should have it
if (width < 650) {
document.getElementById("myCSS").setAttribute("href", "css/narrow.css");
} else {
document.getElementById("myCSS").setAttribute("href", "css/main.css");
}
}
// now call it when the window is resized.
window.onresize = function () {
adjustStyle();
};
For starters, you shouldn't rely on Javascript to make your pages responsive to different resolutions or screens sizes, CSS can handle that for you using media-queries.
Javascript should be used in responsive design only under these circumstances:
You have an extreme design feature that is impossible to pull off correctly in CSS
You want to enhance your web page by adding in some interactions, animations or custom behaviors.
You have an experimental website where users are expecting something out of the ordinary
You are willing to warn your users if javascript is required to display/run your page properly.
It is best practice to only use javascript to enhance your page after you have written your responsive layouts in CSS, that way, your site is still functional if javascript is turned off in the browser.
"Progressive enhancement" is a popular technique for web developers who want to get the page looking nicely while assuming Javascript is turned off, so that users without javascript will still get a nice experience, then, progressive-enhancement in javascript means that the user can be ensured an even better experience if they have javascript turned on, because they might see some nice animations, and cool parallax scrolling, etc.
With that said, your question was directly asking about using javascript for responsive design, so from there, the advice is simple:
Use jQuery's bind() or resize()functions to listen for browser resize events:
$(window).resize(function() {
// handle layout here
// change widths, heights, positions, etc
});
$(window).bind("resize", function(){
// handle layout here
// change widths, heights, positions, etc
});
And from there, you can effect the width's height's and positions of your elements, or assign CSS properties to them, depending on your design.
As for good "breakpoints" (screen sizes to watch for in your responsive layouts), you can refer to this guide: Media Queries for Common Device Breakpoints.
I tend to start somewhere around here, and then tweak as I go:
Mobile: width: 320px - 750px
Tablets: width: > 750px - 1024px
Laptops/Desktops: width: > 1024px
And then I test on multiple devices, and make changes accordingly, your final design will dictate the final numbers you choose as your breakpoints.
Hope this helps.
I typically use a width of 600 to adjust for mobile devices.
Make sure to add this meta tag inside the <head> tag:
<meta name="viewport" content="width=device-width">
This should make the page render at a reasonable size.
Add this <style> tag inside the <head> tag:
<style>
img {
max-width: 100%;
}
</style>
I think this will make sure all images don't render any wider than the app's webview's viewport.
(If that doesn't work, try width: 100%; instead. That'll definitely make all images be as wide as the viewport, and therefore no wider.)
You can also try the #media tags in your css, they allow you to completely reprogram it depending on the resolution:
#media screen and (max-width: 600px) {
.button {
width:300px;
}
}
then for different resolutions above mobile
#media screen (min-width: 600px) and (max-width: 1200px) {
.button {
width:500px;
}
}
But you can always use jquery's
$(window).resize(...) which binds a callback for the resize event or triggers this event.
Bind a callback function, if the users resizes the browser window:
$(window).resize(function() {
alert('resize handler called');
});
If you want to call all listeners manually (without any parameters):
$(window).resize();
I'm, setting up the mobile side of a website at the moment, and I need custom CSS and Javascript for mobile, so in the CSS I have rules using #media screen and (max-width: 500px) { and in Javascript I was going to use if ($(window).width() < 500.
However, if I resize my browser to the exact pixel the mobile CSS starts being used and I console.log($(window).width()); I get 485.
Is this normal behaviour or am I doing something wrong?
Update:
Using this, the values seem to be in sync, only tested in firefox though at the moment.
var scrollBarWidth = false;
function mobileRules() {
if (!scrollBarWidth) {
var widthWithScrollBars = $(window).width();
$('body').css('overflow', 'hidden');
var widthNoScrollBars = $(window).width();
$('body').css('overflow', 'scroll');
scrollBarWidth = widthNoScrollBars - widthWithScrollBars;
console.log('Width: '+widthWithScrollBars+'. Without: '+widthNoScrollBars+'. Scroll: '+scrollBarWidth);
}
console.log($(window).width()+scrollBarWidth+' vs '+globals.mobile_width);
if ($(window).width()+scrollBarWidth < globals.mobile_width) {
console.log('Running mobile rules in jQuery');
}
}
In firefox, media queries consider the width of the scrollbar to be inside the screen width.
This is what gives you the 15px wider screen width.
In webkit based browsers they don't.
If you're interested in why this thing happens, I'll quote this comment of this article :
A problem with Webkit browsers (that aren't following spec) is that the browser can encounter an infinite loop condition caused by media queries, which leads to a browser crash.
For example: >500px overflow-y: scroll, <500px overflow-y: hidden. Size your browser to 505px window width. Since the scroll bar subtracts 15 or so pixels from the width used by the media query, the media query flips you to < 500, but as soon as you hit <500 the scrollbar goes away, and the media query flips you to >500, and then the fun starts because now you have a scroll bar again and you're <500px and you get that style with no scroll bar... Rinse and repeat until the browser finally dies.
Now, write some javascript to calculate the media query max widths, and you have a page that will crash Chrome/Safari as soon as you load it.
My guess is that the spec was written the way it was to prevent this condition. Firefox & Opera are following spec, it's not really their fault you don't agree with spec.
I have the following function that checks the scrolling position of a user so that the menu becomes fixed once they scroll past the masthead
function checkScrolling() {
if( $(window).scrollTop() > $('.masthead').height() ) { // we check against ACTUAL height
$('.menu').addClass('fixed');
}else {
$('.menu').removeClass('fixed');
}
}
and here the desktop and touch screen event listeners:
$(document).bind('touchstart touchend touchcancel touchleave touchmove', function(e){
checkScrolling();
});
$(window).scroll(function(){
checkScrolling();
});
However the touch events only make the menu fixed during the touchmove reliably. If I scroll really fast with a swipe up or down, there is a delay before the menu becomes fixed or unfixed.
Any ideas on how to fix this? See a code example here: http://dev.driz.co.uk/mobileMasthead.html (has been amended based on some answers below, but still does not work correctly on an iPad or iPhone)
Update:
And reading up on the topic reveals that JS like checking scroll position don't happen during the scroll... BUT... I've noticed that http://www.facebook.com/home/ doesn't have this issue when testing it on my iPad. So it's definitely possible to achieve this effect without using any custom JavaScript scrollbars like iScroll or similar.
Maybe I understand the question wrong, but to ensure functionality with high speed scrolling, why don't you tackle it the pure CSS way (aka faking the 'fancy' effect):
Old School (fast but primitive)
HTML
<div id="menu2"></div>
<div class="scroll" id="scroller">
<div id="overlay"></div>
<div id="menu"></div>
</div>
With the simple CSS:
html, body { overflow: hidden; height: 100% }
body { margin:0; padding:0; }
.scroll {
-webkit-overflow-scrolling: touch;
height:960px;
width:640px;
}
#menu2 {
width:640px;
height:20px;
background:red;
position:absolute;
z-index:-1;
}
#menu {
width:100%;
height:20px;
background:red;
z-index:0;
}
You can see a working example HERE.
It may be a bit primitive: but hey! It works! :)
New School (fancy but slow)
Check all of the other answers supplied.
But note that it 'is known' that the usage of JavaScript in combination with scrolling on mobile devices is causing a lot of trouble regarding speed.
That's why I think the simple CSS approach may be better.
If you want to learn about JavaScript and scrolling on mobile devices (and other functions), then there are two articles which I highly recommend reading:
Fast animations with iOS Webkit
Native scrolling: Grins and Gotchas
Facebook doesn't use JavaScript but pure css:
position: -webkit-sticky;
If i remember it correctly this makes the element stick at the top of its parent container when scrolled.
You just needed to attach your scroll events, not to window, document or body, but to a custom container.
On iOS you can't programatically react during these hardware-accelerated window scrolling behaviour.
Here's a fiddle:
a wrapper:
<div id="content">
some not-so-relevant css:
html,body,#content {
width:100%;
height:100%;
}
#content {
background-color: lightblue;
overflow:scroll;
}
attaching the listeners to the container:
function checkScrolling() {
if ($('#content').scrollTop() > mastheadHeight) {
menu.addClass('fixed');
} else {
menu.removeClass('fixed');
}
}
$('#content').scroll(function () {
checkScrolling();
});
You can see it working here for the JS-only fallback:
http://jsfiddle.net/jaibuu/YqPzS/
direct URL: http://fiddle.jshell.net/jaibuu/YqPzS/show/
I once tried to implement sticky headers on mobile version of a site but had encountered whole set of problems.
First and most important is that scroll event does not fire on mobile devices as often as it does on desktop. Usually it does fire when page stops. I don't know the exact reason for that but I can suggest it's because browsers on mobile "send" such tasks to GPU meantime CPU can not keep JS objects up to date with what happens to the canvas (but that's just my suggestion). Newer version of iOSes are making it better for us and probably scroll will work on iPhones.
You should use touch events. This makes you write a separate version of code for devices that support touch input. So have to look for reliable ways of testing for platform.
But touch events are also tricky especially on Android. I thought that in the callback for touchmove event I will be able to figure out current coordinates and go further from that point.
But There this issue https://code.google.com/p/android/issues/detail?id=5491 on Android, in summary touchmove fires once or twice at the very beginning of the touch action and does not fire again. Which makes it totally useless. People are suggesting preventDefault() but you no longer can scroll with that.
So I ended up with idea to reimplement scrolling from scratch using CSS transforms. And here is my results so far:
http://jsbin.com/elaker/23
Open that link on your device and http://jsbin.com/elaker/23/edit on your desktop: you'll be able to edit code and it will live update on you device, very convenient.
NOTE:
I'd like to warn you that this CSS scrolling thing is raw and there are some known problems that are not resolved yet: like you can sometimes scroll beyond top or bottom boundaries or if you just touch (not move) it still will scroll. Also the notorious URL bar will not hide. So there is work to do.
Do you need the touch events to fire this at all? Modern devices should return $(window).scroll() events and scrollTop values. On older Android and and pre-ios5 (I think), position:fixed: didn't work as expected because the of how the viewport worked. But it has been corrected in all new releases.
To further debug devices, I highly recommend Adobe Edge Inspect. You could console.log the scrollTop and see if the devices you care about actually work correctly with out any trickery. You'll get everything you need with their free version.
A great way to dealing with scroll events is not to attach your checks to the scroll event
takes a lot of resources and doesn't work very well with older browsers.
fortunately you can have a lot more control if you just perform a time loop to do that.
codewise that looks like that:
(It's used by twitter)
var MyMenu = $('#menu'),
didScroll = false;
$(window).scroll(function() {
didScroll = true;
});
setInterval(function() {
if ( didScroll ) {
didScroll = false;
//Do your check here
checkScrolling();
}
}, 180);
You should put your $('.masthead').height() outside this checkScrolling function of yours (in a higher scope) as this kind of operations takes a lot of resources and always ask jquery to "select your element" and calculate its size will eventually make your application laggy:
var headerHeight = $('.masthead').height()
function checkScrolling()
.....
Last thing , you can change the value of the interval attribute (right now it's 180 (a bit more that 60hz -. refresh time of the screen) you can make this value bigger, if you want your app to be more performant but a bit less accurate)
Thanks John Resig and twitter:
http://ejohn.org/blog/learning-from-twitter/
Hope that helped
Ajios !
I have just tested your functions for efficiency. You should try this
function checkScrolling() {
if( $(window).scrollTop() > mastheadHeight )menu.css('position','fixed');
else menu.css('position','');
}
This will reduce function call of addClass and RemoveClass and your execution time will take effect. If you want to reduce more execution time, then better to use pure JavaScript
$(document).ready(function(){
var p = $("#stop").offset().top;
$(window).scroll(function(){
if(p<$(window).scrollTop()){
console.log("div reached");
$("#stop").css({position:"fixed",top:0});
}
else{
console.log("div out");
$("#stop").css({position:"static"});
}
})
});
I think this will help you.
The total code is here in jsfiddle.net.
I have tested it for ipad using safari of online ipad2 simulator in http://alexw.me/ipad2/ and it has worked there. So, I think it will work on real ipad too.
setScrollTop: function(inTop) {
var top = Math.max(0,inTop);
if (wm.isMobile) { // should always be true for touch events
top = Math.min(top, this.listNode.clientHeight - this.listNodeWrapper.clientHeight);
if (dojo.isWebKit) {
this.listNode.style.WebkitTransform = "translate(0,-" + top + "px)";
} else if (dojo.isMoz) {
this.listNode.style.MozTransform = "translate(0,-" + top + "px)";
} else if (dojo.isOpera) {
this.listNode.style.OTransform = "translate(0,-" + top + "px)";
} else if (dojo.isIE) {
this.listNode.style.MsTransform = "translate(0,-" + top + "px)";
}
this.listNode.style.transform = "translate(0,-" + top + "px)";
this._scrollTop = top;
this._onScroll();
} else {
this.listNode.scrollTop = top + "px";
}
},
I'm working on a web application for tablets(for android and ios) and I'm facing a problem which is
giving me trouble for 2 days already.
The problem is that on android when you are in portrait mode and for example you focus an input field so the soft keyboard pops up the css media query orientation changes to landscape.
I already have read this question : CSS Media Query - Soft-keyboard breaks css orientation rules - alternative solution? and came up with this :
var is_keyboard = false;
var is_landscape = false;
var initial_screen_size = window.innerHeight;
window.addEventListener("resize", function() {
is_keyboard = (window.innerHeight < initial_screen_size);
is_landscape = (screen.height < screen.width);
updateViews();
}, false);
function updateViews()
{
if(!is_landscape)
{
if(is_keyboard)
{
$("html").removeClass("landscape portrait");
$("html").addClass("portrait");
}
}
}
However this doesn't work for some reason.
Is there anyway to change the orientation to portrait mode so the css media query thinks
we are in portrait mode ? I prefer not the use max-width etc because I need to support multiple screen sizes.
Thanks in advance.
After some searching I came up with this :
#media screen and (min-aspect-ratio: 13/9){ } // landscape
#media screen and (max-aspect-ratio: 13/9){ } // portrait
instead of
#media (orientation : landscape){ }
#media (orientation : portrait){ }
So if you are in the same boat as me I would advise you to just go
with this, so you spare yourself the headache.
There is this good article to solve this problem.
But sometimes 13/9 is not enough.
#media screen and (min-aspect-ratio: 14/9){ } // landscape
Becareful, if you increase it to 16/9, iphone5 don't recognize the landscape.
Separate from min and max "-aspect-ratio" are min and max "-device-aspect-ratio" which are the parameters for the device rather than the screen.
#media only screen and (max-device-aspect-ratio:1/1){
section{background:purple;}
:root{--devmul:1;}
}
#media only screen and (min-device-aspect-ratio:1/1){
section{background:orange;}
:root{--devmul:0.5;}
}
Mozilla et al say the parameter is deprecated but they leave no note as to what it has been deprecated by. Im operating by the assumption that the deprecation is that the parameter is not supported on all devices (like desktop browsers) but is generally available on devices which have an orientation.
The above snippet operated in the context of assumption that the user is on a desktop which is then corrected according to "device-aspect-ratio" and the general aspect ratio. Havent had the opportunity to test my theory out on portrait based desktop devices but this seems to work on all the mobile devices I have.
I'm having a problem detecting a retina iPad (and similar devices) using just screen.availWidth and window.devicePixelRatio. The problem is that iPhones and iPads give the number of dips for screen.availWidth whereas android devices seem to report the number of physical pixels so I can't reliably do screen.availWidth / window.devicePixelRatio to calculate if the screen is of a tablet size.
Is there some other DOM property I can use to help me?
edit - To sum up in a way which hopefully makes clear that the question isn't a duplicate
How can I tell if screen.availWidth reports a value that has already been adjusted to take account of window.devicePixelRatio
That should help
var retina = (window.retina || window.devicePixelRatio > 1);
UPDATE
Retina.isRetina = function(){
var mediaQuery = "(-webkit-min-device-pixel-ratio: 1.5),\
(min--moz-device-pixel-ratio: 1.5),\
(-o-min-device-pixel-ratio: 3/2),\
(min-resolution: 1.5dppx)";
if (root.devicePixelRatio > 1)
return true;
if (root.matchMedia && root.matchMedia(mediaQuery).matches)
return true;
return false;
};
I haven't tested this, but here's an approach I think might work. I'll do a jsbin for it when I get time.
Because all devices (to the best of my knowledge) adjust for devicePixelRatio before passing the value to CSS media queries we can (in slightly pseudo code)
measure window.devicePixelRatio and screen.availWidth
Write a style tag to the head which includes a media query something like the following
#my-test-el {
display: none;
visibility: visible;
}
#media screen and (min-device-width:screen.availWidth) {
#my-test-el {
visibility: hidden;
}
}
Append <div id="my-test-el"> to the page
Read off the style.visibility attribute. If it equals hidden then the css value is the same value as screen.availWidth => screen.availWidth has been preadjusted for dpr.
edit It works! http://jsbin.com/IzEYuCI/3/edit. I'll put together a modernizr plugin too
edit And here's the pull request to get it in Modernizr - https://github.com/Modernizr/Modernizr/pull/1139. please upvote if you'd find it useful
This Modernizr plugin may help : Modernizr Retina : HiDPI Test
Note: Requires Modernizr's Media Queries feature