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

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?
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.

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.

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: How do you log all events fired by an element in jQuery?

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
}
});

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.

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">

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.

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>
)
}

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.

Vue 2 implementation (i think vuejs's syntax is similar to angular):
BarcodeScanner.vue component code is here:
<template>
<input type="hidden" name="_barcode" v-model="finalCode" />
</template>
<script>
export default {
props: {
onSuccess: {
type: Function,
required: true
},
minLength: {
type: Number,
default: () => 10
}
},
data() {
return {
code: "",
finalCode: "",
fromScanner: false,
reading: false
};
},
mounted() {
document.addEventListener("keypress", this.documentKeyboardListener);
},
destroyed() {
document.removeEventListener("keypress", this.documentKeyboardListener);
},
methods: {
documentKeyboardListener(e) {
if (e.target.nodeName !== 'BODY') return;
if (e.code === "Enter") {
if (this.reading && this.code.length > this.minLength) {
if (this.onSuccess)
this.onSuccess(this.code);
this.finalCode = this.code;
this.code = "";
this.fromScanner = true;
}
} else {
this.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 (!this.reading) {
this.reading = true;
setTimeout(() => {
this.code = "";
this.reading = false;
this.fromScanner = false;
}, 200); //200 works fine for me but you can adjust it
}
},
},
};
</script>
You can invoke the component anywhere:
...
<barcode-scanner onSuccess="yourListener"/>
...
(Js scanner code is taken from Hanz Herdel)

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

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">

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;
}
}

This code works fine for me, you can try it
var barcode = '';
var interval;
document.addEventListener('keydown', function(evt) {
if (evt.code === 'F12'){
evt.preventDefault();
}
if (interval){
clearInterval(interval);
}
if (evt.code == 'Enter') {
if (barcode){
$('#barcode').val(barcode);
console.log(barcode);
}
barcode = '';
return;
}
if (evt.key != 'Shift'){
barcode += evt.key;
}
interval = setInterval(() => barcode = '', 20);
});

Related

Keyup event not firing when meta key is held (Mac OSX only?) - How to handle in javascript?

Seems like others have ran into this issue outside of the browser. I'm trying to figure out how to deal with it in my javascript code.
When I hold command (on mac) and press and let go of another key, (such as if I were to enter command-c to copy some text), the keyup event does not fire unless I let go of command first.
This affects me because my web app responds to the alt-x key combination to toggle a menu, and this is important. I'm using keyup and keydown events to track the state of which keys are pressed at any given time, so that I can toggle the menu when the user enters alt-x (and not some other combination including alt-x). It works great until I enter command-[some key] and then my program thinks I'm entering [some key]-alt-x when I enter alt-x to toggle the menu.
Does anyone have any ideas for how to deal with this situation?
I made a code pen to illustrate the problem.
const pressed_keys = {};
const keydown_handler = e => {
pressed_keys[e.code] = true;
if (has_entered_alt_x(e)) {
document.querySelector('div').classList.toggle('blue');
}
};
const keyup_handler = e => pressed_keys[e.code] = false;
const has_entered_alt_x = e => {
if (!(e.altKey && e.code == 'KeyX')) return false;
for (const key in pressed_keys) {
if (Object.hasOwnProperty.call(pressed_keys, key)) {
if (!['AltLeft', 'AltRight', 'KeyX', 'CapsLock'].includes(key) &&
pressed_keys[key]) {
return false;
}
}
}
return true;
}
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('keydown', keydown_handler);
document.addEventListener('keyup', keyup_handler);
});
div {
width: 40px;
height: 40px;
background-color: red;
}
.blue {
background-color: blue;
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8"/>
</head>
<body>
<div></div>
</body>
</html>
Here's a decent work around I came up with, and you can try it in this code pen.
const pressed_keys = {};
const keyup_handler = e => {
pressed_keys[e.code] = false;
// In macos the keyup event doesn't fire when the Meta key is held down.
// So, when Meta key is lifted, assume other keys have also been lifted
// as well. There is a bug introduced: If the user is quick, they can
// hit meta-c, lift the meta key and then hit alt-x very quickly
// thereafter, and in this case they will be able to toggle the change
// while holding down some other key. This is unlikely to occur and in
// the event that it does, it doesn't cause problems for the application.
if (e.key == 'Meta') {
Object.keys(pressed_keys).forEach(key => { pressed_keys[key] = false; });
}
}
const keydown_handler = e => {
// In macos the keyup doesn't fire on the Meta key (command) after
// certain key combinations in the browser, such as
// M-Q : shutdown computer
// M-p : print page
// M-o : opens files
// M-t : new tab
// M-y : opens history
// and possibly others. So, to be able to detect when this has happened,
// on keydown ask if the meta key is pressed using e.meta and query
// pressed_keys to see if the key up event has fired. If meta isn't
// pressed by the keyup event hasn't fired set all pressed_keys to false.
if (!e.metaKey && (pressed_keys['MetaLeft'] || pressed_keys['MetaRight'])) {
Object.keys(pressed_keys).forEach(key => { pressed_keys[key] = false; });
}
pressed_keys[e.code] = true;
if (has_entered_alt_x(e)) {
document.querySelector('div').classList.toggle('blue');
}
};
const has_entered_alt_x = e => {
if (!(e.altKey && e.code == 'KeyX')) return false;
for (const key in pressed_keys) {
if (Object.hasOwnProperty.call(pressed_keys, key)) {
if (!['AltLeft', 'AltRight', 'KeyX', 'CapsLock'].includes(key) &&
pressed_keys[key]) {
return false;
}
}
}
return true;
}
document.addEventListener('DOMContentLoaded', () => {
document.addEventListener('keydown', keydown_handler);
document.addEventListener('keyup', keyup_handler);
});

DOM Reloads on Barcode Scanner Input

I have an application which works with a barcode scanner. The barcode scanner communicates with the computer in a way an external keyboard would, so I've put a keydown event listener on the DOM to capture input from the barcode scanner and mark a corresponding item in the dom. Now my problem is, anytime a barcode is read in, the dom seems to refresh and clear all selection. Can someone help? This is my code. All of this is been done in electron by the way
document.addEventListener("keydown", (e) => {
if (e.key === "" || e.key === "Control") {
return
}
buffer.push(e.key);
setTimeout(() => {
if (buffer.length > 0) {
buffer.forEach((char) => {
string = string + char
})
if (buffer.length >= 12) {
const tableRows = document.querySelector("tbody").querySelectorAll("tr");
tableRows.forEach((row) => {
const barcode = row.querySelector(".td_Barcode--hidden").innerText;
if (barcode === string) {
toggleRowCB(row)
}
})
} else {
console.log(buffer.length);
}
}
buffer = [];
string = "";
}, 500)
})
Code seems alright, but can you try adding
e.preventDefault();
for submitting. I think when you scan barcode with barcode scanner it mimics with reading + "Enter" which bypass your e.key conditions.
Package suggestion for electron keyboard event handling;
Electron-localshortcut

How to adapt Event DOM Listener to Google Maps JavaScript API v3.35

I used to use the following code to catch users' Enter key event and automatically pick the first result from the Autocomplete results (pac-items) if users haven't selected any of them (i.e., there is no pac-item marked as pac-item-selected).
var input = document.getElementById('pac-input')
var autocomplete = new google.maps.places.Autocomplete(input)
google.maps.event.addDomListener(input, 'keypress', function(e) {
if (e.keyCode===13 && !e.triggered) {
var suggestion_selected = document.querySelectorAll('.pac-item-selected').length > 0
if (!isLatLngInInputBox && !suggestion_selected) {
google.maps.event.trigger(this,'keydown',{keyCode:40})
google.maps.event.trigger(this,'keydown',{keyCode:13,triggered:true})
}
}
})
However, started from Google Maps JavaScript API v3.35, I would get an error like Uncaught TypeError: a.stopPropagation is not a function raised in the line of google.maps.event.trigger(this,'keydown',{keyCode:40}).
As a result, I checked the documentation and noticed that trigger method of google.maps.event has changed. The third argument is now stated as eventArgs instead of var_args.
I tried to figure out how to adapt to it and modified the code like:
google.maps.event.addDomListener(input, 'keypress', function(e) {
console.log(e.key)
if (e.key==="Enter" && !e.triggered) {
var suggestion_selected = document.querySelectorAll('.pac-item-selected').length > 0
if (!isLatLngInInputBox && !suggestion_selected) {
google.maps.event.trigger(this,'keydown',{key:"ArrowDown"})
google.maps.event.trigger(this,'keydown',{key:"Enter",triggered:true})
}
}
})
Although the Enter key can be captured perfectly, the ArrowDown key does not work as intended.
In fact, I can capture the ArrowDown key with console.log(e.key), but nothing really happens. Also, console.log(e.key) does not catch anything when I press the ArrowDown key on my keyboard, which makes me so confused.
Does anyone encounter similar problem? Thanks for any suggestion!
Use new Event() to create proper event object as third parameter and also keydown instead of keypress in your addDomListener
var input = document.getElementById('pac-input')
var autocomplete = new google.maps.places.Autocomplete(input)
google.maps.event.addDomListener(input, 'keydown', function(e) {
var suggestion_selected = document.querySelectorAll('.pac-item-selected').length > 0
if (suggestion_selected) {
console.log(document.querySelectorAll('.pac-item-selected'));
} else {
if (e.key === "Enter" && !e.triggered) {
var ex1 = new Event('keydown');
ex1.code = "ArrowDown";
ex1.key = "ArrowDown";
ex1.keyCode = 40;
google.maps.event.trigger(this, 'keydown', ex1);
var ex2 = new Event('keydown');
ex2.code = "Enter";
ex2.key = "Enter";
ex2.keyCode = 13;
ex2.triggered = true;
google.maps.event.trigger(this, 'keydown', ex2);
}
}
});
Edit:
I have a working fiddle here https://jsfiddle.net/ergec/e6wsdb85/

How do you do key event handling in React.js?

I'm working on one of my first projects in React.js and have been having some trouble with key event handling. It seems that I'm following examples I've found online pretty precisely. Could some take a look and tell me what they see wrong?
var ScreenplayElement = React.createClass({
handleKeyPress: function(e) {
if (e.keyCode == 13) {
e.preventDefault();
console.log("New action to be created");
}
},
render: function () {
var classString = this.props.type + " screenplay-el";
return (
<p className={classString} contentEditable onKeyPress={this.handleKeyPress}><br /></p>
);
}
});
Try using KeyboardEvent.key (e.key) instead. See the Mozilla Docs.
var ScreenplayElement = React.createClass({
handleKeyPress: function(e) {
if (e.key === "Enter") {
e.preventDefault();
console.log("New action to be created");
}
},
render: function () {
var classString = this.props.type + " screenplay-el";
return (
<p className={classString} contentEditable onKeyPress={this.handleKeyPress}><br />Content goes in here</p>
);
}
});
According to MDN: KeyboardEvent.keyCode is depreceated.
This feature has been removed from the Web standards. Though some browsers may still support it, it is in the process of being dropped. Do not use it in old or new projects. Pages or Web apps using it may break at any time.
Tip: if you place a console.log() message directly before the if statement, it will fire immediately. This way you can determine whether an issue is with the event handler or the if statement :-)

ExtJs simulate TAB on ENTER keypress

I know it is not the smartest idea, but I still have to do it.
Our users want to use ENTER like TAB.
So, the best I came up with is this:
Ext.override(Ext.form.field.Base, {
initComponent: function() {
this.callParent(arguments);
this.on('afterrender', function() {
var me=this;
this.getEl().on('keypress',function (e){
if(e.getKey() == 13) {
me.nextNode().focus();
}
});
});
}
});
But it still does not work exactly the same way as TAB.
I mean, it works OK with input fields, but not other controls.
May be there is some low-level solution.
Any ideas?
In the past I've attached the listener to the document, something like this:
Ext.getDoc().on('keypress', function(event, target) {
// get the form field component
var targetEl = Ext.get(target.id),
fieldEl = targetEl.up('[class*=x-field]') || {},
field = Ext.getCmp(fieldEl.id);
if (
// the ENTER key was pressed...
event.ENTER == event.getKey() &&
// from a form field...
field &&
// which has valid data.
field.isValid()
) {
// get the next form field
var next = field.next('[isFormField]');
// focus the next field if it exists
if (next) {
event.stopEvent();
next.focus();
}
}
});
For Ext.form.field.Text and similar xtypes there is a extra config enableKeyEvents that needs to be set before the keypress/keydown/keyup events fire.
The enableKeyEvents config option needs to be set to true as it's default to false.
ExtJS API Doc
Disclaimer: I'm not an expert on ExtJs.
That said, maybe try something like:
if (e.getKey() === 13) {
me.blur();
return false; // cancel key event to prevent the [Enter] behavior
}
You could try this
if (e.getKey() === 13) {
e.keyCode = Ext.EventObject.TAB
this.fireEvent(e, {// any additional options
});
}
Haven't really tried this ever myself.

Categories