JavaScript - - By Dmitri Lau

How to Create a QR Code Reader for Your Mobile Website

The barcode and QR code have modernized our shopping and searching experience. Customers carrying smartphones can now pick up a product anywhere in the world, scan its barcode or its QR code using one of the many free phone apps and find out its lowest price as well as where it can be bought.

Screenshots of apps with barcode scanner buttons

Companies like Walmart and Amazon have embraced this technique to draw customers to their online and offline stores using their phone app. Other companies like Fedex and UPS allow customers to scan the codes on packages using a phone app, instead of needing to manually type in long lists of characters.

If the users of your mobile website have a need to type in long codes like activation codes or they like to look up specific products on your website based on a model number printed in a magazine or advertisement, then you too can take advantage of QR codes to eliminate the frustration of tiny keyboards and spare them the need to double check for errors.

QR Code Scanning with your Mobile Website

You don’t need a native phone app to scan QR codes —it’s quite simple to create your own QR code reader. Your website running on a smartphone equipped with a camera and running a little JavaScript can do the same trick.

Here is a demo of a text field with an in-built QR button. In addition to being able to type text into the field, users can also click a button to activate the camera.

Browser Requirements:
Your users need to be on an iPhone running iOS 6 or higher or an Android phone running Android 3.0 or higher. Other devices have not been tested.

See the Pen Bare QR reader demo by SitePoint (@SitePoint) on CodePen.

If you are on a supported device, go ahead and click it. Depending on your phone, it will either launch the camera immediately or ask you whether to open the camera or browse your photo library. On unsupported devices, the browser will open up a regular file explorer window.

If you don’t have a QR code handy to scan, here is one that shows the first 8 digits of Pi.

Test the QR Code Reader: QR code encoding the value of Pi

Watch Add More Responsive Tricks to Your Toolbelt
No longer cutting edge, it's the norm

Creating the QR Code Reader

The magic starts with the file upload element. We spruce it up with an accept attribute which tells the browser that we only want images and we give it a capture attribute which means we want the input to be captured now, as opposed to uploading an old picture from the phone’s memory.

<input type=file
       accept="image/*"
       capture=environment>

Beautifying the Upload element

Because the file upload element is difficult to style and takes up a whole bunch of space, we want to shrink it down to an icon, which we can achieve by making it invisible and wrapping it inside a label element. The label element provides an area for the user to click on, as well as giving us an area to add our QR icon.

Notice that we’ve also added a tabindex=-1 attribute to prevent the user from accidentally reaching the file upload element with the tab key, since we intend to make it invisible.

<label class=qrcode-text-btn>
  <input type=file
         accept="image/*"
         capture=environment
         tabindex=-1>
</label>

The class name of qrcode-text-btn on the outer label element allows us to style both elements with the following CSS:

.qrcode-text-btn {
  display: inline-block;
  height: 1em;
  width: 1em;
  background: url(qr_icon.svg) 50% 50% no-repeat;
  cursor: pointer;
}

.qrcode-text-btn > input[type=file] {
  position: absolute;
  overflow: hidden;
  width: 1px;
  height: 1px;
  opacity: 0;
}

In the first CSS block, we are giving the label element a display:inline-block property, which allows us to assign a height and width to the element.

Then we give it a background image, which is the QR icon that the users of our QR code reader will see.

In the second CSS block we are using a direct descendant selector (>) to select the file upload element and apply a couple of CSS properties to make that element invisible:

We give it a position:absolute property, so that it is excluded from the regular document flow, which means objects around it will flow over and under it as if it didn’t exist.

We give it the width:1px and height:1px properties, so that it doesn’t accidentally cover up other things on the page.

We give it an opacity:0 property, so that it becomes transparent and therefore hidden. Note that we can’t simply use display:none, because some browsers will completely disable the file upload element if we do that.

Introducing the Text Input element

Next we introduce the text input element that will house the QR button for our QR code reader.

<input type=text class=qrcode-text
><label class=qrcode-text-btn>
   <input type=file
         accept="image/*"
         capture=environment
         tabindex=-1>
</label>

Tip:
Any white-space characters, including empty lines, in between 2 inline elements will create a gap that you might not want and affects our math. We have eliminated the white-space above by moving the end bracket (>) to the next line touching the other element’s opening bracket (<).

This is what our QR code reader should look like so far:

Button position before shifting to the left

Right now the QR button is to the right of the text element. To make it appear inside it, we will shift it to the left overlapping part of the text field, using CSS.

.qrcode-text {
  padding-right: 1.7em;
  margin-right: 0;
  vertical-align: middle;
}

.qrcode-text + .qrcode-text-btn {
  width: 1.7em;
  margin-left: -1.7em;
  vertical-align: middle;
}

In the first CSS block, we are giving the text element some white space on the right where the QR button will be located. Without the padding, any text inside the text element will run into and overlap the QR button.

We give it a margin-right:0 property, because by default the text element has a non-zero margin which affects our math.

In the second CSS block, we are using the adjacent element selector (+) to select and apply CSS to the QR button, to cause it to shift to the left to overlap the text element.

Know Your Selectors: With the adjacent element selector, the CSS is only applied when the QR button is next to the text element. When the QR button is re-used elsewhere, the selector syntax won’t apply and the QR button won’t shift to the left unnecessarily.

We need a vertical-align:middle property on both the text element and QR button to make the QR button appear vertically centered in the text box. Without it, the QR button will shift up because by default, the bottom of all inline-block elements are vertically aligned to the baseline of any inline elements around it.

Notice we’re also giving the QR button a bigger width of 1.7em to give users a bigger touch target. The QR icon itself won’t get bigger, because the icon is only an image centered on the button and the button itself is transparent. The actual clickable area can be observed in this rendering where the button has been given a green background color.

Actual clickable area shown in green

Including the Dependent JavaScript Libraries

The secret to reading QR codes is math and the substitute for math is open source libraries. To read QR codes we will be using the JavaScript port of the Java based image processing library written by ZXing. The JavaScript version was ported by Lazar Laszlo.

Because the JavaScript library consists of 17 files, I have taken the liberty of merging them into one file, wrapping the code in an anonymous function to prevent pollution of the global namespace and putting the file through Google Closure’s minifier to make the file size smaller.

Some Minor Tweaks to the Library

In order to make the library more adaptable, I have also added a few minor changes to the library’s output function to differentiate between a success response and an error response.

Two important changes made were made in qrcode.js to these 2 lines:

qrcode.result = "error decoding QR Code";
//...
qrcode.callback("Failed to load the image");

These strings have been replaced by Error objects:

qrcode.result = Error("error decoding QR Code");
//...
qrcode.callback(Error("Failed to load the image"));

Now I can detect in my callback function whether an error occurred, just by checking if the callback payload is an instance of Error or not.

Those changes can be found in this fork of the library.

Adding the Script Tag

To use the library in our QR code reader, we first need to include it in our HTML using a regular script tag:

<script src="https://rawgit.com/sitepoint-editors/jsqrcode/master/src/qr_packed.js">
</script>

Hooking up the Event Handlers

Then we need to hook into the onchange event of the file upload element:

<input type=file
       accept="image/*"
       capture=environment
       onchange="openQRCamera(this);"
       tabindex=-1>

Then we introduce the JavaScript function that will read the image in the file upload element and pass it to the QR library.

function openQRCamera(node) {
  var reader = new FileReader();
  reader.onload = function() {
    node.value = "";
    qrcode.callback = function(res) {
      if(res instanceof Error) {
        alert("No QR code found. Please make sure the QR code is within the camera's frame and try again.");
      } else {
        node.parentNode.previousElementSibling.value = res;
      }
    };
    qrcode.decode(reader.result);
  };
  reader.readAsDataURL(node.files[0]);
}

In this function, we are creating a FileReader object which can read the binary data of the file.

Once the FileReader has finished reading the file, we assign a callback to the QR library and send the file contents to the decode function of the library. The library will call our callback and either return an Error object or return the value of the QR code as a string.

If successful, we set the text element to the value of the QR code. If an error occurs, we show an alert box telling the user to try again.

Showing a Helpful Alert for Beginners

To make our QR code reader a little more intuitive for beginners, we will hook into the onclick event on the upload element and show an alert box before the camera appears, describing how to use the camera.

<input type=file
       accept="image/*"
       capture=environment
       onclick="return showQRIntro();"
       onchange="openQRCamera(this);"
       tabindex=-1>

We will use a confirmation box instead of an alert box, so that the user can abort if they want to, after reading the tip.

function showQRIntro() {
  return confirm("Use your camera to take a picture of a QR code.");
}

See the QR Code Reader in Action

This is what our QR code reader looks like so far:

See the Pen Nicer QR reader demo by SitePoint (@SitePoint) on CodePen.

How about Unsupported Browsers?

Right now the QR button will show up in all browsers regardless of support. We need a way to hide it in unsupported browsers.

We cannot use feature detection, because only iOS supports the capture attribute, and for Android, we are relying on the default behavior of webkit-derived browsers running on mobile devices of showing a selection sheet with Camera being one of the options.

On Windows tablet and laptop devices, the default behavior is to show the file explorer window, which doesn’t help as it doesn’t offer a camera option.

Our best bet is to use screen size detection and only show the QR button on smartphones, because marketshare of non-iOS and non-Android devices is only 0.7% according to IDC (Nov 2016), and it also makes sense since taking photos with a tablet or laptop is difficult considering it’s size and weight. We will use the arbitrary cut off size of 750 pixels width, which supports the iPhone 6 Plus (width is 736 pixels) and excludes netbooks (width is 800 pixels in portrait).

We will be using CSS media queries to accomplish this.

Using CSS Media Queries

First we define the media query for when the device width is 750 pixels or less.

@media only screen and (max-device-width:750px) {
  /* previous CSS code goes here */
}

We need to take all the CSS code we wrote earlier and put it inside the curly brackets above. This causes the CSS to only apply when the device width is less than 750 pixels.

Now we need to add some additional CSS code outside that media query block to hide the QR button by default. Put this code just before the media query block.

.qrcode-text-btn {
  display: none;
}

@media only screen and (max-device-width:750px) {
   /* ... */

Provide Consistency for Designers

While handy but not necessary, we will also add some additional CSS code outside the media query block to make the text input element behave consistently on both supported and unsupported devices.

.qrcode-text {
  box-sizing: border-box;
  vertical-align: middle;
}

@media only screen and (max-device-width:750px) {
   /* ... */

We have given the qrcode-text element a box-sizing:border-box property, which makes sure its width calculation is inclusive of any padding. This means when the text element gets extra padding (when we previously used padding-right:1.7em), the width of the text element won’t be wider, but will instead steal space from it’s internal dimensions.

We have given it a vertical-align:middle property, so that it’s vertical alignment is consistent regardless of whether it is on a supported or unsupported device.

Conclusion

So there we have it, your very own QR code reader for your mobile website.

QR codes have been around for many years and the image processing code written by ZXing was first ported to JavaScript almost 6 years ago. Lazar Laszlo, the author of the JavaScript port, even has a proof of concept on his website using various HTML technologies to capture images and process for QR codes.

The technique shown here is just a rehashing of current technologies into a more attractive and user-friendly format for user consumption.

There is still room to improve our QR code reader. We can write code to use getUserMedia to capture images from the camera, which is more versatile, but that technology is newer and will exclude older iPhone and Android devices.

The image processing code could also be improved for faster detection and better handling of background noise.

Such changes can be left for a future article.

Downloadable Assets

This article was peer reviewed by Giulio Mainardi. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

Sponsors