Javascript: How to read a hand held barcode scanner best?

JavascriptJqueryAjaxAngularjsBarcode Scanner

Javascript Problem Overview


I'd like to be able to scan barcodes via a hand held scanner and handle the results with Javascript.

A barcode-scanner works almost like a keyboard. It outputs the scanned/translated (barcode->number) data raw (right?). Actually I just need to catch the output and proceed. But how?

Here's some pseudocode I'd like to make work:

$(document).on("scanButtonDown", "document", function(e) {
    // get scanned content
    var scannedProductId = this.getScannedContent();
    
    // get product 
    var product = getProductById(scannedProductId);

    // add productname to list
    $("#product_list").append("<li>" + product.name + "</li>");
});
  • Any ideas (frameworks, plugins, snippets)?
  • Any barcode-scanner (hardware) recommendation?

Thanks in advance!

I found this and this good questions but I'd like to get more information about the handling. Just to focus a textarea may be not enough in my case.

Javascript Solutions


Solution 1 - Javascript

Your pseudo code won't work, because you don't have access to the scanner to catch events like scanButtonDown. Your only option is a HID scanner, which behaves exactly like a keyboard. To differentiate scanner input from keyboard input you have two options: Timer-based or prefix-based.

Timer-based

The scanner is likely to input characters much quicker than a user can (sensibly) with a keyboard. Calculate how quickly keystrokes are being received and buffer fast input into a variable to pass to your getProductsId function. @Vitall wrote a reusable jQuery solution for catching barcode scanner input, you would just need to catch the onbarcodescanned event.

Prefix-based

Most scanners can be configured to prefix all scanned data. You can use the prefix to start intercepting all input and once you've got your barcode you stop intercepting input.

Full disclosure: I work as a consultant to Socket Mobile, Inc. who make handheld scanners.

Solution 2 - Javascript

After a lot of research and testing, what worked the best for me was to capture input from a barcode scanner without focusing a form input. Listen to the keydown and textInput events.

The textInput event acts like a paste event. It has then entire barcode data. In my case I am looking for UPC barcodes. The e.preventDefault() prevents the barcode data from being inserted into a form input:

document.addEventListener('textInput', function (e){
    if(e.data.length >= 6){
        console.log('IR scan textInput', e.data);
        e.preventDefault();
    }
});

I have tested this on Android 4.4 and 7.0 with a CipherLab IR scanner.

Example for listening to the keydown event. In my case I am able to assume that as long as a form input is not focused, the user is scanning a barcode.

    let UPC = '';
    document.addEventListener("keydown", function(e) {
        const textInput = e.key || String.fromCharCode(e.keyCode);
        const targetName = e.target.localName;
        let newUPC = '';
        if (textInput && textInput.length === 1 && targetName !== 'input'){
            newUPC = UPC+textInput;
      
          if (newUPC.length >= 6) {
            console.log('barcode scanned:  ', newUPC);
          } 
       }
    });

Of course, rather than checking the length of the string to determine a scan, you can listen for the e.keyCode === 13 in the keydown event listener.

Not all IR scanners will trigger the textInput event. If your device does not, then you can check to see if it is emitting something similar with:

monitorEvents(document.body);

Found this monitoring trick here: https://stackoverflow.com/questions/7439570/how-do-you-log-all-events-fired-by-an-element-in-jquery

Solution 3 - Javascript

I'm little late but I made this work around based in some answers here.

let code = "";
let reading = false;

document.addEventListener('keypress', e => {
  //usually scanners throw an 'Enter' key at the end of read
   if (e.keyCode === 13) {
          if(code.length > 10) {
            console.log(code);
            /// code ready to use                
            code = "";
         }
    } else {
        code += e.key; //while this is not an 'enter' it stores the every key            
    }

    //run a timeout of 200ms at the first read and clear everything
    if(!reading) {
        reading = true;
        setTimeout(() => {
            code = "";
            reading = false;
        }, 200);  //200 works fine for me but you can adjust it
    }
});

Solution 4 - Javascript

> A barcode-scanner works almost like a keyboard.

It depends on the model. Every one that I've used works exactly like a keyboard (at least as far as the computer is concerned)

> It outputs the scanned/translated (barcode->number) data raw (right?).

It outputs keycodes.

> $(document).on("scanButtonDown"

You probably want keypress, not scanButtonDown.

Look at the event object to determine the "key" that was pressed.

To determine when the entire code has been scanned, you might get an "end of data" key (possibly a space or a return) or you might have to just count how many characters are being input.

Solution 5 - Javascript

Here is working fine.
It's working when input has focus and input hasn't focus

on_scanner() // init function

function on_scanner() {
    let is_event = false; // for check just one event declaration
    let input = document.getElementById("scanner");
    input.addEventListener("focus", function () {
        if (!is_event) {
            is_event = true;
            input.addEventListener("keypress", function (e) {
                setTimeout(function () {
                    if (e.keyCode == 13) {
                        scanner(input.value); // use value as you need
                        input.select();
                    }
                }, 500)
            })
        }
    });
    document.addEventListener("keypress", function (e) {
        if (e.target.tagName !== "INPUT") {
            input.focus();
        }
    });
}

function scanner(value) {
    if (value == '') return;
    console.log(value)
}

HTML

<input type="text" id="scanner" placeholder="scanner">

Solution 6 - Javascript

Tried all the solutions, but not worked as expected. I found very easiest solution onscan.js I have application using angular 8.

Very simple and good implementation.

For angular 8, I followed steps:

1.npm install onscan.js --save

2.open angular.json, add one entry to script array as "node_modules/onscan.js/onscan.min.js"

3.In component class, implement interface AfterViewInit

declare var onscan:any;
  ngAfterViewInit(): void {

    //Put focus to textbox and press scanner button
    onScan.attachTo(document, {
      suffixKeyCodes: [13], // enter-key expected at the end of a scan
      reactToPaste: true, // Compatibility to built-in scanners in paste-mode (as opposed to keyboard-mode)
      onScan: function (sCode, iQty) { // Alternative to document.addEventListener('scan')
        console.log('Scanned: ' + iQty + 'x ' + sCode);
      },
    });
  }

Best thing is scanned text appears into focued textbox element

Hope this help.

Solution 7 - Javascript

This is an extension to the answer given by Hanz Herdel incase you are using one of the PosX scanners or any other scanner that are capable of adding a special symbol to the beginning of the characters. In this case, the tilde (~) symbol:

let barcode = "";
let reading = false;

document.addEventListener("keydown", e => {
	//console.log(e.key);
	
	if (e.key == 'Enter') {
		if (barcode.length == 17) {
			if (barcode.charAt(0) == '~') {
				
				console.log(barcode);
				barcode = "";
			}
		}
	}
	else {
		if (e.key != 'Shift') {
			barcode += e.key;
		}
	}
	
	if (!reading) {
		reading = true;
		setTimeout( () => {
			barcode = "";
			reading = false;
		}, 200);
	}
}, true)

You can change the barcode length and the timeout speed to your liking but this worked perfect for me.

Solution 8 - Javascript

I wanted to share this topic using React too, as I struggled a lot with it. I think most of the barcode scanners, such as Hanz Herdel said, terminate with ENTER. In my case, I found easier to wrap the input in a form and catch the submission event, prevent default and retrieve the value of the input.
I preferred this type of approach so to handle any type of barcode length, instead to check the length of it.

Here's how I handled it in React:

import { useState } from "react";

export default function Modal() {
const [repairArticles, setRepairArticles] = useState([]);

function handleBarcodeInput(e) {
  e.preventDefault();
  const input = e.target.querySelector("input");
  const value = input.value;
  setRepairArticles((prev) => {
    return (prev = [...prev, value]);
  });
  input.value = "";
}

return (
  <div>
    <form onSubmit={(e) => handleBarcodeInput(e)} >
      <input id="barcode-input" />
      <button type="submit" className="hidden" />
    </form>
    <div className="mt-3">
      {repairArticles.map((el, index) => {
        return <p key={index}>{el}</p>;
      })}
    </div>
  </div>    
 )           
}

Solution 9 - Javascript

I've just started working on a plugin that handles barcode scanning and credit card scanning (built on jQuery):

https://github.com/ericuldall/jquery-pos

Simple implementation:

$(function(){
    $(document).pos();
    $(document).on('scan.pos.barcode', function(event){
        var barcode = event.code;
        //handle your code here....
    });
});

So far this plugin is only tested with one type of scanner and codes containing only digits, but if you have further requirements that aren't working with it, I'd be happy to adapt it to your needs. Please check out the github page and give it a whirl. Contributions are encouraged.

E

Solution 10 - Javascript

var txt = "";
function selectBarcode() {
    if (txt != $("#focus").val()) {
        setTimeout('use_rfid()', 1000);
        txt = $("#focus").val();
    }
    $("#focus").select();
    setTimeout('selectBarcode()', 1000);
}

$(document).ready(function () {
    setTimeout(selectBarcode(),1000);       
});

<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> 

<input type="text" name="tag" id="focus" placeholder="Use handheld RFID scanner">

Solution 11 - Javascript

Needs hardening but this routine, which counts on scanned data being sent in under 100ms, is working in production. Thanks to @jfbloom22 and other answers for inspiration and reminding me of monitorEvents.

It appears scanners need to be set to send "HID Keyboard"-type data(?) and be set to terminate with "Enter".

Although is purely JavaScript logic, was written in TypeScript app for a PCF (Power Apps Component Framework) project that allows the app to accept scan data without the need to focus on an input box. Globals were used as a convenience.

public Scan(evt: Event): void {
	
	const e:KeyboardEvent = evt as KeyboardEvent;
	const timeDiff = e.timeStamp - CheckInPCF.LastTimeStamp;
	CheckInPCF.LastTimeStamp = e.timeStamp; //"global"

	//console.log(e.key + ': ' + timeDiff);

	if (timeDiff < 100) {

		if (e.key == 'Enter') {

            //Assemble complete scan text
			CheckInPCF.ScanText = CheckInPCF.FirstCharacterCandidate + CheckInPCF.ScanText; //.replace('\u000D','');

			//console.log('finished: ' + CheckInPCF.ScanText);
			
			CheckInPCF._this._notifyOutputChanged(); //Power Apps related
			
		}
		else {
			CheckInPCF.ScanText += e.key;
		}
	}
	else {
		CheckInPCF.ScanText = '';
		CheckInPCF.FirstCharacterCandidate = e.key;
	}
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionMr. B.View Question on Stackoverflow
Solution 1 - JavascriptEnricoView Answer on Stackoverflow
Solution 2 - Javascriptjfbloom22View Answer on Stackoverflow
Solution 3 - JavascriptHanz HerdelView Answer on Stackoverflow
Solution 4 - JavascriptQuentinView Answer on Stackoverflow
Solution 5 - JavascriptInfomasterView Answer on Stackoverflow
Solution 6 - JavascriptrohitView Answer on Stackoverflow
Solution 7 - JavascriptDBeckView Answer on Stackoverflow
Solution 8 - JavascriptMarco ScapinView Answer on Stackoverflow
Solution 9 - JavascriptEric UldallView Answer on Stackoverflow
Solution 10 - JavascriptboatengView Answer on Stackoverflow
Solution 11 - JavascriptRodneyView Answer on Stackoverflow