35

How I can programmatically detect when text input filled by typing on keyboard and when it filled automatically by bar-code scanner?

Ilya
  • 27,538
  • 18
  • 104
  • 148
MyTitle
  • 17,348
  • 50
  • 147
  • 265

12 Answers12

37

I wrote this answer, because my Barcode Scanner Motorola LS1203 generated keypress event, so I can't use Utkanos's solution.

My solution is:

var BarcodeScanerEvents = function() {
     this.initialize.apply(this, arguments);
};

BarcodeScanerEvents.prototype = {
    initialize: function() {
       $(document).on({
          keyup: $.proxy(this._keyup, this)
       });
    },
    _timeoutHandler: 0,
    _inputString: '',
    _keyup: function (e) {
        if (this._timeoutHandler) {
            clearTimeout(this._timeoutHandler);
            this._inputString += String.fromCharCode(e.which);
        } 

        this._timeoutHandler = setTimeout($.proxy(function () {
            if (this._inputString.length <= 3) {
                this._inputString = '';
                return;
            }

            $(document).trigger('onbarcodescaned', this._inputString);

            this._inputString = '';

        }, this), 20);
    }
};
isapir
  • 14,475
  • 6
  • 82
  • 97
Vitall
  • 1,101
  • 1
  • 12
  • 19
  • Vitall, I am not a JavaScript guy. I will be extremely grateful to know how this code works and how can I use it in my own code. I want to put an event listener for the $(document) element to catch anytime a barcode is scanned. – Ahmad Karimi Oct 22 '19 at 18:03
  • @AhmadKarimi, for add event listener you just need to use jquery on method, something like `$(document).on( "onbarcodescaned", function(inputString) { // your code here in inputString should be barcode result });` – Vitall Oct 22 '19 at 18:49
  • 1
    @AhmadKarimi also my answer is very old, try to use adapted version of code https://stackoverflow.com/a/56540835/527925 – Vitall Oct 22 '19 at 18:54
11

Well a barcode won't fire any key events so you could do something like:

$('#my_field').on({
    keypress: function() { typed_into = true; },
    change: function() {
        if (typed_into) {
            alert('type');
            typed_into = false; //reset type listener
        } else {
            alert('not type');
        }
    }
});

Depending on when you want to evaluate this, you may want to do this check not on change but on submit, or whatever.

Mitya
  • 30,438
  • 7
  • 46
  • 79
  • thanks. But how I can detect when barcode were input? `onCange` event rises only when element lost focus, but I need to know inserted value immediately after inserting. I can to make timer and periodically check value on text field, but I think it's wrong solution. – MyTitle Jul 02 '12 at 14:35
  • You didn't mention in your question when precisely you wanted to check input, so I assumed onChange. Yes, I think a timer is probably the only way there, unless the barcode application fires some sort of custom event when it manipulates the page. – Mitya Jul 02 '12 at 14:49
  • I tried in Android mobile bar code scanning, it is taking key press event only instead bar code scan? How this is working for you people. – Sharad Oct 18 '19 at 12:09
10

you can try following example, using jQuery plugin https://plugins.jquery.com/scannerdetection/

Its highly configurable, time based scanner detector. It can be used as solution for prefix/postfix based, time based barcode scanner.

Tutorial for usage and best practices, as well discussed about various Barcode Scanner Models and how to deal with it. http://a.kabachnik.info/jquery-scannerdetection-tutorial.html

$(window).ready(function(){

 //$("#bCode").scannerDetection();

 console.log('all is well');
 
 $(window).scannerDetection();
 $(window).bind('scannerDetectionComplete',function(e,data){
            console.log('complete '+data.string);
            $("#bCode").val(data.string);
        })
        .bind('scannerDetectionError',function(e,data){
            console.log('detection error '+data.string);
        })
        .bind('scannerDetectionReceive',function(e,data){
            console.log('Recieve');
            console.log(data.evt.which);
        })

        //$(window).scannerDetection('success');
<input id='bCode'type='text' value='barcode appears here'/>
Manos Nikolaidis
  • 18,967
  • 11
  • 60
  • 72
dextermini
  • 316
  • 2
  • 12
9

Adapted the super useful Vitall answer above to utilize an IIFE instead of prototyping, in case anyone just seeing this now is into that.

This also uses the 'keypress' event instead of keyup, which allowed me to reliably use KeyboardEvent.key, since KeyboardEvent.which is deprecated now. I found this to work for barcode scanning as well as magnetic-strip card swipes.

In my experience, handling card swipes with keyup caused me to do extra work handling 'Shift' keycodes e.g. a Shift code would be followed by the code representing '/', with the intended character being '?'. Using 'keypress' solved this as well.

(function($) {
    var _timeoutHandler = 0,
        _inputString = '',
        _onKeypress = function(e) {
            if (_timeoutHandler) {
                clearTimeout(_timeoutHandler);
            }
            _inputString += e.key;

            _timeoutHandler = setTimeout(function () {
                if (_inputString.length <= 3) {
                    _inputString = '';
                    return;
                }
                $(e.target).trigger('altdeviceinput', _inputString);
                _inputString = '';

            }, 20);
        };
    $(document).on({
        keypress: _onKeypress
    });
})($);
Spence7
  • 91
  • 1
  • 3
  • 2
    Wanted to point out, this solution also fixes the first character missing bug that the answer from @Vitall had. – chadiusvt Aug 15 '18 at 15:58
  • 1
    Also, `$(document).trigger(...` should be changed to `$(e.target).trigger(...` so that the event registers with the desired DOM object. – chadiusvt Aug 15 '18 at 16:07
  • 1
    Thanks. Also, In my own usage, I noticed that some browsers (IE, Firefox) gave the value of `e.key` for keyboard keys like backspace to `"BACKSPACE"` and trigger the event. I fixed this by adding `if(e.key.length == 1) _inputString += e.key;` – chadiusvt Aug 24 '18 at 14:24
  • If your barcode scanner is slow (our bluetooth scanners) you may need to increase the timeout from 20 to a higher number. We changed our timeout to 200, to give the scanner more time to type the barcode in. – Baxny Dec 19 '19 at 01:32
  • We also added a check to not type in special characters because when we were manually deleting the barcode out of the field, the script was typing the characters // Stop special keys inserting words e.g. backspace if (e.key.length == 1) { _inputString += e.key; } – Baxny Dec 19 '19 at 01:33
  • 2
    This worked for me. To get the barcode-value, I had to subscribe to the trigger like this: `$(document).on("altdeviceinput", function (e, barCode) { ...` – Jeppe Sep 07 '20 at 05:33
  • @jeppe thx to pointing on that. @spence7 could you please add some comments on your solution, it works well but I'm a bit confused with that `timeoutHandler` is that the timeout between keypresses? – khashashin Apr 13 '21 at 01:12
  • 1
    @khashashin The basic algorithm is: on the keypress event, cancel the setTimeout call if one is waiting to execute, add the pressed key value to an input buffer, then call setTimeout. If another keypress is not captured in 20ms, the function passed to setTimeout will be called. Said function will trigger an event IF the input string is at least 3 characters long (this combined with the short timeout was enough to indicate a scan). Either way the input buffer is flushed. – Spence7 Apr 14 '21 at 19:52
7

For ES6 2019 version of Vitall answer.

const events = mitt()

class BarcodeScaner {
  initialize = () => {
    document.addEventListener('keypress', this.keyup)
    if (this.timeoutHandler) {
      clearTimeout(this.timeoutHandler)
    }
    this.timeoutHandler = setTimeout(() => {
      this.inputString = ''
    }, 10)
  }

  close = () => {
    document.removeEventListener('keypress', this.keyup)
  }

  timeoutHandler = 0

  inputString = ''

  keyup = (e) => {
    if (this.timeoutHandler) {
      clearTimeout(this.timeoutHandler)
      this.inputString += String.fromCharCode(e.keyCode)
    }

    this.timeoutHandler = setTimeout(() => {
      if (this.inputString.length <= 3) {
        this.inputString = ''
        return
      }
      events.emit('onbarcodescaned', this.inputString)

      this.inputString = ''
    }, 10)
  }
}

Can be used with react hooks like so:

const ScanComponent = (props) => {
  const [scanned, setScanned] = useState('')
  useEffect(() => {
    const barcode = new BarcodeScaner()
    barcode.initialize()
    return () => {
      barcode.close()
    }
  }, [])

  useEffect(() => {
    const scanHandler = code => {
      console.log(code)
      setScanned(code)
    }

    events.on('onbarcodescaned', scanHandler)
    return () => {
      events.off('onbarcodescaned', scanHandler)
    }
  }, [/* here put dependencies for your scanHandler ;) */])
  return <div>{scanned}</div>
}

I use mitt from npm for events, but you can use whatever you prefer ;)

Tested on Zebra DS4208

MisieQQQ
  • 71
  • 1
  • 2
3

The solution from Vitall only works fine if you already hit at least one key. If you don't the first character will be ignored (if(this._timeoutHandler) returns false and the char will not be appended).

If you want to begin scanning immediately you can use the following code:

var BarcodeScanerEvents = function() {
 this.initialize.apply(this, arguments);
};

BarcodeScanerEvents.prototype = {
 initialize : function() {
  $(document).on({
   keyup : $.proxy(this._keyup, this)
  });
 },
 _timeoutHandler : 0,
 _inputString : '',
 _keyup : function(e) {
  if (this._timeoutHandler) {
   clearTimeout(this._timeoutHandler);
  }
  this._inputString += String.fromCharCode(e.which);

  this._timeoutHandler = setTimeout($.proxy(function() {
   if (this._inputString.length <= 3) {
    this._inputString = '';
    return;
   }

   $(document).trigger('onbarcodescaned', this._inputString);

   this._inputString = '';

  }, this), 20);
 }
};
Mhastrich
  • 111
  • 1
  • 7
2

If you can set a prefix to your barcode scanner I suggests this (I changed a bit the Vitall code):

var BarcodeScanner = function(options) {
     this.initialize.call(this, options);
};
BarcodeScanner.prototype = {
    initialize: function(options) {
       $.extend(this._options,options);
       if(this._options.debug) console.log("BarcodeScanner: Initializing");
       $(this._options.eventObj).on({
          keydown: $.proxy(this._keydown, this),
       });
    },
    destroy: function() {
        $(this._options.eventObj).off("keyup",null,this._keyup);
        $(this._options.eventObj).off("keydown",null,this._keydown);
    },
    fire: function(str){
        if(this._options.debug) console.log("BarcodeScanner: Firing barcode event with string: "+str);
        $(this._options.fireObj).trigger('barcode',[str]);
    },
    isReading: function(){
        return this._isReading;
    },
    checkEvent: function(e){
        return this._isReading || (this._options.isShiftPrefix?e.shiftKey:!e.shiftKey) && e.which==this._options.prefixCode;
    },
    _options: {timeout: 600, prefixCode: 36, suffixCode: 13, minCode: 32, maxCode: 126, isShiftPrefix: false, debug: false, eventObj: document, fireObj: document},
    _isReading: false,
    _timeoutHandler: false,
    _inputString: '',
    _keydown: function (e) {
        if(this._input.call(this,e))
            return false;
    },
    _input: function (e) {
        if(this._isReading){
            if(e.which==this._options.suffixCode){
                //read end
                if(this._options.debug) console.log("BarcodeScanner: Read END");
                if (this._timeoutHandler) 
                    clearTimeout(this._timeoutHandler);
                this._isReading=false;
                this.fire.call(this,this._inputString);
                this._inputString='';
            }else{
                //char reading
                if(this._options.debug) console.log("BarcodeScanner: Char reading "+(e.which));
                if(e.which>=this._options.minCode && e.which<=this._options.maxCode)
                    this._inputString += String.fromCharCode(e.which);
            }
            return true;
        }else{
            if((this._options.isShiftPrefix?e.shiftKey:!e.shiftKey) && e.which==this._options.prefixCode){
                //start reading
                if(this._options.debug) console.log("BarcodeScanner: Start reading");
                this._isReading=true;
                this._timeoutHandler = setTimeout($.proxy(function () {
                    //read timeout
                    if(this._options.debug) console.log("BarcodeScanner: Read timeout");
                    this._inputString='';
                    this._isReading=false;
                    this._timeoutHandler=false;
                }, this), this._options.timeout);
                return true;
            }
        }
        return false;
    }
};

If you need you customize timeout, suffix, prefix, min/max ascii code readed:

new BarcodeScanner({timeout: 600, prefixKeyCode: 36, suffixKeyCode: 13, minKeyCode: 32, maxKeyCode: 126});

I also added the isShiftPrefix option to use for example the $ char as prefix with these options: new BarcodeScanner({prefixKeyCode: 52, isShiftPrefix: true});

This is a fiddle: https://jsfiddle.net/xmt76ca5/

Tobia
  • 8,015
  • 24
  • 90
  • 182
  • This is not working if focus is on input field, how can I avoid rfid typing it to input field and should only be detected in above code? – Aadam Sep 08 '18 at 07:42
  • I changed a bit the code moving the event from keyup to keydown and now you can avoid typing in the input field during barcode reading. Please try the fiddle. – Tobia Oct 01 '18 at 06:35
1

You can use a "onkeyup" event on that input box. If the event has triggered then you can called it "Input from Keyboard".

Sudip Pal
  • 1,947
  • 1
  • 10
  • 16
0

$(window).ready(function(){

 //$("#bCode").scannerDetection();

 console.log('all is well');
 
 $(window).scannerDetection();
 $(window).bind('scannerDetectionComplete',function(e,data){
            console.log('complete '+data.string);
            $("#bCode").val(data.string);
        })
        .bind('scannerDetectionError',function(e,data){
            console.log('detection error '+data.string);
        })
        .bind('scannerDetectionReceive',function(e,data){
            console.log('Recieve');
            console.log(data.evt.which);
        })

        //$(window).scannerDetection('success');
<input id='bCode'type='text' value='barcode appears here'/>
  • From Review: Welcome to Stack Overflow! Please don't answer just with source code. Try to provide a nice description about how your solution works. See: [How do I write a good answer?](https://stackoverflow.com/help/how-to-answer). Thanks – sɐunıɔןɐqɐp Oct 09 '18 at 06:41
0

Hi I have and alternative solution for evaluate a result of the bar code scanner without use of jQuery, first you need and input text that have a focus the moment that the barcode scanner is works

<input id="input_resultado" type="text" />

The code in JavaScript is:

var elmInputScan = document.getElementById('input_resultado');           

elmInputScan.addEventListener('keypress', function (e){        
  clearInterval( timer_response );    
  timer_response = setTimeout( "onInputChange()", 10);
});    

When the barcode scanner input the text call serveral times to the keypress event, but only I interested to the final result, for this reason I use the timer. That's all, you can process the value into the onInputChange function.

function onInputChange() {    
  console.log( document.getElementById('input_resultado').value );
}
Juan Carlos
  • 83
  • 2
  • 6
0
document.addEventListener("keypress", function (e) {
  if (e.target.tagName !== "INPUT") {
    // it's your scanner
  }
});
levi
  • 675
  • 2
  • 8
  • 18
  • Just to clarify, this method works when the focus is not on an text input form element. If the focus IS on an input element, then we need to consider other factors as mentioned elsewhere in this Q&A. – Jonathan B. Jan 27 '21 at 15:16
-1

I highly recommend this js plugin https://github.com/axenox/onscan.js It's easy to use and has tones of options to meet your need.

ruleboy21
  • 1
  • 1
  • 1
  • you do not mention how that plugin will help in "Detecting when input box filled by keyboard and when by barcode scanner." – Joel Oct 26 '20 at 18:08