I am using Google maps API version 3. I would like my double click event handler to work as follows:
google.maps.event.addListener(map, 'dblclick', function(e) {
if (/* ctrl is pressed */) {
doSomething(e)
} else {
doSomethingElse(e)
}
});
It looks the the event handler only provides a MouseEvent which does not contain information about the keyboard state. I need the LatLng information so I doubt that I can use JQuery's event handling.
Am I out of luck here?
You could just cache the control key state
var ctrlPressed = false;
function cacheIt(event) {
ctrlPressed = event.ctrlKey;
}
document.onkeydown = cacheIt;
document.onkeyup = cacheIt;
Now, ctrlPressed should always reflect whether or not the control key is down.
When caching key presses, I always bind a function to the blur event of the window object which clears any cached key presses. Otherwise you get stuck keys if the window loses focus.
Here's an example using jQuery:
$(window).blur( function () {
// Clear cached key presses here
})
Related
How does a website recognize keystrokes, mouse movement?
Is there a way to send a command ("like pressing down your left mouse button) via JavaScript, without actually pressing down your mouse button?
If my question is too unclear, I'm very happy to explain further. I'm new and just trying to start somewhere but am lost.
Can you recommend me some good learning material, so I can read into it, thank you very much.
Mouse, Keyboard, and other Events
Sites recognize keyboard and mouse 'events' by subscribing a function to them.
You can do that thru html like so: onkeypress="keypressFunction()", onmousemove="mousemoveFunction()", onclick="clickFunction()"... and other events
<div onclick="clickFunction()">Clickable</div>
Of course these functions keypressFunction(), mousemoveFunction(), clickFunction() need to exist somewhere in your site, whether inside
<script>
function clickFunction(){ alert('clicked!') }
</script>
or included from file: <script src="myscripts.js"></script> .
You can also subscribe to events using just javascript:
//Write `document` instead of `element` to apply to whole document
//Or you can find element by id like document.getElementById('id')
//You can of course use any other method of finding elements such
// as querySelector or use variables you already made before
element.onkeypress = function(eventArgs){
eventArgs = eventArgs || window.event;
// use eventArgs.keyCode to get which key
};
Or, more common and safe, subscribe with addEventListener:
element.addEventListener('keypress', function(eventArgs){
eventArgs = eventArgs || window.event;
// use eventArgs.keyCode to get which key
});
Note you dont have to write the prefix on in the event names (eg onkeypress) if using addEventListener.
You can of course also use already made functions:
element.onkeypress = myFunction;
and
element.addEventListener('keypress', myFunction);
All of these events usually pass an event-specific parameter to give more data about what exactly happened in the event.
For example, onclick passes MouseEvent args, so you can know where the mouse was (X and Y coords on screen) when the thing was clicked, were the alt/shift/ctrl keys held, and which mouse button was clicked (left, right, middle).
Keyboard events have their own event args with info on which keyboard key was pressed, if its being held, and so on. You can find all event arguments here.
Simulating events
Some basic events, such as a mouse click on an element, can be simulated with just element.click();, but that doesnt give you much control over the event args that are getting passed.
To properly send an event, you need to create a browser event object, and dispatch it on an element:
//Call oncontextmenu (right mouse click) on an element:
var element = document.getElementById('Id_here');
if (window.CustomEvent) {
element.dispatchEvent(new CustomEvent('contextmenu'));
} else if (document.createEvent) {
var ev = document.createEvent('HTMLEvents');
ev.initEvent('contextmenu', true, false);
element.dispatchEvent(ev);
} else { // Internet Explorer
element.fireEvent('oncontextmenu');
}
With that event object you can pass some data, here is simulating a keypress:
var element = document.getElementById('Id_here');
var keyboardEvent = document.createEvent("KeyboardEvent");
var initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
keyboardEvent[initMethod](
"keydown", // event type: keydown, keyup, keypress
true, // bubbles
true, // cancelable
window, // view: should be window
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey
65, // keyCode: unsigned long - the virtual key code, else 0. 65 - 'a'
0 // charCode: unsigned long - the Unicode character associated with the depressed key, else 0
);
element.dispatchEvent(keyboardEvent);
Jquery gives some nice functions to make simulating events easier, but you can find those all over stack overflow, not even to mention google. Just search js simulating keypress/mouse, js subsribe to key/mouse event, and all the other things you can imagine.
I have registered an onChange event to a comboBox in my form. I am trying to get the event type for example(item selected or key pressed) based on the event type other actions will be done. But I haven't managed to get this properties. Is it even possible? I am using Dojo 1.6!
searchEvent = dojo.connect(combo, 'onChange', function(event) {
// console debug(event.type);
});
To listen for key-events, you can use dojo/keys.
Have a look :
http://dojotoolkit.org/reference-guide/1.6/dojo/keys.html (the old one) and here the new AMD :
http://dojotoolkit.org/reference-guide/1.9/dojo/keys.html
Please note that the old dojo.connect is deprecated an will sooner or later not work anymore. So if it is not a big problem for you, I would use the new AMD.
You can listen for the onKeypress like this(using dojo 1.8):
require(["dojo/keys","dojo/dom","dojo/on"], function(keys, dom, on){
on(dom.byId("exampleCombo"), "keypress", function(evt){
alert(evt.charCode); //alerts the charCode of a letter or Number pressed
alert(evt.keyCode); // alerts the keyCode of a key like ENTER
});
});
In this example the CharCode of the pressed key will be alerted.
To listen for the onChange of the Combobox you can do it like this
require(["dojo/dom", "dojo/on"], function (dom, on) {
on(dom.byId("exampleCombo"), "change", function (evt) {
alert('evt : '+JSON.stringify(evt.type));
});
});
The alert shows : evt : 'change'.
Here's the fiddle for the Example above: http://jsfiddle.net/WPWv3/
For more information about events and their handling look here:
http://dojotoolkit.org/reference-guide/1.9/quickstart/events.html
Hope i could help you.
Regards, Miriam
I'm writing web application which should support both mouse and touch interactions.
For testing I use touch screen device with Windows 7. I've tried to sniff touch events in latest Firefox and Chrome canary and got the following results:
On touch Firefox fires touch and corresponding mouse event.
Chrome fires touchstart/mousedown, touchend/mouseup pairs, but mousemove fired in very strange manner: one/two times while touchmove.
All mouse events handled as always.
Is there any way to handle mouse and touch evens simultaneously on modern touch screens? If Firefox fires a pair of touch and mouse event what happens on touchmove with mousemove in Chrome? Should I translate all mouse events to touch or vice versa? I hope to find right way to create responsive interface.
You can't really predict in advance which events to listen for (eg. for all you know a USB touch screen could get plugged in after your page has loaded).
Instead, you should always listen to both the touch events and mouse events, but call preventDefault() on the touch events you handle to prevent (now redundant) mouse events from being fired for them. See http://www.html5rocks.com/en/mobile/touchandmouse/ for details.
You should rather check availability of touch interface and bind events according to that.
You can do something like this:
(function () {
if ('ontouchstart' in window) {
window.Evt = {
PUSH : 'touchstart',
MOVE : 'touchmove',
RELEASE : 'touchend'
};
} else {
window.Evt = {
PUSH : 'mousedown',
MOVE : 'mousemove',
RELEASE : 'mouseup'
};
}
}());
// and then...
document.getElementById('mydiv').addEventListener(Evt.PUSH, myStartDragHandler, false);
If you want to handle both in same time and browser does not translate well touch events into mouse events, you can catch touch events and stop them - then corresponding mouse event shouldn't be fired by browser (you won't have double events) and you can fire it yourself as mouse event or just handle it.
var mydiv = document.getElementsById('mydiv');
mydiv.addEventListener('mousemove', myMoveHandler, false);
mydiv.addEventListener('touchmove', function (e) {
// stop touch event
e.stopPropagation();
e.preventDefault();
// translate to mouse event
var clkEvt = document.createEvent('MouseEvent');
clkEvt.initMouseEvent('mousemove', true, true, window, e.detail,
e.touches[0].screenX, e.touches[0].screenY,
e.touches[0].clientX, e.touches[0].clientY,
false, false, false, false,
0, null);
mydiv.dispatchEvent(clkEvt);
// or just handle touch event
myMoveHandler(e);
}, false);
The solutions on this thread are outdated - for those (like me) who still land here in 2021, there is a new W3 specification for pointer events. These events combine mouse and touch into one.
https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events
https://www.w3.org/TR/pointerevents/
MouseEvents and TouchEvents do not technically provide exactly the same functionality, but for most purposes , they can be used interchangeably. This solution does not favor one over the other, as the user may have both a mouse and a touch screen. Instead, it allows the user to use which ever input device they wish, as long as they wait at least five seconds before changing inputs. This solution ignores mouse pointer emulation on touchscreen devices when the screen is tapped.
var lastEvent = 3 ;
var MOUSE_EVENT = 1;
var TOUCH_EVENT = 2 ;
element.addEventListener('touchstart', function(event)
{
if (lastEvent === MOUSE_EVENT )
{
var time = Date.now() - eventTime ;
if ( time > 5000 )
{
eventTime = Date.now() ;
lastEvent = TOUCH_EVENT ;
interactionStart(event) ;
}
}
else
{
lastEvent = TOUCH_EVENT ; ;
eventTime = Date.now() ;
interactionStart(event) ;
}
}) ;
element.addEventListener('mousedown', function(event)
{
if (lastEvent === TOUCH_EVENT )
{
var time = Date.now() - eventTime ;
if ( time > 5000 )
{
eventTime = Date.now() ;
lastEvent = MOUSE_EVENT ;
interactionStart(event) ;
}
}
else
{
lastEvent= MOUSE_EVENT ;
eventTime = Date.now() ;
interactionStart(event) ;
}
}) ;
function interactionStart(event) // handle interaction (touch or click ) here.
{...}
This is by no means a win all solution, I have used this a few times , and have not found problems with it, but to be fair i usually just use it to start animation when a canvas it tapped , or to provide logic to turn a div into a button. I leave it to you all to use this code , find improvements and help to improve this code.(If you do not find a better solution ).
I found this thread because I have a similar & more complex problem:
supposing we create a js enabled scrollable area with arrows NEXT/PREVIOUS which we want not only to respond to touch and mouse events but also to fire them repeatedly while the user continues to press the screen or hold down his/her mouse!
Repetition of events would make my next button to advance 2 positions instead one!
With the help of closures everything seems possible:
(1) First create a self invoking function for variable isolation:
(function(myScroll, $, window, document, undefined){
...
}(window.myScroll = window.myScroll || {}, jQuery, window, document));
(2) Then, add your private variables that will hold internal state from setTimeout():
/*
* Primary events for handlers that respond to more than one event and devices
* that produce more than one, like touch devices.
* The first event in browser's queue hinders all subsequent for the specific
* key intended to be used by a handler.
* Every key points to an object '{primary: <event type>}'.
*/
var eventLock = {};
// Process ids based on keys.
var pids = {};
// Some defaults
var defaults = {
pressDelay: 100 // ms between successive calls for continuous press by mouse or touch
}
(3) The event lock functions:
function getEventLock(evt, key){
if(typeof(eventLock[key]) == 'undefined'){
eventLock[key] = {};
eventLock[key].primary = evt.type;
return true;
}
if(evt.type == eventLock[key].primary)
return true;
else
return false;
}
function primaryEventLock(evt, key){
eventLock[key].primary = evt.type;
}
(4) Attach your event handlers:
function init(){
$('sth').off('mousedown touchstart', previousStart).on('mousedown touchstart', previousStart);
$('sth').off('mouseup touchend', previousEnd).on('mouseup touchend', previousEnd);
// similar for 'next*' handlers
}
Firing of events mousedown and touchstart will produce double calls for handlers on devices that support both (probably touch fires first). The same applies to mouseup and touchend.
We know that input devices (whole graphic environments actually) produce events sequentially so we don't care which fires first as long a special key is set at private eventLock.next.primary and eventLock.previous.primary for the first events captured from handlers next*() and previous*() respectively.
That key is the event type so that the second, third etc. event are always losers, they don't acquire the lock with the help of the lock functions eventLock() and primaryEventLock().
(5) The above can be seen at the definition of the event handlers:
function previousStart(evt){
// 'race' condition/repetition between 'mousedown' and 'touchstart'
if(!getEventLock(evt, 'previous'))
return;
// a. !!!you have to implement this!!!
previous(evt.target);
// b. emulate successive events of this type
pids.previous = setTimeout(closure, defaults.pressDelay);
// internal function repeats steps (a), (b)
function closure(){
previous(evt.target);
primaryEventLock(evt, 'previous');
pids.previous = setTimeout(closure, defaults.pressDelay);
}
};
function previousEnd(evt){
clearTimeout(pids.previous);
};
Similar for nextStart and nextEnd.
The idea is that whoever comes after the first (touch or mouse) does not acquire a lock with the help of function eventLock(evt, key) and stops there.
The only way to open this lock is to fire the termination event handlers *End() at step (4): previousEnd and nextEnd.
I also handle the problem of touch devices attached in the middle of the session with a very smart way: I noticed that a continuous press longer than defaults.pressDelay produces successive calls of the callback function only for the primary event at that time (the reason is that no end event handler terminates the callabck)!
touchstart event
closure
closure
....
touchend event
I define primary the device the user is using so, all you have to do is just press longer and immediately your device becomes primary with the help of primaryEventLock(evt, 'previous') inside the closure!
Also, note that the time it takes to execute previous(event.target) should be smaller than defaults.pressDelay.
(6) Finally, let's expose init() to the global scope:
myScroll.init = init;
You should replace the call to previous(event.target) with the problem at hand: fiddle.
Also, note that at (5b) there is a solution to another popular question how do we pass arguments to a function called from setTimeout(), i.e. setTimeout(previous, defaults.pressDelay) lacks an argument passing mechanism.
I have been using this jQuery helper to bind both touch and click events.
(function ($) {
$.fn.tclick = function (onclick) {
this.bind("touchstart", function (e) { onclick.call(this, e); e.stopPropagation(); e.preventDefault(); });
this.bind("click", function (e) { onclick.call(this, e); }); //substitute mousedown event for exact same result as touchstart
return this;
};
})(jQuery);
Pressing space bar in game will make a character shoot, pressing space bar when a confirmation box is shown will make this box disappear and pressing space bar in a highscore form will add a space in an input box. In this example there are several events for the same key, but only one is fired at a time.
Is there a general (or specific for Javascript) method or way of programming to add events to a certain key, so they are only executed under certain circumstances?
Of course it can be done like this:
var inGame = true|false;
var inConfirmationBox = true|false;
function spaceBarHandler(){
if(inGame){ /*shoot*/}
else if(inConfirmationBox){ /*remove box*/}
}
document.onkeydown = function(){ /* call space bar handler if space bar was pressed */ };
But this is a very confusing way of programming, since specific actions are mixed together in a space bar handler function, which makes maintenance hard.
What is the best way to handle multiple events for one key, such that these events are only fired under certain circumstances?
Functions are first-class objects in javascript, which makes them really powerful. Because of this, your problem can be solved very elegantly.
// the whole thing can be encapsulated
// into an object actually
function spaceBarHandler() {
var state = spaceBarHandler.state;
var actions = spaceBarHandler.actions;
// execute function if exists
if (actions[state]) {
actions[state]();
}
}
// spaceBar actions
spaceBarHandler.actions = {
shoot: function() {
// bang bang
},
removeBox: function() {
// do it...
}
};
// change current state from outside
// we are in the game
spaceBarHandler.state = "shoot";
// change current state from outside
// confirmation box is shown
spaceBarHandler.state = "removeBox";
All these cases will be handled by one function. If you want to extend with another case, you just add another function to the actions object. Notice how the whole thing is encapsulated into one object.
you could instead add and remove the event listener as needed.
let's assume you're using a javascript framework (if you're not, then you probably should be considering the amount of JS code involved in a game like this)
using PrototypeJS:
when game starts,
document.observe("keydown",shootHandler());
when the message box is created,
function createBox(text) {
...snip
document.observe("keydown",closeBox());
document.fire("game:pause");
}
and, for example
var paused = false;
function shoothandler() {
if (!paused) {
alert("pew! pew!");
}
}
function closeBox() {
$('messagebox').remove();
document.fire("game:unpaused");
document.stopObserving("keydown",closeBox());
}
document.observe("game:paused", function() { paused = true;});
document.observe("game:unpaused", function() { paused = false;});
document.observe("game:over", function() { document.stopObserving("keydown",shootHandler());});
I haven't included the high score screen but the theory is the same.
As you can see, I also used custom events to notify the pause status. The same event could also be fire by a puase button in the interface, etc...
Attach event listeners to individual elements instead of the entire document.
document.getElementById('highscore').onkeypress = function(keyEvent) {
if (is_spacebar(keyEvent)) //Do something...
};
document.getElementById('game').onkeypress = function(keyEvent) {
if (is_spacebar(keyEvent)) //Do something else...
};
This is a simplistic example. You will probably have to deal with event bubbling which can be controlled when using addEventListener() to attach functions to events. Given browser (IE) compatibility issues involving this, some JS library should be used to deal with events.
There are a few ways, typically involving code-branching for IE's ‘special’ event model.
One way is to stop keypresses handled further down from bubbling up to the document key handler:
confirmationbox.onkeydown = function(event) {
if (event === undefined) event = window.event;
// do something with event.keyCode
if ('stopPropagation' in event) // standards browsers
event.stopPropagation();
else if ('cancelBubble' in event) // IE before version 9
event.cancelBubble = true;
};
document.onkeydown = ... // will not be called for keydowns inside confirmationbox
Another way would be to check the event target element to see if it's in the box:
document.onkeydown = function(event) {
if (event === undefined) event = window.event;
var target = 'target' in event ? event.target : event.srcElement; // srcElement is for IE<9
if (target === containerbox || isDescendantOf(target, containerbox) {
// do containerbox stuff
} else {
// do other stuff
}
};
function isDescendantOf(element, ancestor) {
while (element = element.parentNode)
if (element === ancestor)
return true;
return false;
}
I'm making a javascript game and need to bind a lot of keys to different functions. This I know how to do, what I need some help on is over-riding the shortcut keys for the browser. in other-words I want to blur hot keys for the browser and divert them to my application.
I believe that if you stop the propogation of the event, then you will prevent the browser from catching the event.
an example of this:
element.onkeyup = function(e) {
var ev = e || event;
//do stuff here, probably with ev.keyCode
return false;
}