Auto Detecting Mobile Devices & Screen Orientation
With Vanilla JavaScript
As an extension of something I wrote about in my last blog, Top Ten Tips I Learned From Development, I wanted to go into a little more detail about automatically detecting mobile devices and screen orientation with vanilla JavaScript.
This has been something I have been working with a lot over the past week, and I thought it would be a useful topic to write a blog about. There are a lot of ways to design responsive layouts, especially with how many options JavaScript provides us, but the solution we are going to work through today is fairly simple and easy to understand.
*** NOTE *** I would like to point out something Hugo Larrousse mentioned that this solution does not work with Safari (thanks Hugo!).
So, let’s get coding.
THE BASICS
First, let’s try to detect if a user is connecting to our project from a mobile device or a desktop/laptop (for brevity’s sake, let’s just say a computer).
We can start off by making sure we are detecting things statically on page load. Then, once we have that working, we can figure out how to automatically update our functions whenever a device is switched or screen orientation is changed. Finally, we can manipulate a few DOM elements with what our functions are returning to make sure everything is working as intended.
To make sure we are on starting off on the same foot, here is the folder structure I am working from:
For now, detectDevice.js
and testLog.js
are blank.
index.html
is a basic HTML skeleton with a root
div
, an h1
element, and an h2
element. It also loads our detectDevice.js
script. We will briefly use testLog.js
later, but it is not essential:
Really basic stuff.
DETECTING A MOBILE DEVICE
The browser’s navigator
object
provides us with a property that perfect for what we need. navigator.maxTouchPoints
sets whether or not a connected device has a touch screen.
If navigator.maxTouchPoints === 0
, we can safely assume a connected device is a desktop or laptop (unless it also has a touch screen but we aren’t going to worry about that today). If navigator.maxTouchPoints === 1
, we can also assume the connected device is a smart phone or tablet.
Since 0 is a falsey statement, we can also double banging navigator.maxTouchPoints
to return
true
or false
instead of 1 or 0: !!navigator.maxTouchPoints
.
To test this out, we can write a function
in detectDevice.js
called…well…detectDevice
, and console.log(!!navigator.maxTouchPoints)
:
In Chrome, this will console.log()
true
if the device is a mobile device or false
the device is a computer:
Desktop:
Mobile:
Great.
The only problem is that it does not automatically update if we switch between devices. We will fix this later. For now though, let’s get our device orientation detection working.
DETECTING SCREEN ORIENTATION
Detecting a screen’s orientation is very similar to how we can detect devices. There is another very useful property in our browser’s window
object
called window.screen.orientation.angle
that is perfect for what we need.
window.screen.orientation.angle
sets the degree at which the browser’s viewport is rotated, and will return
0 if a mobile device is held sideways (landscape), or 90 if a mobile device is held right side up (portrait). Once again, since 0 is a falsey statement, we can single or double bang window.screen.orientation.angle
and treat it as a boolean expression.
With this, we can add another function
to detectDevice.js
called detectOrientation
. In combination with navigator.maxTouchPoints
, we can write a nested ternary statement to return
‘desktop’ or ‘mobile’ depending on if a device has a touch screen, and ‘landscape’, or ‘portrait’ depending on if a mobile device is being held sideways or right side up (landscape or portrait orientation):
In Chrome, this will console.log()
‘desktop’ if navigator.maxTouchPoints
is false
. If it is false
, it will then check if window.screen.orientation.angle
is false
and return
‘portrait’. If it isn’t, it will return
‘landscape’:
Desktop:
Landscape:
Portrait:
Nice. This means our two functions are properly detecting mobile devices and screen orientation.
But they are doing so statically, and only once on page load.
Ideally, we would have these function
s update what they are return
ing and log
ging dynamically. Like if a user rotates their phone, or if they want to send what they are browsing from their phone to their laptop or desktop. We would want our detection functions to notice that, and say something if something does change.
Well, we can figure that out too.
DYNAMICALLY DOING BOTH
First, let’s combine our two detection function
s into a single function
, and have our new function
return
an object
. That way we can package both detection methods together:
I’d also like to remove the function
call from our script. Call it a personal preference, but it will also help set us up for what we want to do later. I still want detectDevice()
to run whenever we first load our page, and we can keep that functionality by adding a “DOMContentLoaded”
event listener
and have it invoke the detectDevice
function
:
If we check the browsers console now, we should get a nice object
returned to us whenever a page is reloaded:
Desktop:
Landscape:
Portrait:
Now we can turn our attention to updating the detectDevice
object
dynamically.
We can do so by adding a “resize” eventListener
to the browser’s window
object
. The catch is we need our detectDevice
function
to execute both on page load, and whenever the browser’s window is resized.
This means we are going to need another function
that is called with the “DOMContentLoaded”
eventListener
. This function
invokes the detectDevice
function
, and adds a new “resize” eventListener
to the window
object
, and calls detectDevice()
whenever the browser window is resized:
This should do exactly what we need.
If we go back to the browser, and try to switch between devices and orientations, we should be getting the correct console.log()
and return
values for our object
:
Now…yes. I am aware that we are getting two return
values with a single invocation of detectDevice()
. I had to do some research and figure out why.
It turns out that when switching devices or orientations in Chrome’s dev tools, the window is actually getting resized twice.
We can test this with the 2nd script that is commented out in index.html
, testLog.js
:
We can add the same “resize” eventListener
to the window
object
. This time, we can console.log()
the two properties window.innerWidth
and window.innerHeight
to see what is actually happening.
In testLog.js
:
In index.html
:
Here is what gets returned:
So, it doesn’t seem to be a problem with our solution, but just that the window is being resized twice whenever we switch between orientations, which causes the double invocation.
Interesting.
Either way, this doesn’t pose a problem for what we have written. We have detectDevice.js
dynamically detecting both mobile devices and screen orientation, and all we are using are a couple of eventListners
and a few simple functions
.
Splendid.
UPDATING THE DOM
The last thing I wanted to do for this blog is some simple DOM manipulation and update the innerHTML
of the two header elements with what detectDevice.js
is detecting and return
ing.
For this, I am going to remove the return
values and console.log()
s from the detectDevice
function
, and chain 1 extra function
onto it.
This newfunction
will take detectDeviceObj
as an argument, then query select both header elements, and replace their innerHTML
with the appropriate properties from detectDeviceObj
:
We should now see both h1
and h2
tags update whenever we switch devices or screen orientations:
Fantastic.
MISSION COMPLETE
While we could come up with many other solutions for detecting mobile devices and screen orientations (there are many), I have found the solution we came up with today to be one of the more clear cut and easy to understand solutions out there.
As I first mentioned at the beginning of this blog, this solution won’t work with Safari (yet). Safari does not support navigator.maxTouchPoints
or window.screen.orientation.angle
. I have a few potential solutions for this, but I am still trying to find the cleanest solution that does not involve browser sniffing. I will update this blog when I do.
My next blog will get into mobile device and screen orientation detection with React. We will use the same navigator
and window
properties as we did today, but it’s implementation will be a little different. Again, it’s nothing complex, but something I have found to be extremely useful. Especially if you don’t want to mess with CSS media queries.
Either way, I hope you got some useful information, and may all your functions return true, and all your requests respond with 200.
Stay safe…stay healthy…and keep fighting the good fight.