I'm showing a notification bar on my website, and frankly, it doesn't work well when its on a mobile device. I'd like to show the bar ONLY for desktop users.
What is the easiest way to determine if a user is on desktop or on mobile?
A user agent check is the "easiest", though you could easily employ CSS3 media queries
Here is an example that checks iphone, android and blackberry; you could easily add other mobile browsers.
var is_mobile = !!navigator.userAgent.match(/iphone|android|blackberry/ig) || false;
Check this http://detectmobilebrowsers.com/
Work for Javascript, jQuery etc.
I find that it's best to use feature detection. Use Modernizr to detect if it's a touch device. You can do things like:
var mousedown = 'mousedown';
if (Modernizr.touch) {
mousedown = 'touchstart';
}
$('.foo').on(mousedown, handleMouseDown);
And then use CSS Media Queries for handling screen width (and it's also easy to detect screen width with javascript). That way you can correctly handle touch devices with large screens, or non-touch devices with small screens.
If you use modernizr. a "no-touch" class will be added to the element. You could hide the bar by default and add a css rule to show the bar if the "no-touch" class exists. Example:
default:
.bar{display:none;}
desktop:
.no-touch .bar{display:block;}
If the user is on a mobile device this javascript 'if' will return true.
if (navigator.userAgent.indexOf('Mobile') !== -1) { ...
See also: https://deviceatlas.com/blog/list-of-user-agent-strings
The easiest way to differentiate between touch and non-touch devices is using media queries.
1) CSS to target Mobile/Touch Devices can be written using media query,
#media (hover: none), (pointer: coarse) {}
2) CSS to target Desktop/Non-Touch Devices (only) can be written using media query,
#media not all and (pointer: coarse) {}
On Few latest mobile devices (Eg: IOS 10+, one plus etc.,.) hover is detected hence we use, the (2) to identify non-touch devices.
const is_mobile = navigator.userAgent.match(/Android/i) || navigator.userAgent.match(/iPhone/i) || navigator.userAgent.match(/BlackBerry/i);
if (is_mobile != null){
popup.modal('show');
}
If you are reading this post 2021, there is an even easier way to find this out.
let isMobile = window.navigator.userAgentData.mobile;
console.log(isMobile);
The above method simply returns a boolean value.
Related
I'm working on a site for a client who bought a template and the issue is that now client wants the slider remove from hand held devices.
I know that we can use <picture> tag to assign different images based on screens size but what I'm trying to do here is to disable(by disable i mean canceling image load on mobile devices, not {display:none;} the slider loading all together from a certain screen size.
We'll probably have to create a script to call event assigned to that <div> to cancel its loading on for instance any screen size bellow 768px.
How can I do this?
(BTW I'm still learning JS .. any help is muchappreciated)
Many thanks in advance.
I have a simple solution to achieve your need. Below is the mobile check method.
// Mobile Device Check
function MobileDeviceCheck() {
var mobileFlag = false;
var isMobile = window.matchMedia("only screen and (max-width: 768px)");
if ($(window).innerWidth() < 768 || (/Android|webOS|iPhone|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent))) {
mobileFlag = true;
}
return mobileFlag;
}
Call the Above function to check the mobile device and do your necessary actions.
// Load Device Specific Actions
var mobileFlag = MobileDeviceCheck();
if (mobileFlag == true) {
// Do Your Actions
}
I have a span that needs its font-size value changed when the window is resized. I do that with the jquery code:
$(window).on('resize', function () {
y = $(window).height();
x = $(window).width();
if (y > 500) {
$('#testing').css("fontSize", "50px");
} else if (y < 500) {
$('#testing').css("fontSize", "10px");
}
});
and may also do an equivalent with the css code:
#media screen and (max-height: 500px)
{
#testing{
font-size: 10px;
}
}
which is actually more efficent? Is one solution more acceptable/common? Is there a better way to do this? And also which way is more acceptable on a mobile device? Is there a better way to do this on a mobile device?
Which is actually more efficent? Is one solution more acceptable/common?
Performance wise, CSS is way better. Using media queries will be faster than processing a function on every resize (which are called numerous times). Of course, you could insert a throttle and call it once when the window didnt resize in a said lapse of time, but even there, media queries will be faster.
Furthermore, Javascript window width may not alway be the same than the CSS media queries. Scrollbar can change value and other things may also affect the width. You have to use a function to check the real width, which consume even more juice. It look like that :
function viewport() {
//Get the current view port
var e = window, a = 'inner';
if (!('innerWidth' in window )) {
a = 'client';
e = document.documentElement || document.body;
}
return { width : e[ a+'Width' ] , height : e[ a+'Height' ] };
}
The down side of mediaqueries is the support, it is not supported in old IE. But there is of course some libraries that solve this issue.
Is there a better way to do this?
Not really, media queries are fast and efficient.
And also which way is more acceptable on a mobile device? Is there a better way to do this on a mobile device?
Most mobile browser support media queries. You should always use media queries on mobile, it is the prime reason why they exist.
Self shaming promotion and why Javascript media queries could be usefull
There is a case where javascript could be used as media queries. It would be usefull if you want to change the font-size dynamically depend and a screen size with a ratio. Doing that with media queries would be long and painfull since you'd have to do 20~30 media queries depending on the smoothness.
I've created a plugin changing font size depending on the screen for that reason.
https://github.com/kagagnon/Responsive-Font
If you're not worry about cross browser compatibility problem since media queries is CSS3 which is not supported for old browsers such as IE7, IE8... then I'd suggest you to use CSS over javascript.
In the other hand, beside better in handling cross browser compatibility issues, if a task cannot be achieved through CSS then you should probably go with javascript instead.
In short, I'm always prefer CSS than Javascript :)
Depends, using a responsive design will allow you to do much more than just change the font size.
vw units is a good option if you can. See this article
As my opinion ,You have to prefer JQuery,because the reason is : It is a library that fully support most of devices and auto configure them self as per the environment need ! You have to not think about what the browser need only put a line of code jquery do it in its own way. And place apply your style with respect to device or browser !
When working on responsive designs I make use of CSS3 media queries, including with older browsers using respond.js. There are occasions when I also need to use JavaScript/jQuery for manipulating some elements on the page. However, when using $(window).width(), that value is often different than the value used in the CSS media query due to browser chrome and/or the presence of a vertical scrollbar, meaning a CSS media query will fire at a different breakpoint than the JS media query.
I know Modernizr alleviates this issue through the use of Modernizr.mq, but that only works in browsers that support CSS media queries to begin with, generally meaning it won't work in IE8 and below. And if you use a polyfill for adding media query support to these older browsers (e.g., respond.js), then the Modernizr.mq no longer works due to a conflict/override between the two.
What I've instead done is made use of a hidden input element on the page and given it a CSS width style within each of the media queries. I then have a function that runs automatically on the page (and on resize) that gets that width value and executes the following:
if (width == "320px" ) {
//Functions
}
//481px to 600px
else if (width == "481px" ) {
//Functions
}
//601px to 768px
else if (width == "601px" ) {
//Functions
}
//769px to 960px
else if (width == "769px" ) {
//Functions
}
//769 to 1024px
else if (width == "961px" ) {
//Functions
}
//1024px+
else {
//Functions
}
This basically forces the JS to work entirely off of the CSS media query, syncing the two. This can become more specific rather than generic as I have it, instead firing based off of a specific element and a specific style that changes on that element, it depends on individual project needs.
My question then is, is there a simpler way of doing this? I've spent considerable time looking into and experimenting with this and it seems the best option thus far that takes into account older browsers as well.
PPK listed a nice way to pair the CSS & JS on his blog which sort of backs up your method:
#media all and (max-width: 900px) {
// styles
}
if (document.documentElement.clientWidth < 900) {
// scripts
}
His full post on it is here.
There's also the enquire.js plug-in.
I have a surface web app that uses touch panning (container divs have "overflow: auto" style) and I'm using the built-in paging scroll styles:
-ms-scroll-snap-points-x: snapInterval(0px, 1366px);
-ms-scroll-snap-type: mandatory;
My app has a 300% width child container resulting in 3 pages that snap on page boundaries.
This works great for high-performance paging scrolling, except when the user is on the first page and they swipe to the right, which activates the browser's built-in back gesture, exiting my web app and going into the user's IE10 history.
I'm able to disable the back gesture using:
-ms-touch-action: none;
But that also disables touch scrolling so the page is no longer draggable. If I use:
-ms-touch-action: pan-x;
Then the scrolling works again but the browser back gesture reappears which is a really annoying user experience. Is there a way to allow panning but not the history gesture?
The solution is simple, you just need to add a CSS style that prevents scroll behavior from bubbling up from child elements that have reached their scroll limit, to parent elements (where the scroll eventually turns into a top-level history navigation).
The docs (http://msdn.microsoft.com/en-us/library/windows/apps/hh466007.aspx) state that the default is:
-ms-scroll-chaining: none;
However the default appears to really be:
-ms-scroll-chaining: chained;
I set that style to none by default and chained on the elements in my carousel that really should be chained, which disabled history navigation gestures in my app:
* {
-ms-scroll-chaining: none;
}
.carousel * {
-ms-scroll-chaining: chained;
}
You need to set -ms-touch-action: none; on all elements.
This will instead fire events to your JavaScript handlers, (if there are any), but will prevent ALL new actions including: panning, zooming, and sliding. This is best if you'd like to custom tailor how your app utilizes touch.
Not an ideal or elegant solution, but can you use the MSPointerDown, MSPointerMove and MSPointerUp event listeners to detect a swipe and preventDefault?
// Touch events
target.addEventListener('MSPointerDown', callback);
target.addEventListener('MSPointerMove', callback);
target.addEventListener('MSPointerUp', callback);
Edit: The following doesn't prevent swipe causing navigation on Windows Phone 8.1, although it does prevent swipe navigation for me on a windows 8.1 tablet. Leaving answer here, since it might be partially useful to someone.
If your page is larger than the viewport (touch to pan), then the following CSS works (edit: only on 8.1 tablet):
html {
-ms-scroll-chaining: none;
}
If your page is smaller than then viewport (i.e. the page is not scrollable/pannable e.g. zoomed out) then the above doesn't work.
However code similar to the following works (edit: only on 8.1 tablet) for me:
CSS:
html.disable-ie-back-swipe {
overflow: scroll;
-ms-scroll-chaining: none;
}
JavaScript:
if (navigator.msMaxTouchPoints) {
if (/Windows Phone/.test(navigator.userAgent)) {
document.documentElement.classList.add('disable-ie-back-swipe');
} else {
try {
var metroTestElement = document.createElement('div');
metroTestElement.style.cssText = 'position:absolute;z-index:-1;top:0;right:0;bottom:-10px;width:1px';
document.body.appendChild(metroTestElement);
if (Math.round(window.outerWidth - metroTestElement.offsetLeft) === 1) {
document.documentElement.classList.add('disable-ie-back-swipe');
}
document.body.removeChild(metroTestElement);
} catch (e) { // window.outerWidth throws error if in IE showModalDialog
}
}
}
Notes on the JavaScript:
Testing navigator.msMaxTouchPoints checks that this is IE10/IE11 and that the device uses touch.
Edit: detects Windows Phone and sets disable-ie-back-swipe however the CSS doesn't actually disable the feature on Windows Phone 8.1 ARRRGH.
The metroTestElement tests for modern (modern doesn't have scrollbars so right is 1 pixel, whereas desktop has scrollbars so right is 18 pixels or so depending on scrollbar width).
The code only disables the back swipe if IE11 and modern is used.
It seems that either html or body can be used for the CSS rules, and I am unsure which is actually better (IMHO I usually think of the body as the page and not scrollable, and html as the viewport/window, but actually depends upon IE implementation details).
Edit 2: This IE feature is called "flip ahead". Corporates may be able to disable it using group policy - see http://www.thewindowsclub.com/enable-disable-flip-feature-internet-explorer-10
On a project I was working on needed exactly this but needed to scroll in certain areas (not just general scrolling but overflow). Seems the following does work, tested on IE11 (Metro) Surface 2 and IE11 WinPhone on Lumia 930. Also with touch mouse which does the horizontal scrolling too.
Please see this demo here: http://jsbin.com/fediha/1/edit?html,css,output
The trick to disable history back/forward) seems to be to disable "pan-x" on any element except the ones you want to scroll horizontally. Excerpt from CSS:
* {
/* disallow pan-x! */
touch-action: pan-y pinch-zoom double-tap-zoom;
/* maybe add cross-slide-x cross-slide-y? */
}
.scroller-x,
.scroller-x * {
/* horizontal scrolling only */
touch-action: pan-x;
}
.scroller-y,
.scroller-y * {
/* vertical scrolling only */
touch-action: pan-y;
}
On rare instances history back is still triggered but that is really rare (could only do this by wildly flicking on the tablet and even then not it does only happen sometimes).
touch-action is IE11 only, on IE10 you'd need -ms-touch-action but IE10 is not used that much anymore and I have no test device with it.
I am trying to find a script that detects if a device places position: fixed elements relative to the ViewPort and not to the entire document.
Currently, standard desktop browsers and Mobile Safari (for iOS 5) do so, whereas Android devices place the fixed elements relative to the entire document.
I have found a couple of tests to detect this, but none of the seem to work:
http://kangax.github.com/cft/ Gives me a false positive when I pass it from an Android device.
https://gist.github.com/1221602 Gives me a false negative when I pass it in an iPhone with iOS 5.
Does anybody know where to find / how to write a test that actually detects that? I don't want to rely on browser sniffing.
According to the contributors at Modernizr, you cannot do this without detecting the browser in use. The contributors are quite well-established in the field.
Testing for position: fixed on iOS and Android devices is listed under the Undetectables wiki page in the Modernizr project.
The MobileHTML5 website lists the support for position:fixed. http://mobilehtml5.org/
Actually, the guys from the Filament Group did a smart thing with their Fixedfixed putting the user agent string of known false positives in their test.
Check it # http://github.com/filamentgroup/fixed-fixed
Someone could complete it with some false negatives too, and make it a modernizr aditional featur test.
I've created another check if position:fixed is really supported in browser. It creates fixed div and try to scroll and check if the position of div changed.
function isPositionFixedSupported(){
var el = jQuery("<div id='fixed_test' style='position:fixed;top:1px;width:1px;height:1px;'></div>");
el.appendTo("body");
var prevScrollTop = jQuery(document).scrollTop();
var expectedResult = 1+prevScrollTop;
var scrollChanged = false;
//simulate scrolling
if (prevScrollTop === 0) {
window.scrollTo(0, 1);
expectedResult = 2;
scrollChanged = true;
}
//check position of div
suppoorted = (el.offset().top === expectedResult);
if (scrollChanged) {
window.scrollTo(0, prevScrollTop);
}
el.remove();
return suppoorted;
}
This function was tested in Firefox 22, Chrome 28, IE 7-10, Android Browser 2.3.