Auto Detecting Mobile Devices & Screen Orientation

With Vanilla JavaScript

Austin Smith
Level Up Coding

--

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 functions update what they are returning and logging 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 functions 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 returning.

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.

--

--