Detect when input box filled by keyboard and when by barcode scanner. - javascript

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

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

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

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.

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'/>

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

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

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/

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

$(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'/>

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

document.addEventListener("keypress", function (e) {
if (e.target.tagName !== "INPUT") {
// it's your scanner
}
});

None of the solutions worked for me because I don't want to focus on an input. I want the result page(item details page) to keep listening for the scanner to scan next item. My scanner fires the keypress event so this worked like a charm for me.
var inputTemp = '';
var inputTempInterval = setInterval(function() {
// change 5 as minimum length of the scan code
if (inputTemp.length >= 5) {
var detected = inputTemp;
inputTemp = '';
clearInterval(inputTempInterval); // stop listening if you don't need anymore
onScannerTrigger(detected);
} else {
inputTemp = '';
}
}, 100);
$(window).keypress(function(e){
inputTemp += String.fromCharCode(e.which);
});
function onScannerTrigger(scannedCode) {
console.log(scannedCode);
// do your stuff
}

I have published a lightweight JS package which doesn't rely on jQuery or input fields. It simple looks at the timing of the keyPress-events to determine wether it was a barcode scanner or regular input.
https://www.npmjs.com/package/#itexperts/barcode-scanner
import {BarcodeScanner} from "#itexperts/barcode-scanner";
let options = {
timeOut: 130,
characterCount: 13
}
let barcodeScanner = new BarcodeScanner(options);
barcodeScanner.addEventListener('scan', function(e){
let barcode = e.detail;
console.log(barcode);
});

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.
<script src="path-to-onScan.js"></script>
<script>
// Initialize with options
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);
},
onKeyDetect: function(iKeyCode){ // output all potentially relevant key events - great for debugging!
console.log('Pressed: ' + iKeyCode);
}
});
</script>

Related

Apply multi-event do same function on same element [duplicate]

So my dilemma is that I don't want to write the same code twice. Once for the click event and another for the touchstart event.
Here is the original code:
document.getElementById('first').addEventListener('touchstart', function(event) {
do_something();
});
document.getElementById('first').addEventListener('click', function(event) {
do_something();
});
How can I compact this? There HAS to be a simpler way!
I thought some might find this approach useful; it could be applied to any similarly repetitive code:
ES6
['click','ontouchstart'].forEach( evt =>
element.addEventListener(evt, dosomething, false)
);
ES5
['click','ontouchstart'].forEach( function(evt) {
element.addEventListener(evt, dosomething, false);
});
You can just define a function and pass it. Anonymous functions are not special in any way, all functions can be passed around as values.
var elem = document.getElementById('first');
elem.addEventListener('touchstart', handler, false);
elem.addEventListener('click', handler, false);
function handler(event) {
do_something();
}
Maybe you can use a helper function like this:
// events and args should be of type Array
function addMultipleListeners(element,events,handler,useCapture,args){
if (!(events instanceof Array)){
throw 'addMultipleListeners: '+
'please supply an array of eventstrings '+
'(like ["click","mouseover"])';
}
//create a wrapper to be able to use additional arguments
var handlerFn = function(e){
handler.apply(this, args && args instanceof Array ? args : []);
}
for (var i=0;i<events.length;i+=1){
element.addEventListener(events[i],handlerFn,useCapture);
}
}
function handler(e) {
// do things
};
// usage
addMultipleListeners(
document.getElementById('first'),
['touchstart','click'],
handler,
false);
[Edit nov. 2020] This answer is pretty old. The way I solve this nowadays is by using an actions object where handlers are specified per event type, a data-attribute for an element to indicate which action should be executed on it and one generic document wide handler method (so event delegation).
const firstElemHandler = (elem, evt) =>
elem.textContent = `You ${evt.type === "click" ? "clicked" : "touched"}!`;
const actions = {
click: {
firstElemHandler,
},
touchstart: {
firstElemHandler,
},
mouseover: {
firstElemHandler: elem => elem.textContent = "Now ... click me!",
outerHandling: elem => {
console.clear();
console.log(`Hi from outerHandling, handle time ${
new Date().toLocaleTimeString()}`);
},
}
};
Object.keys(actions).forEach(key => document.addEventListener(key, handle));
function handle(evt) {
const origin = evt.target.closest("[data-action]");
return origin &&
actions[evt.type] &&
actions[evt.type][origin.dataset.action] &&
actions[evt.type][origin.dataset.action](origin, evt) ||
true;
}
[data-action]:hover {
cursor: pointer;
}
<div data-action="outerHandling">
<div id="first" data-action="firstElemHandler">
<b>Hover, click or tap</b>
</div>
this is handled too (on mouse over)
</div>
For large numbers of events this might help:
var element = document.getElementById("myId");
var myEvents = "click touchstart touchend".split(" ");
var handler = function (e) {
do something
};
for (var i=0, len = myEvents.length; i < len; i++) {
element.addEventListener(myEvents[i], handler, false);
}
Update 06/2017:
Now that new language features are more widely available you could simplify adding a limited list of events that share one listener.
const element = document.querySelector("#myId");
function handleEvent(e) {
// do something
}
// I prefer string.split because it makes editing the event list slightly easier
"click touchstart touchend touchmove".split(" ")
.map(name => element.addEventListener(name, handleEvent, false));
If you want to handle lots of events and have different requirements per listener you can also pass an object which most people tend to forget.
const el = document.querySelector("#myId");
const eventHandler = {
// called for each event on this element
handleEvent(evt) {
switch (evt.type) {
case "click":
case "touchstart":
// click and touchstart share click handler
this.handleClick(e);
break;
case "touchend":
this.handleTouchend(e);
break;
default:
this.handleDefault(e);
}
},
handleClick(e) {
// do something
},
handleTouchend(e) {
// do something different
},
handleDefault(e) {
console.log("unhandled event: %s", e.type);
}
}
el.addEventListener(eventHandler);
Update 05/2019:
const el = document.querySelector("#myId");
const eventHandler = {
handlers: {
click(e) {
// do something
},
touchend(e) {
// do something different
},
default(e) {
console.log("unhandled event: %s", e.type);
}
},
// called for each event on this element
handleEvent(evt) {
switch (evt.type) {
case "click":
case "touchstart":
// click and touchstart share click handler
this.handlers.click(e);
break;
case "touchend":
this.handlers.touchend(e);
break;
default:
this.handlers.default(e);
}
}
}
Object.keys(eventHandler.handlers)
.map(eventName => el.addEventListener(eventName, eventHandler))
Unless your do_something function actually does something with any given arguments, you can just pass it as the event handler.
var first = document.getElementById('first');
first.addEventListener('touchstart', do_something, false);
first.addEventListener('click', do_something, false);
Simplest solution for me was passing the code into a separate function and then calling that function in an event listener, works like a charm.
function somefunction() { ..code goes here ..}
variable.addEventListener('keyup', function() {
somefunction(); // calling function on keyup event
})
variable.addEventListener('keydown', function() {
somefunction(); //calling function on keydown event
})
I have a small solution that attaches to the prototype
EventTarget.prototype.addEventListeners = function(type, listener, options,extra) {
let arr = type;
if(typeof type == 'string'){
let sp = type.split(/[\s,;]+/);
arr = sp;
}
for(let a of arr){
this.addEventListener(a,listener,options,extra);
}
};
Allows you to give it a string or Array. The string can be separated with a space(' '), a comma(',') OR a Semicolon(';')
I just made this function (intentionally minified):
((i,e,f)=>e.forEach(o=>i.addEventListener(o,f)))(element, events, handler)
Usage:
((i,e,f)=>e.forEach(o=>i.addEventListener(o,f)))(element, ['click', 'touchstart'], (event) => {
// function body
});
The difference compared to other approaches is that the handling function is defined only once and then passed to every addEventListener.
EDIT:
Adding a non-minified version to make it more comprehensible. The minified version was meant just to be copy-pasted and used.
((element, event_names, handler) => {
event_names.forEach( (event_name) => {
element.addEventListener(event_name, handler)
})
})(element, ['click', 'touchstart'], (event) => {
// function body
});
I'm new at JavaScript coding, so forgive me if I'm wrong.
I think you can create an object and the event handlers like this:
const myEvents = {
click: clickOnce,
dblclick: clickTwice,
};
function clickOnce() {
console.log("Once");
}
function clickTwice() {
console.log("Twice");
}
Object.keys(myEvents).forEach((key) => {
const myButton = document.querySelector(".myButton")
myButton.addEventListener(key, myEvents[key]);
});
<h1 class="myButton">Button</h1>
And then click on the element.
document.getElementById('first').addEventListener('touchstart',myFunction);
document.getElementById('first').addEventListener('click',myFunction);
function myFunction(e){
e.preventDefault();e.stopPropagation()
do_something();
}
You should be using e.stopPropagation() because if not, your function will fired twice on mobile
This is my solution in which I deal with multiple events in my workflow.
let h2 = document.querySelector("h2");
function addMultipleEvents(eventsArray, targetElem, handler) {
eventsArray.map(function(event) {
targetElem.addEventListener(event, handler, false);
}
);
}
let counter = 0;
function countP() {
counter++;
h2.innerHTML = counter;
}
// magic starts over here...
addMultipleEvents(['click', 'mouseleave', 'mouseenter'], h2, countP);
<h1>MULTI EVENTS DEMO - If you click, move away or enter the mouse on the number, it counts...</h1>
<h2 style="text-align:center; font: bold 3em comic; cursor: pointer">0</h2>
What about something like this:
['focusout','keydown'].forEach( function(evt) {
self.slave.addEventListener(evt, function(event) {
// Here `this` is for the slave, i.e. `self.slave`
if ((event.type === 'keydown' && event.which === 27) || event.type === 'focusout') {
this.style.display = 'none';
this.parentNode.querySelector('.master').style.display = '';
this.parentNode.querySelector('.master').value = this.value;
console.log('out');
}
}, false);
});
// The above is replacement of:
/* self.slave.addEventListener("focusout", function(event) { })
self.slave.addEventListener("keydown", function(event) {
if (event.which === 27) { // Esc
}
})
*/
You can simply do it iterating an Object. This can work with a single or multiple elements. This is an example:
const ELEMENTS = {'click': element1, ...};
for (const [key, value] of Object.entries(ELEMENTS)) {
value.addEventListener(key, () => {
do_something();
});
}
When key is the type of event and value is the element when you are adding the event, so you can edit ELEMENTS adding your elements and the type of event.
Semi-related, but this is for initializing one unique event listener specific per element.
You can use the slider to show the values in realtime, or check the console.
On the <input> element I have a attr tag called data-whatever, so you can customize that data if you want to.
sliders = document.querySelectorAll("input");
sliders.forEach(item=> {
item.addEventListener('input', (e) => {
console.log(`${item.getAttribute("data-whatever")} is this value: ${e.target.value}`);
item.nextElementSibling.textContent = e.target.value;
});
})
.wrapper {
display: flex;
}
span {
padding-right: 30px;
margin-left: 5px;
}
* {
font-size: 12px
}
<div class="wrapper">
<input type="range" min="1" data-whatever="size" max="800" value="50" id="sliderSize">
<em>50</em>
<span>Size</span>
<br>
<input type="range" min="1" data-whatever="OriginY" max="800" value="50" id="sliderOriginY">
<em>50</em>
<span>OriginY</span>
<br>
<input type="range" min="1" data-whatever="OriginX" max="800" value="50" id="sliderOriginX">
<em>50</em>
<span>OriginX</span>
</div>
//catch volume update
var volEvents = "change,input";
var volEventsArr = volEvents.split(",");
for(var i = 0;i<volknob.length;i++) {
for(var k=0;k<volEventsArr.length;k++) {
volknob[i].addEventListener(volEventsArr[k], function() {
var cfa = document.getElementsByClassName('watch_televised');
for (var j = 0; j<cfa.length; j++) {
cfa[j].volume = this.value / 100;
}
});
}
}
'onclick' in the html works for both touch and click event. Here's the example.
This mini javascript libary (1.3 KB) can do all these things
https://github.com/Norair1997/norjs/
nor.event(["#first"], ["touchstart", "click"], [doSomething, doSomething]);

jQuery - If browser tab is focused, then do AJAX - Not working

I have a code that determines if current browser window or tab is active. If it's active, the title of the tab says "active" and if not it says "blurred"
It is working fine. Here's the code:
$(window).on("blur focus", function (e) {
var prevType = $(this).data("prevType");
if (prevType != e.type) { // reduce double fire issues
if (e.type == "blur") {
document.title = 'blurred';
} else if (e.type = "focus") {
document.title = 'focus';
}
}
$(this).data("prevType", e.type);
})
The code above is working fine.
Now if I add AJAX to it, it doesn't work.
$(window).on("blur focus", function (e) {
var prevType = $(this).data("prevType");
if (prevType != e.type) { // reduce double fire issues
if (e.type == "blur") {
document.title = 'blurred';
} else if (e.type = "focus") {
var interval = function () {
$.ajax({
url: "<?php echo base_url('home/get') ?>",
cache: false,
success: function (html) {
$("#text").val(html);
document.title ='focus';
},
});
};
setInterval(interval, <?php echo $int ?>);
}
}
$(this).data("prevType", e.type);
})
It says focused if it's in focus. If I go out of focus, it says "blurred" for less than a second, then says focus again. I don't know why. I want it to say blurred if it's not in focus. Adding the AJAX code doesn't make it work.
Please help. Thanks.
You need to use clearTimeout() in your blur event. My code continuously polls my server for data, but when I go out of the page, it stops polling. Please look at the implementation below. I have done the similar one in my application here:
$(window).blur(function() {
clearTimeout(getJsonTmr);
clearTimeout(updatePreviewTmr);
}).focus(StartTimers);
function StartTimers () {
// Every half a second,
getJsonTmr = setInterval(function () {
$.get("/doodles/update?getJson&DoodleID=" + DoodleOptions.DoodleID, function (data) {
data = JSON.parse(data);
if (!DoodleOptions.isActive)
clearDoodleCanvas();
$.each(data, function (index) {
drawFromStream(data[index]);
});
});
}, 500);
updatePreviewTmr = setInterval(function () {
$.post("/doodles/update?updatePreview", {
"DoodleID": DoodleOptions.DoodleID,
"DoodlePreview": canvas.toDataURL()
});
}, 5000);
}
StartTimers();
You can use the above code as reference and change yours.
A simple reference implementation for you...
function timers() {
tmrAjax = setInterval(function () {
$.get(/* ... */);
}, 1000);
}
timers();
$(window).blur(function () {
clearInterval(tmrAjax);
}).focus(timers);

html5 audio bind timeupdate before element exists

im trying to bind the "timeupdate" event from an audio tag, which doesn't exist yet. I was used to do it this way:
$("body").on("click","#selector", function(e) {
});
I tried this with the audio tag:
$("body").on("timeupdate", ".audioPlayerJS audio", function(e) {
alert("test");
console.log($(".audioPlayerJS audio").prop("currentTime"));
$(".audioPlayerJS span.current-time").html($(".audioPlayerJS audio").prop("currentTime"));
});
This doesn't work though. Is this supposed to work? Or what am I doing wrong?
Any help is highly appreciated.
There is a fiddel for you: jsfiddel
Apparently media events( those specifically belonging to audio or video like play, pause, timeupdate, etc) do not get bubbled. you can find the explanation for that in the answer to this question.
So using their solution, I captured the timeupdate event,
$.createEventCapturing(['timeupdate']);
$('body').on('timeupdate', '.audioPlayerJS audio', updateTime); // now this would work.
JSFiddle demo
the code for event capturing( taken from the other SO answer):
$.createEventCapturing = (function () {
var special = $.event.special;
return function (names) {
if (!document.addEventListener) {
return;
}
if (typeof names == 'string') {
names = [names];
}
$.each(names, function (i, name) {
var handler = function (e) {
e = $.event.fix(e);
return $.event.dispatch.call(this, e);
};
special[name] = special[name] || {};
if (special[name].setup || special[name].teardown) {
return;
}
$.extend(special[name], {
setup: function () {
this.addEventListener(name, handler, true);
},
teardown: function () {
this.removeEventListener(name, handler, true);
}
});
});
};
})();

WinJS gestureRecognizer - how to trap multiple gestures

I've been using this article (and some others) to try and implement a gesture recognition in my app, and it does work. However, what I want to do is to detect multiple gestures; for example, a swipe, and a touch. What i don't seem to be able to do is to establish whether the MouseUp event is caused by the end of a gesture, or by a single touch.
function processUpEvent(e) {
lastElement = e.currentTarget;
gestureRecognizer.processUpEvent(e.currentPoint);
processTouchEvent(e.currentPoint);
}
What happens currently is it processed both. How can I detect whether the user has 'let go' of the screen for a swipe, or a touch?
EDIT:
var recognizer = new Windows.UI.Input.GestureRecognizer();
recognizer.gestureSettings = Windows.UI.Input.GestureSettings.manipulationTranslateX
recognizer.addEventListener('manipulationcompleted', function (e) {
var dx = e.cumulative.translation.x
//Do something with direction here
});
var processUp = function (args) {
try {
recognizer.processUpEvent(args.currentPoint);
}
catch (e) { }
}
canvas.addEventListener('MSPointerDown', function (args) {
try {
recognizer.processDownEvent(args.currentPoint);
}
catch (e) { }
}, false);
canvas.addEventListener('MSPointerMove', function (args) {
try {
recognizer.processMoveEvents(args.intermediatePoints);
}
catch (e) { }
}, false);
canvas.addEventListener('MSPointerUp', processUp, false);
canvas.addEventListener('MSPointerCancel', processUp, false);
So I need to handle both processUp and manipulationcompleted, but one or the other.
You can have a look at my "input" demo in codeSHOW. Just install the codeSHOW app (http://aka.ms/codeshowapp) and look at the Pointer Input demo and "see the code" or just go to the source code on CodePlex.
Hope that helps.
I've found a way to do this, but it's not pretty:
var eventFlag = 0;
var processUp = function (args) {
try {
recognizer.processUpEvent(args.currentPoint);
if (eventFlag == 0) {
// do stuff
} else {
eventFlag = 0;
}
}
catch (e) { }
}
recognizer.gestureSettings = Windows.UI.Input.GestureSettings.manipulationTranslateX
recognizer.addEventListener('manipulationcompleted', function (e) {
var dx = e.cumulative.translation.x
//Do something with direction here
eventFlag = 1;
});

Long Press in JavaScript?

Is it possible to implement "long press" in JavaScript (or jQuery)? How?
(source: androinica.com)
HTML
Long press
JavaScript
$("a").mouseup(function(){
// Clear timeout
return false;
}).mousedown(function(){
// Set timeout
return false;
});
There is no 'jQuery' magic, just JavaScript timers.
var pressTimer;
$("a").mouseup(function(){
clearTimeout(pressTimer);
// Clear timeout
return false;
}).mousedown(function(){
// Set timeout
pressTimer = window.setTimeout(function() { ... Your Code ...},1000);
return false;
});
Based on Maycow Moura's answer, I wrote this. It also ensures that the user didn't do a right click, which would trigger a long press and works on mobile devices. DEMO
var node = document.getElementsByTagName("p")[0];
var longpress = false;
var presstimer = null;
var longtarget = null;
var cancel = function(e) {
if (presstimer !== null) {
clearTimeout(presstimer);
presstimer = null;
}
this.classList.remove("longpress");
};
var click = function(e) {
if (presstimer !== null) {
clearTimeout(presstimer);
presstimer = null;
}
this.classList.remove("longpress");
if (longpress) {
return false;
}
alert("press");
};
var start = function(e) {
console.log(e);
if (e.type === "click" && e.button !== 0) {
return;
}
longpress = false;
this.classList.add("longpress");
if (presstimer === null) {
presstimer = setTimeout(function() {
alert("long click");
longpress = true;
}, 1000);
}
return false;
};
node.addEventListener("mousedown", start);
node.addEventListener("touchstart", start);
node.addEventListener("click", click);
node.addEventListener("mouseout", cancel);
node.addEventListener("touchend", cancel);
node.addEventListener("touchleave", cancel);
node.addEventListener("touchcancel", cancel);
You should also include some indicator using CSS animations:
p {
background: red;
padding: 100px;
}
.longpress {
-webkit-animation: 1s longpress;
animation: 1s longpress;
}
#-webkit-keyframes longpress {
0%, 20% { background: red; }
100% { background: yellow; }
}
#keyframes longpress {
0%, 20% { background: red; }
100% { background: yellow; }
}
You can use taphold event of jQuery mobile API.
jQuery("a").on("taphold", function( event ) { ... } )
I created long-press-event (0.5k pure JS) to solve this, it adds a long-press event to the DOM.
Listen for a long-press on any element:
// the event bubbles, so you can listen at the root level
document.addEventListener('long-press', function(e) {
console.log(e.target);
});
Listen for a long-press on a specific element:
// get the element
var el = document.getElementById('idOfElement');
// add a long-press event listener
el.addEventListener('long-press', function(e) {
// stop the event from bubbling up
e.preventDefault()
console.log(e.target);
});
Works in IE9+, Chrome, Firefox, Safari & hybrid mobile apps (Cordova & Ionic on iOS/Android)
Demo
While it does look simple enough to implement on your own with a timeout and a couple of mouse event handlers, it gets a bit more complicated when you consider cases like click-drag-release, supporting both press and long-press on the same element, and working with touch devices like the iPad. I ended up using the longclick jQuery plugin (Github), which takes care of that stuff for me. If you only need to support touchscreen devices like mobile phones, you might also try the jQuery Mobile taphold event.
For modern, mobile browsers:
document.addEventListener('contextmenu', callback);
https://developer.mozilla.org/en-US/docs/Web/Events/contextmenu
jQuery plugin. Just put $(expression).longClick(function() { <your code here> });. Second parameter is hold duration; default timeout is 500 ms.
(function($) {
$.fn.longClick = function(callback, timeout) {
var timer;
timeout = timeout || 500;
$(this).mousedown(function() {
timer = setTimeout(function() { callback(); }, timeout);
return false;
});
$(document).mouseup(function() {
clearTimeout(timer);
return false;
});
};
})(jQuery);
$(document).ready(function () {
var longpress = false;
$("button").on('click', function () {
(longpress) ? alert("Long Press") : alert("Short Press");
});
var startTime, endTime;
$("button").on('mousedown', function () {
startTime = new Date().getTime();
});
$("button").on('mouseup', function () {
endTime = new Date().getTime();
longpress = (endTime - startTime < 500) ? false : true;
});
});
DEMO
For cross platform developers (Note All answers given so far will not work on iOS):
Mouseup/down seemed to work okay on android - but not all devices ie (samsung tab4). Did not work at all on iOS.
Further research its seems that this is due to the element having selection and the native magnification interupts the listener.
This event listener enables a thumbnail image to be opened in a bootstrap modal, if the user holds the image for 500ms.
It uses a responsive image class therefore showing a larger version of the image.
This piece of code has been fully tested upon (iPad/Tab4/TabA/Galaxy4):
var pressTimer;
$(".thumbnail").on('touchend', function (e) {
clearTimeout(pressTimer);
}).on('touchstart', function (e) {
var target = $(e.currentTarget);
var imagePath = target.find('img').attr('src');
var title = target.find('.myCaption:visible').first().text();
$('#dds-modal-title').text(title);
$('#dds-modal-img').attr('src', imagePath);
// Set timeout
pressTimer = window.setTimeout(function () {
$('#dds-modal').modal('show');
}, 500)
});
The Diodeus's answer is awesome, but it prevent you to add a onClick function, it'll never run hold function if you put an onclick. And the Razzak's answer is almost perfect, but it run hold function only on mouseup, and generally, the function runs even if user keep holding.
So, I joined both, and made this:
$(element).on('click', function () {
if(longpress) { // if detect hold, stop onclick function
return false;
};
});
$(element).on('mousedown', function () {
longpress = false; //longpress is false initially
pressTimer = window.setTimeout(function(){
// your code here
longpress = true; //if run hold function, longpress is true
},1000)
});
$(element).on('mouseup', function () {
clearTimeout(pressTimer); //clear time on mouseup
});
You could set the timeout for that element on mouse down and clear it on mouse up:
$("a").mousedown(function() {
// set timeout for this element
var timeout = window.setTimeout(function() { /* … */ }, 1234);
$(this).mouseup(function() {
// clear timeout for this element
window.clearTimeout(timeout);
// reset mouse up event handler
$(this).unbind("mouseup");
return false;
});
return false;
});
With this each element gets its own timeout.
This worked for me:
const a = document.querySelector('a');
a.oncontextmenu = function() {
console.log('south north');
};
https://developer.mozilla.org/docs/Web/API/GlobalEventHandlers/oncontextmenu
You can use jquery-mobile's taphold. Include the jquery-mobile.js and the following code will work fine
$(document).on("pagecreate","#pagename",function(){
$("p").on("taphold",function(){
$(this).hide(); //your code
});
});
Most elegant and clean is a jQuery plugin:
https://github.com/untill/jquery.longclick/,
also available as packacke:
https://www.npmjs.com/package/jquery.longclick.
In short, you use it like so:
$( 'button').mayTriggerLongClicks().on( 'longClick', function() { your code here } );
The advantage of this plugin is that, in contrast to some of the other answers here, click events are still possible. Note also that a long click occurs, just like a long tap on a device, before mouseup. So, that's a feature.
I needed something for longpress keyboard events, so I wrote this.
var longpressKeys = [13];
var longpressTimeout = 1500;
var longpressActive = false;
var longpressFunc = null;
document.addEventListener('keydown', function(e) {
if (longpressFunc == null && longpressKeys.indexOf(e.keyCode) > -1) {
longpressFunc = setTimeout(function() {
console.log('longpress triggered');
longpressActive = true;
}, longpressTimeout);
// any key not defined as a longpress
} else if (longpressKeys.indexOf(e.keyCode) == -1) {
console.log('shortpress triggered');
}
});
document.addEventListener('keyup', function(e) {
clearTimeout(longpressFunc);
longpressFunc = null;
// longpress key triggered as a shortpress
if (!longpressActive && longpressKeys.indexOf(e.keyCode) > -1) {
console.log('shortpress triggered');
}
longpressActive = false;
});
In vanila JS if need to detect long-click after click released:
document.addEventListener("mousedown", longClickHandler, true);
document.addEventListener("mouseup", longClickHandler, true);
let startClick = 0;
function longClickHandler(e){
if(e.type == "mousedown"){
startClick = e.timeStamp;
}
else if(e.type == "mouseup" && startClick > 0){
if(e.timeStamp - startClick > 500){ // 0.5 secound
console.log("Long click !!!");
}
}
}
May need to use timer if need to check long-click while clicking. But for most case after release click is enought.
For me it's work with that code (with jQuery):
var int = null,
fired = false;
var longclickFilm = function($t) {
$body.css('background', 'red');
},
clickFilm = function($t) {
$t = $t.clone(false, false);
var $to = $('footer > div:first');
$to.find('.empty').remove();
$t.appendTo($to);
},
touchStartFilm = function(event) {
event.preventDefault();
fired = false;
int = setTimeout(function($t) {
longclickFilm($t);
fired = true;
}, 2000, $(this)); // 2 sec for long click ?
return false;
},
touchEndFilm = function(event) {
event.preventDefault();
clearTimeout(int);
if (fired) return false;
else clickFilm($(this));
return false;
};
$('ul#thelist .thumbBox')
.live('mousedown touchstart', touchStartFilm)
.live('mouseup touchend touchcancel', touchEndFilm);
You can check the time to identify Click or Long Press [jQuery]
function AddButtonEventListener() {
try {
var mousedowntime;
var presstime;
$("button[id$='" + buttonID + "']").mousedown(function() {
var d = new Date();
mousedowntime = d.getTime();
});
$("button[id$='" + buttonID + "']").mouseup(function() {
var d = new Date();
presstime = d.getTime() - mousedowntime;
if (presstime > 999/*You can decide the time*/) {
//Do_Action_Long_Press_Event();
}
else {
//Do_Action_Click_Event();
}
});
}
catch (err) {
alert(err.message);
}
}
You can use jquery Touch events. (see here)
let holdBtn = $('#holdBtn')
let holdDuration = 1000
let holdTimer
holdBtn.on('touchend', function () {
// finish hold
});
holdBtn.on('touchstart', function () {
// start hold
holdTimer = setTimeout(function() {
//action after certain time of hold
}, holdDuration );
});
like this?
target.addEeventListener("touchstart", function(){
// your code ...
}, false);

Categories

Resources