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.
Related
So I'm working on a react app and I basically want to include a scroll bar for smaller window sizes such as a mobile device. So a scroll bar would if and only if the window size is smaller than the menu itself.
What I have so far as a hacky fix.
const styles = {
dropdownContentClass: {
maxHeight: "15 em",
overflowY: "auto",
}
}
Here I force a max height on the menu so its always scrollable but I would like to have a scroll available if a user is on mobile or the window size is relatively small.
EDIT:
var mq = window.matchMedia("(max-width: 768px)")
if (mq.matches){
var mh = "15em"
var oy = "scroll"
}
return {maxHeight: mh, overflowY: oy}
So I got it to work this this sort of hacky fix. Still kind of new to Javascript but its pretty hard to confirm your code when theres millions of ways to do something.
The browser will have scrollbars by default if the content exceeds the height of the window, so this should already work. If you have a container that has a fixed height that's wrapping your page, like one that's absolutely positioned, you'll need to add overflow-y: auto to it as well.
You can wrap everything into a media query which will apply from smaller devices such as mobiles, up to a maximum width you want the scrollbar to stop being displayed.
Example :
#media (max-width: 768px) {
dropdownContentClass: {
maxHeight: "15 em",
overflowY: "scroll",
}
}
This way, your little workaround will only apply for devices with a maximum of 768px, on tablets or desktops will stop
My issue is a hack for a dubious mobile browser discrepancy between the 100vh and the area hidden behind the mobile browser bars. This is a problem for any page where a full screen effect is desired.
The hack detects the difference between the VH and the actual visible viewport. However the simple js in the first code example appears to be detecting it backwards. So if the browser bar is overlaid the value should be 75 but is instead 0 -- likewise when the the browser is large it should return 0 but instead returns -75.
This can be reproduced simply by testing an iPhone with the 'develop' tool in Safari on google.com and entering this in the console.
document.documentElement.clientHeight - window.innerHeight
This seems to compute the shorter browser height with the top offset to compensate for the browser bar as being offset by 0 and the tall browser height where the browser bar is no longer covering the top of the page at -75px. This, to me, appears to be very wrong and should be 75 and 0 respectively. The below example is how I think it should work to properly offset the body tag to position elements with absolute positioning.
function fixIt(){
var offset = (document.documentElement.clientHeight - window.innerHeight);
document.body.style.marginTop = offset;
}
fixIt(); // run on resize
My extra super hacky solution it to offset the body by 75px by default and to assign the old value on vertical resize. (so when the browser bars hide, instead of 75px offset, use previous offset of 0 and save the 75px offset for the next resize). The works in Mobile Chrome and Mobile Safari, but for instance, if the link is opened in facebook where there is no browser bar and no resize events, it is offset by 75px permanently. This code is obviously insane and should be banished into the nothing.
var offset;
var oldOffset = 75px;
function fixIt(oldOffset){
offset = (document.documentElement.clientHeight - window.innerHeight);
document.body.style.marginTop = oldOffset;
oldOffset = offset;
return oldOffset;
}
fixIt(oldOffset); // run on resize
As an aside, or for further reading, here's a big complaint blog on the issue. https://nicolas-hoizey.com/2015/02/viewport-height-is-taller-than-the-visible-part-of-the-document-in-some-mobile-browsers.html I agree with the browser vendors that VH should be constant (because the reflow would be obnoxious for everything besides the actual body height) but for the top level elements like body and html tags on mobile this behavior appears to be wrong and it makes no semantic sense, plus these computed numbers appear backwards, further making this issue headache inducing.
What other solutions are out there?
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 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
I need to be able to detect whether there are scrollbars (both vertical and horizontal) on a browser window. I've been using this code but it isn't working reliably in Firefox 5.
JFL.GetScrollbarState = function () {
var myWidth = 0;
var myHeight = 0;
if (document.documentElement && document.documentElement.clientWidth) {
myWidth = document.documentElement.clientWidth;
myHeight = document.documentElement.clientHeight;
} else {
myWidth = document.body.clientWidth;
myHeight = document.body.clientHeight;
}
return ({
vScrollbar: document.body.scrollHeight > myHeight,
hScrollbar: document.body.scrollWidth > myWidth
});
}
Is there a better way to do this that will work cross browser. My browser targets are Firefox 4-5, Chrome, Safari 4+, Opera 10+.
If you're interested in why I need to know if there are scrollbars, it's because I have some spinning CSS3 transitions that (due to the nature of their spinning) may temporarily go beyond the edges of the current document size (thus making the document temporarily larger). If were no scrollbars initially present, the CSS3 transition may cause scrollbars to show up during the transition and then go away when the transition is finished, leading to an ugly scrollbar flash. If I know that there are no scrollbars present, I can temporarily add a class that will set overflow-x or overflow-y to hidden and thus prevent the scrollbar flash during the CSS3 transition. If scrollbars are already present, I don't have to do anything because they may move a little, but they won't go on/off during the transition.
Bonus points if one can actually tell not only if scrollbars would generally be required, but whether they are actually there or not.
After running into flicker problems with the scrolling version proposed by David in some browsers (Safari and IE), I've settled on this code that does not have the flicker problem:
function getScrollBarState() {
var result = {vScrollbar: true, hScrollbar: true};
try {
var root = document.compatMode=='BackCompat'? document.body : document.documentElement;
result.vScrollbar = root.scrollHeight > root.clientHeight;
result.hScrollbar = root.scrollWidth > root.clientWidth;
} catch(e) {}
return(result);
}
It's a derivative of what I was using and the general technique was referenced in the post that fanfavorite posted. It seems to work in every browser I've tried even in IE6. For my purposes, I wanted any failure to return that there was a scrollbar so I've coded the failure condition that way.
Note: this code does not detect if a scrollbar has been forced on or off with CSS. This code detects if an auto-scrollbar is called for or not. If your page might have CSS settings that control the scrollbar, then you can get the CSS and check that first.
Have you taken a look at this other post? How can I detect a Scrollbar presence ( using Javascript ) in HTML iFrame?
It's actually pretty easy. This will work in every modern browser:
// try scrolling by 1 both vertically and horizontally
window.scrollTo(1,1);
// did we move vertically?
if (window.pageYOffset != 0) {
console.log("houston, we have vertical scrollbars");
}
// did we move horizontally?
if (window.pageXOffset != 0) {
console.log("houston, we have horizontal scrollbars");
}
// reset window to default scroll state
window.scrollTo(0,0);