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);
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 know that this is possible with for mouse events using the MouseEvent constructor.
For example, with the MouseEvent constructor, you can simulate mouse clicks easily. With this approach, I can add additional fields to my "fake" mouse event such as
var event = new MouseEvent(type, mouseEventInit)'
event.fake = true
that allows me to detect in the listener wether this event was triggered by the user or the app.
I would like to do something similar with a html video when it is played/paused/seeked. The video can either be controlled by my app (calling video.play(), video.pause() or setting video.currentTime = x) or the user (using the video controls normally) at any given time. When the event listeners I added are fired I do not have a way to know who or what was the source (the user or my app).
So basically, I don't want to listen to events triggered by my app but I do want to listen to user actions (the user clicked on the play button or seeked in the video by clicking somewhere on the progress bar).
My current approach is to remove the listeners and add them back afterwards like so:
video.removeEventListener('play', playListener)
video.play()
// I have to wait until the video actually plays
setTimeout(function() {
video.addEventListener('play', playListener)
}, 100)
It works fine, but when I do this for seeking, it's harder to select a good time for settimeout because of video buffering (it seems like the seeked event is triggered after the video finished buffering). So I'm exploring different methods.
I know about the CustomEvent() constructor, but I doubt I could use this to actually play or seek. I need to create an event that will trigger the appropriate actions (play, pause or seek). Feel free to correct me if I'm wrong.
Any other ideas I could detect events triggered by my app and/or the user?
You can attach a Boolean to object passed to event handler to determine if event was dispatched in response to user action or application action.
var button = document.querySelector("button");
var div = button.nextElementSibling;
var app = {
config: "def",
userAction: "abc",
setApp: function setApp(duration) {
this.timeout = setTimeout(function() {
button.dispatchEvent(appEvent)
}, duration || 3500);
},
timeout: null
}
// dispatch `app` event
app.setApp(0);
function handleEvent(event) {
// check `event.detail.app` object `Boolean` value
if (event.detail.app) {
div.textContent = app.config;
} else {
div.textContent = app.userAction;
// dispatch `app` event in 3500ms
app.setApp();
}
}
button.onclick = handleEvent;
// set plain object having `Boolean` value at `event.detail` `{app:true}`
var appEvent = new CustomEvent("click", {
detail: {
app: true
}
});
<button>click</button>
<div></div>
I'm facing a strange problem that I think leaves HammerJS internal event loop with a stuck event that ruins subsequent detections.
This only happens on Internet Explorer Edge on a Touch Device with PointerEvents.
Basically, when using HammerJS for a PAN event (panstart -> panmove -> panend), and you cross the current frame boundary (for example, into an IFRAME, or just outside the browser window) AND you release your finger there, then HammerJS never receives the CANCEL event and the session kind of stays stuck.
From then on, all gestures are reported incorrectly, with one more finger ('pointer') than you're using: For example, it will report a PINCH or ROTATE (2 pointers) just tapping (1 pointer) and so on.
I haven't found a way to reset the Hammer Manager once it enters this ghost state. This breaks my app.
I've prepared a Fiddle with a full working example. Please execute it under a Windows/Touch device !
https://jsfiddle.net/28cxrupv/5/
I'd like to know, either how to detect the out-of-bounds event, or just how could I manually reset the Hammer Manager instance if I am able to detect myself by other means that there are stuck events.
UPDATE
I've found in my investigations that the problem is at the lowest level in HammerJS: the PointerEvents handler has an array of detected pointers this.store and there's the stuck event with an old timestamp.
I've found a way to patch Hammer.JS so it can detect stuck pointers. I don't know if this is wrong, but apparently it works!
On HammerJS PointerEvents handler, there's an array this.store that keeps all current pointer events. It's there where, when we pan out of the window and release the touch, a stuck event is kept forever.
Clearing this array causes Hammer to go back to normal again.
I just added a condition where, if we are processing a Primary touch (start of a gesture?), and the store is not empty, it clears the store automatically.
How it works is, on the next interaction with the stuck hammer instance, the internal store gets reset and the gesture is interpreted properly.
On Hammer.js 2.0.6, around line 885
/**
* handle mouse events
* #param {Object} ev
*/
handler: function PEhandler(ev) {
var store = this.store;
var removePointer = false;
var eventTypeNormalized = ev.type.toLowerCase().replace('ms', '');
var eventType = POINTER_INPUT_MAP[eventTypeNormalized];
var pointerType = IE10_POINTER_TYPE_ENUM[ev.pointerType] || ev.pointerType;
var isTouch = (pointerType == INPUT_TYPE_TOUCH);
// get index of the event in the store
var storeIndex = inArray(store, ev.pointerId, 'pointerId');
// start and mouse must be down
if (eventType & INPUT_START && (ev.button === 0 || isTouch)) {
// NEW CONDITION: Check the store is empty on a new gesture
// http://stackoverflow.com/questions/35618107/cross-frame-events-on-ie-edge-break-hammerjs-v2
if (ev.isPrimary && store.length) {
window.console.warn ("Store should be 0 on a primary touch! Clearing Stuck Event!");
this.reset();
}
if (storeIndex < 0) {
store.push(ev);
storeIndex = store.length - 1;
}
} else if (eventType & (INPUT_END | INPUT_CANCEL)) {
removePointer = true;
}
// it not found, so the pointer hasn't been down (so it's probably a hover)
if (storeIndex < 0) {
return;
}
// update the event in the store
store[storeIndex] = ev;
this.callback(this.manager, eventType, {
pointers: store,
changedPointers: [ev],
pointerType: pointerType,
srcEvent: ev
});
if (removePointer) {
// remove from the store
store.splice(storeIndex, 1);
}
}
});
and also I define the function "reset":
/**
* Reset internal state
*/
reset: function() {
this.store = (this.manager.session.pointerEvents = []);
},
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 attempting to build a webpage that loads depending on the input provided. I'm having some trouble wrapping my head around event handling in javascript, basically. Coming from python, if I wanted to wait for a specific keyboard input before moving on to the next object to display, I would create a while loop and put a key listener inside it.
Python:
def getInput():
while 1:
for event in pygame.event.get(): #returns a list of events from the keyboard/mouse
if event.type == KEYDOWN:
if event.key == "enter": # for example
do function()
return
elif event.key == "up":
do function2()
continue
else: continue # for clarity
In trying to find a way to implement this in DOM/javascript, I seem to just crash the page (I assume due to the While Loop), but I presume this is because my event handling is poorly written. Also, registering event handlers with "element.onkeydown = function;" difficult for me to wrap my head around, and setInterval(foo(), interval] hasn't brought me much success.
Basically, I want a "listening" loop to do a certain behavior for key X, but to break when key Y is hit.
In JavaScript, you give up control of the main loop. The browser runs the main loop and calls back down into your code when an event or timeout/interval occurs. You have to handle the event and then return so that the browser can get on with doing other things, firing events, and so on.
So you cannot have a ‘listening’ loop. The browser does that for you, giving you the event and letting you deal with it, but once you've finished handling the event you must return. You can't fall back into a different loop. This means you can't write step-by-step procedural code; if you have state that persists between event calls you must store it, eg. in a variable.
This approach cannot work:
<input type="text" readonly="readonly" value="" id="status" />
var s= document.getElementById('status');
s.value= 'Press A now';
while (true) {
var e= eventLoop.nextKeyEvent(); // THERE IS NO SUCH THING AS THIS
if (e.which=='a')
break
}
s.value= 'Press Y or N';
while (true) {
var e= eventLoop.nextKeyEvent();
if (e.which=='y') ...
Step-by-step code has to be turned inside out so that the browser calls down to you, instead of you calling up to the browser:
var state= 0;
function keypressed(event) {
var key= String.fromCharCode(event? event.which : window.event.keyCode); // IE compatibility
switch (state) {
case 0:
if (key=='a') {
s.value= 'Press Y or N';
state++;
}
break;
case 1:
if (key=='y') ...
break;
}
}
s.value= 'Press A now';
document.onkeypress= keypressed;
You can also make code look a little more linear and clean up some of the state stuff by using nested anonymous functions:
s.value= 'Press A now';
document.onkeypress= function(event) {
var key= String.fromCharCode(event? event.which : window.event.keyCode);
if (key=='a') {
s.value= 'Press Y or N';
document.onkeypress= function(event) {
var key= String.fromCharCode(event? event.which : window.event.keyCode);
if (key=='y') ...
};
}
};
you should not use such loops in javascript. basically you do not want to block the browser from doing its job. Thus you work with events (onkeyup/down).
also instead of a loop you should use setTimeout if you want to wait a little and continue if something happened
you can do sth like that:
<html>
<script>
var dataToLoad = new Array('data1', 'data2', 'data3' );
var pos = 0;
function continueData(ev) {
// do whatever checks you need about key
var ele = document.getElementById("mydata");
if (pos < dataToLoad.length)
{
ele.appendChild(document.createTextNode(dataToLoad[pos]));
pos++;
}
}
</script>
<body onkeyup="continueData()"><div id="mydata"></div></body></html>
everytime a key is released the next data field is appended
For easier implementation of event handling I recommend you to use a library such as Prototype or Jquery (Note that both links take you to their respective Event handling documentation.
In order to use them you have to keep in mind 3 things:
What DOM element you want to observe
What Event you want to capture
What action will the event trigger
This three points are mutually inclusive, meaning you need to take care of the 3 when writing the code.
So having this in mind, using Prototype, you could do this:
Event.observe($('id_of_the_element_to_observe'), 'keypress', function(ev) {
// the argument ev is the event object that has some useful information such
// as which keycode was pressed.
code_to_run;
});
Here is the code of a more useful example, a CharacterCounter (such as the one found in Twitter, but surely a lot less reliable ;) ):
var CharacterCounter = Class.create({
initialize: function(input, counter, max_chars) {
this.input = input;
this.counter = counter;
this.max_chars = max_chars;
Event.observe(this.input, 'keypress', this.keyPressHandler.bind(this));
Event.observe(this.input, 'keyup', this.keyUpHandler.bind(this));
},
keyUpHandler: function() {
words_left = this.max_chars - $F(this.input).length;
this.counter.innerHTML = words_left;
},
keyPressHandler: function(e) {
words_left = this.max_chars - $F(this.input).length;
if (words_left <= 0 && this.allowedChars(e.keyCode)) {
e.stop();
}
},
allowedChars: function(keycode) {
// 8: backspace, 37-40: arrow keys, 46: delete
allowed_keycodes = [ 8, 37, 38, 39, 40, 46 ];
if (allowed_keycodes.include(keycode)) {
return false;
}
return true
}
});
Any good browser will crash when it encounters a script that runs too long. This is to prevent malicious websites from locking up the client application.
You cannot have a infinite loop in javascript. Instead, attach an event listener to the window and point do your processing in the handler (think of it as interrupts instead of polling).
Example:
function addEventSimple(obj,evt,fn) {
if (obj.addEventListener)
obj.addEventListener(evt,fn,false);
else if (obj.attachEvent)
obj.attachEvent('on'+evt,fn);
} // method pulled from quirksmode.org for cross-browser compatibility
addEventSimple(window, "keydown", function(e) {
// check keys
});
document.onkeydown = function(e) {
//do what you need to do
}
That's all it takes in javascript. You don't need to loop to wait for the event to happen, whenever the event occurs that function will be called, which in turn can call other functions, do whatever needs to be be done. Think of it as that instead of you having to wait for the event your looking for to happen, the event your looking for will let you know when it happens.
you could attach an event listener to the window object like this
window.captureEvents(Event.KEYPRESS);
window.onkeypress = output;
function output(event) {
alert("you pressed" + event.which);
}
Check out the YUI key listener
http://developer.yahoo.com/yui/docs/YAHOO.util.KeyListener.html
using the key listener, YUI takes care of capturing any events. IN javascript, there will almost never be an instance where you must wait in a while loop for something to happen.
If you need examples of how event handling works, check out these pages.
http://developer.yahoo.com/yui/examples/event/eventsimple.html