Javascript event handling and flow control - javascript

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

Related

Tampermoney script to click on button automatically [duplicate]

I have a hyperlink in my page. I am trying to automate a number of clicks on the hyperlink for testing purposes. Is there any way you can simulate 50 clicks on the hyperlink using JavaScript?
MSDN
I'm looking for onClick event trigger from the JavaScript.
Performing a single click on an HTML element: Simply do element.click(). Most major browsers support this.
To repeat the click more than once: Add an ID to the element to uniquely select it:
Google Chrome
and call the .click() method in your JavaScript code via a for loop:
var link = document.getElementById('my-link');
for(var i = 0; i < 50; i++)
link.click();
You should just use click. For more advanced event firing, use dispatchEvent.
const body = document.body;
body.addEventListener('click', e => {
console.log('clicked body');
});
console.log('Using click()');
body.click();
console.log('Using dispatchEvent');
body.dispatchEvent(new Event('click'));
Original Answer - Obsolete
Here is what I use for IE9+ http://jsfiddle.net/mendesjuan/rHMCy/4/
/**
* Fire an event handler to the specified node. Event handlers can detect that the event was fired programatically
* by testing for a 'synthetic=true' property on the event object
* #param {HTMLNode} node The node to fire the event handler on.
* #param {String} eventName The name of the event without the "on" (e.g., "focus")
*/
function fireEvent(node, eventName) {
// Make sure we use the ownerDocument from the provided node to avoid cross-window problems
var doc;
if (node.ownerDocument) {
doc = node.ownerDocument;
} else if (node.nodeType == 9){
// the node may be the document itself, nodeType 9 = DOCUMENT_NODE
doc = node;
} else {
throw new Error("Invalid node passed to fireEvent: " + node.id);
}
if (node.dispatchEvent) {
// Gecko-style approach (now the standard) takes more work
var eventClass = "";
// Different events have different event classes.
// If this switch statement can't map an eventName to an eventClass,
// the event firing is going to fail.
switch (eventName) {
case "click": // Dispatching of 'click' appears to not work correctly in Safari. Use 'mousedown' or 'mouseup' instead.
case "mousedown":
case "mouseup":
eventClass = "MouseEvents";
break;
case "focus":
case "change":
case "blur":
case "select":
eventClass = "HTMLEvents";
break;
default:
throw "fireEvent: Couldn't find an event class for event '" + eventName + "'.";
break;
}
var event = doc.createEvent(eventClass);
event.initEvent(eventName, true, true); // All events created as bubbling and cancelable.
event.synthetic = true; // allow detection of synthetic events
// The second parameter says go ahead with the default action
node.dispatchEvent(event, true);
} else if (node.fireEvent) {
// IE-old school style, you can drop this if you don't need to support IE8 and lower
var event = doc.createEventObject();
event.synthetic = true; // allow detection of synthetic events
node.fireEvent("on" + eventName, event);
}
};
Note that calling fireEvent(inputField, 'change'); does not mean it will actually change the input field. The typical use case for firing a change event is when you set a field programmatically and you want event handlers to be called since calling input.value="Something" won't trigger a change event.
What
l.onclick();
does is exactly calling the onclick function of l, that is, if you have set one with l.onclick = myFunction;. If you haven't set l.onclick, it does nothing. In contrast,
l.click();
simulates a click and fires all event handlers, whether added with l.addEventHandler('click', myFunction);, in HTML, or in any other way.
I'm quite ashamed that there are so many incorrect or undisclosed partial applicability.
The easiest way to do this is through Chrome or Opera (my examples will use Chrome) using the Console. Enter the following code into the console (generally in 1 line):
var l = document.getElementById('testLink');
for(var i=0; i<5; i++){
l.click();
}
This will generate the required result
.click() does not work with Android (look at mozilla docs, at mobile section). You can trigger the click event with this method:
function fireClick(node){
if (document.createEvent) {
var evt = document.createEvent('MouseEvents');
evt.initEvent('click', true, false);
node.dispatchEvent(evt);
} else if (document.createEventObject) {
node.fireEvent('onclick') ;
} else if (typeof node.onclick == 'function') {
node.onclick();
}
}
From this post
Use a testing framework
This might be helpful - http://seleniumhq.org/ - Selenium is a web application automated testing system.
You can create tests using the Firefox plugin Selenium IDE
Manual firing of events
To manually fire events the correct way you will need to use different methods for different browsers - either el.dispatchEvent or el.fireEvent where el will be your Anchor element. I believe both of these will require constructing an Event object to pass in.
The alternative, not entirely correct, quick-and-dirty way would be this:
var el = document.getElementById('anchorelementid');
el.onclick(); // Not entirely correct because your event handler will be called
// without an Event object parameter.
IE9+
function triggerEvent(el, type){
var e = document.createEvent('HTMLEvents');
e.initEvent(type, false, true);
el.dispatchEvent(e);
}
Usage example:
var el = document.querySelector('input[type="text"]');
triggerEvent(el, 'mousedown');
Source: https://plainjs.com/javascript/events/trigger-an-event-11/
Please call trigger function any where and button will click.
<a href="#" id="myBtn" title="" >Button click </a>
function trigger(){
document.getElementById("myBtn").click();
}
Fair warning:
element.onclick() does not behave as expected. It only runs the code within onclick="" attribute, but does not trigger default behavior.
I had similar issue with radio button not setting to checked, even though onclick custom function was running fine. Had to add radio.checked = "true"; to set it. Probably the same goes and for other elements (after a.onclick() there should be also window.location.href = "url";)

How do I change the value of a boolean property of an object in an array?

I'm super new to coding in general and I'm learning javascript to start with so forgive me if I have the wrong idea about anything. I've done a pretty good job so far at teaching myself basics but I've hit a wall trying to figure out how to detect multiple keypresses at once. I've seen a couple of apporaches to this but it looks like the general idea is to flag keypresses on 'keydown' and 'keyup'. The problem is, I cant figure out how to make my keyBoard['w'].pressed = true.
const keyBoard = {
w: {pressed: false}
}
document.addEventListener('keyDown', (e) => {
keyBoard[e.key].pressed = true
});
if(keyBoard['w'].pressed){
alert("success!")
}
This is a very simple test case I made to get my head around the logic and through trial and error I know the issue is with making my w.pressed be true.
I know I need to add a 'keyup' listener to set it to false but for now all I need to know how to do is what's described. Thank you!
You just need to put your IF statement inside your listener function - AND use 'keydown' not 'keyDown'. Also, I added a line which adds each key pressed into your object.
const keyBoard = {
w: {
pressed: false
}
}
document.addEventListener('keydown', (e) => {
keyBoard[e.key] = keyBoard[e.key] ? ? {}
keyBoard[e.key].pressed = true;
console.log(keyBoard)
if (keyBoard['w'].pressed) {
console.log("success!")
}
});
First things first, the event you are using has a typo. The proper name is keydown, not keyDown. You can read about it here Document: keydown event
Now moving into you code:
This is not way it is not working, but this will help you in the future. The function for the event lister is looking for a key e.key in your object keyBoard without declaring them first. In JavaScript this is not a problem if your JSON does not have the key, then it will be added. The problem here is that you are trying to add a value of true to an attribute pressed on a key that does not exist. This will raise an error
Cannot set properties of null (setting 'pressed')
To see this, press F12 to open the console in your browser.
Why? Well as mentioned before, the key can be added to object keyBoard, but the type of that key is not defined! when you do keyBoard[e.key].pressed = true you are telling the interpreter that the object keyBoard has an object of the value e.key which is also an object and it has a key called pressed. You need to specify that whatever is going into keyBoard[e.key] is an object. You can solve this as follows:
document.addEventListener('keydown', (e) => {
keyBoard[e.key] = keyBoard[e.key] == undefined ? {} : keyBoard[e.key]
keyBoard[e.key].pressed = true
});
Adding that extra line will specify that keyBoard[e.key] is an object and it will allow you to assign the value of true to the attribute pressed.
Now moving to the actual solution:
Everything in your script gets executed right when the page is loading. This means that your if statement will never be true because you will press any key after load, in other words, after the script has already finished. If you want an alert to pop every time w is pressed then you need to add that as part of the functionality of the event you already created. The event will stay alive even after the script finished listening for keystrokes and performing the code you wrote in it. Here is a way to get what you want. This will popup an alert every time w is pressed.
document.addEventListener('keydown', (e) => {
if (e.key === 'w') {
alert("success")
}
});
Going step by step through three blocks of your code:
declaring a constant with an object
attaching event to the document
checking the state of the object
It seems to be ok, but attaching an event does not stop code execution until it happens. The main thread goes on so the keyBoard object is not changed in 3th step.
To get it worked it should check the variable in the loop, ie.
setInterval(() => {
if (keyBoard['w'].pressed){
alert("success!")
}
}, 10);
but it is not very needed in this case.
The proper way is to let your code to execute some method every time key is down or up:
const keyBoard = {
w: {pressed: false}
}
document.addEventListener('keydown', e => {
keyBoard[e.key] ||= {};
keyBoard[e.key].pressed = true
doImportantStuff();
});
document.addEventListener('keyup', e => {
keyBoard[e.key] ||= {};
keyBoard[e.key].pressed = false; // or better `delete keyBoard[e.key];`
doImportantStuff();
});
function doImportantStuff() {
console.log(Object.entries(keyBoard));
}
If you want to keep ONLY pressed state for each key your code could be a bit simpler if you omit pressed property:
const keyBoard = {}
document.addEventListener('keydown', e => {
keyBoard[e.key] = true
doImportantStuff();
});
document.addEventListener('keyup', e => {
keyBoard[e.key] = false; // or better `delete keyBoard[e.key];
doImportantStuff();
});
function doImportantStuff() {
console.log(Object.keys(keyBoard).filter(key => keyBoard[key]));
// console.log(Object.keys(keyBoard)); if you delete keys from keyBoard when they up
}

How do you force your Javascript event to run first, regardless of the order in which the events were added?

I have Javascript that people are including in their page. In my Javascript I have a version of jQuery (1.8 for sake of easy reference) that is sectioned off into its own namespace, and referenced via a global variable (but not one of the two default vars of "$" or "jQuery"). This allows users to have jQuery in their page and have it not interfere with the stuff I'm doing internally in my functions.
So we have one page that has jQuery already (1.4), and everything works fine, except that the user and my code are both listening to "click" events on elements, and theirs is going first, so on the few events they do that return false, jQuery stops propagation and my event never gets triggered. I need my event to go first. The user is expecting my onClick functionality to still work.
Now I know that jQuery keeps its own order of events internally through the _data() object, and through this it is possible to unbind existing events, bind my event, then rebind the existing events, but that only applies to objects bound through that instance of jQuery. I'd rather not just blindly look for the jQuery object in hopes that the conflict was introduced by a user's own version of jQuery. After all what happens when a user binds the event not through jQuery? Trying to manipulate the existing jQuery object in the page isn't a good solution.
I know that, depending on browser, they are using addEventListener/removeEventListener or attachEvent/detachEvent. If only I could get a listing of the already added events, I could rebind them in the order I wanted, but I can't find out how. Looking through the DOM via Chrome inspect I don't see onclick bound anywhere (not on the object, not on window or document either).
I'm having the darndest time trying to figure out just exactly where jQuery binds its listening. To be able to control the order of its own events, jQuery must blanketly listen somewhere and then fire off its own functions right? If I could figure out where that's done I might get some insight into how to ensure my event is always first. Or maybe there's some Javascript API I haven't been able to find on Google.
Any suggestions?
We solved this by just adding a little jQuery extension that inserts events at the head of the event chain:
$.fn.bindFirst = function(name, fn) {
var elem, handlers, i, _len;
this.bind(name, fn);
for (i = 0, _len = this.length; i < _len; i++) {
elem = this[i];
handlers = jQuery._data(elem).events[name.split('.')[0]];
handlers.unshift(handlers.pop());
}
};
Then, to bind your event:
$(".foo").bindFirst("click", function() { /* Your handler */ });
Easy peasy!
As Bergi and Chris Heald said in the comments, it turns out there's no way to get at the existing events from the DOM, and no method to insert events "first". They are fired in the order they were inserted by design, and hidden by design. As a few posters mentioned you have access to the ones added through the same instance of jQuery that you're using via jQuery's data, but that's it.
There is one other case where you can run before an event that was bound before your code ran, and that's if they used the "onclick" HTML attribute. In that case you can write a wrapper function, as nothingisnecessary pointed out in a rather over-the-top toned comment below. While this wouldn't help in the instance of the original question I asked, and it's now very rare for events to be bound this way (most people and frameworks use addEvent or attachEventListener underneath now), it is one scenario in which you can solve the issue of "running first", and since a lot of people visit this question looking for answers now, I thought I'd make sure the answer is complete.
I encounter an opposite situation where I was asked to include a library, which uses event.stopImmediatePropagation() on an element, to our website. So some of my event handlers are skipped. Here is what I do (as answered here):
<span onclick="yourEventHandler(event)">Button</span>
Warning: this is not the recommended way to bind events, other developers may murder you for this.
Its not a proper solution, but ... You can add event handler to parent node in capture phase. Not on target element itself!
<div>
<div id="target"></div>
</div>
target.parentNode.addEventListener('click',()=>{console.log('parent capture phase handler')},true)
Third argument in addEventListener means:
true - capture phase
false - bubble phase
Helpful links:
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
https://javascript.info/bubbling-and-capturing
Found it easiest to add addListener and removeListener methods to document (as that's only where I need them - I suppose you can use Element.prototype and this instead). Only one "real" listener is added per type, and it's just a func to call the actual listeners in order. The eventListeners dictionary is added to document (so can mess with the handler or order).
[edit]
I think the correct answer for most cases is to use the 3rd argument of addEventListener: https://stackoverflow.com/a/29923421. The answer below ignores the argument (on purpose).
[edit] Updated code to only add one extra property: document.eventHandlers + modified naming.
// Storage.
document.eventListeners = {}; // { type: [ handlerFunc, listenerFuncs ] }
// Add event listener - returns index.
document.addListener = (type, listener, atIndex) => {
// Get info.
const listening = document.eventListeners[type];
// Add to existing.
if (listening) {
// Clean up.
atIndex = atIndex || 0;
const listeners = listening[1]; // Array of funcs.
// Already has.
const iExists = listeners.indexOf(listener);
if (iExists !== -1) {
// Nothing to do.
if (iExists === atIndex)
return atIndex;
// Remove from old position.
listeners.splice(atIndex, 1);
}
// Add (supporting one cycle of negatives).
const nListeners = listeners.length;
if (atIndex > nListeners)
atIndex = nListeners;
else if (atIndex < 0)
atIndex = Math.max(0, atIndex + nListeners + 1);
listeners.splice(atIndex, 0, listener);
}
// New one.
else {
// Handler func.
const handler = (...args) => {
const listening = document.eventListeners[type];
if (listening) {
const listeners = listening[1]; // Array of funcs.
for (const listener of listeners)
listener(...args);
}
};
// Update dictionary.
document.eventListeners[type] = [ handler, [ listener ] ];
// Add listener.
document.addEventListener(type, handler);
// First one.
atIndex = 0;
}
// Return index.
return atIndex;
};
// Remove event listener - returns index (-1 if not found).
document.removeListener = (type, listener) => {
// Get info.
const listening = document.eventListeners[type];
if (!listening)
return -1;
// Check if exists.
const listeners = listening[1];
const iExists = listeners.indexOf(listener);
if (iExists !== -1) {
// Remove listener.
listeners.splice(iExists, 1);
// If last one.
if (!listeners.length) {
// Remove listener.
const handlerFunc = listening[0];
document.removeEventListener(type, handlerFunc);
// Update dictionary.
delete document.eventListeners[type];
}
}
// Return index.
return iExists;
}
Aliaksei Pavlenkos suggestion about useCapture can be used. His allegation that it must be attached to the parent node is wrong: MDN
Event listeners in the “capturing” phase are called before event listeners in any non-capturing phases
target.addEventListener(type, listener, useCapture);
Just so it's said, I think this might be possible if you override the native implementations of these functions. This is BAD practice - very bad practice when developing a library to alter native implementations, because it can easily conflict with other libraries.
However, for completeness, here's one possibility (completely untested, just demonstrating the general concept):
// override createElement()
var temp = document.createElement;
document.createElement = function() {
// create element
var el = document.createElement.original.apply(document, arguments);
// override addEventListener()
el.addEventListenerOriginal = el.addEventListener;
el._my_stored_events = [];
// add custom functions
el.addEventListener = addEventListenerCustom;
el.addEventListenerFirst = addEventListenerFirst;
// ...
};
document.createElement.original = temp;
// define main event listeners
function myMainEventListeners(type) {
if (myMainEventListeners.all[type] === undefined) {
myMainEventListeners.all[type] = function() {
for (var i = 0; i < this._my_stored_events.length; i++) {
var event = this._my_stored_events[i];
if (event.type == type) {
event.listener.apply(this, arguments);
}
}
}
}
return myMainEventListeners.all[type];
}
myMainEventListeners.all = {};
// define functions to mess with the event list
function addEventListenerCustom(type, listener, useCapture, wantsUntrusted) {
// register handler in personal storage list
this._my_stored_events.push({
'type' : type,
'listener' : listener
});
// register custom event handler
if (this.type === undefined) {
this.type = myMainEventListeners(type);
}
}
function addEventListenerFirst(type, listener) {
// register handler in personal storage list
this._my_stored_events.push({
'type' : type,
'listener' : listener
});
// register custom event handler
if (this.type === undefined) {
this.type = myMainEventListeners(type);
}
}
// ...
A lot more work would need to be done in this regard to truly lock this down, and again, it's best not to modify native libraries. But it's a useful mental exercise that helps to demonstrate the flexibility JavaScript provides in solving problems like this.

What is the best way to handle multiple key events in Javascript?

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

How can I trigger a JavaScript event click

I have a hyperlink in my page. I am trying to automate a number of clicks on the hyperlink for testing purposes. Is there any way you can simulate 50 clicks on the hyperlink using JavaScript?
MSDN
I'm looking for onClick event trigger from the JavaScript.
Performing a single click on an HTML element: Simply do element.click(). Most major browsers support this.
To repeat the click more than once: Add an ID to the element to uniquely select it:
Google Chrome
and call the .click() method in your JavaScript code via a for loop:
var link = document.getElementById('my-link');
for(var i = 0; i < 50; i++)
link.click();
You should just use click. For more advanced event firing, use dispatchEvent.
const body = document.body;
body.addEventListener('click', e => {
console.log('clicked body');
});
console.log('Using click()');
body.click();
console.log('Using dispatchEvent');
body.dispatchEvent(new Event('click'));
Original Answer - Obsolete
Here is what I use for IE9+ http://jsfiddle.net/mendesjuan/rHMCy/4/
/**
* Fire an event handler to the specified node. Event handlers can detect that the event was fired programatically
* by testing for a 'synthetic=true' property on the event object
* #param {HTMLNode} node The node to fire the event handler on.
* #param {String} eventName The name of the event without the "on" (e.g., "focus")
*/
function fireEvent(node, eventName) {
// Make sure we use the ownerDocument from the provided node to avoid cross-window problems
var doc;
if (node.ownerDocument) {
doc = node.ownerDocument;
} else if (node.nodeType == 9){
// the node may be the document itself, nodeType 9 = DOCUMENT_NODE
doc = node;
} else {
throw new Error("Invalid node passed to fireEvent: " + node.id);
}
if (node.dispatchEvent) {
// Gecko-style approach (now the standard) takes more work
var eventClass = "";
// Different events have different event classes.
// If this switch statement can't map an eventName to an eventClass,
// the event firing is going to fail.
switch (eventName) {
case "click": // Dispatching of 'click' appears to not work correctly in Safari. Use 'mousedown' or 'mouseup' instead.
case "mousedown":
case "mouseup":
eventClass = "MouseEvents";
break;
case "focus":
case "change":
case "blur":
case "select":
eventClass = "HTMLEvents";
break;
default:
throw "fireEvent: Couldn't find an event class for event '" + eventName + "'.";
break;
}
var event = doc.createEvent(eventClass);
event.initEvent(eventName, true, true); // All events created as bubbling and cancelable.
event.synthetic = true; // allow detection of synthetic events
// The second parameter says go ahead with the default action
node.dispatchEvent(event, true);
} else if (node.fireEvent) {
// IE-old school style, you can drop this if you don't need to support IE8 and lower
var event = doc.createEventObject();
event.synthetic = true; // allow detection of synthetic events
node.fireEvent("on" + eventName, event);
}
};
Note that calling fireEvent(inputField, 'change'); does not mean it will actually change the input field. The typical use case for firing a change event is when you set a field programmatically and you want event handlers to be called since calling input.value="Something" won't trigger a change event.
What
l.onclick();
does is exactly calling the onclick function of l, that is, if you have set one with l.onclick = myFunction;. If you haven't set l.onclick, it does nothing. In contrast,
l.click();
simulates a click and fires all event handlers, whether added with l.addEventHandler('click', myFunction);, in HTML, or in any other way.
I'm quite ashamed that there are so many incorrect or undisclosed partial applicability.
The easiest way to do this is through Chrome or Opera (my examples will use Chrome) using the Console. Enter the following code into the console (generally in 1 line):
var l = document.getElementById('testLink');
for(var i=0; i<5; i++){
l.click();
}
This will generate the required result
.click() does not work with Android (look at mozilla docs, at mobile section). You can trigger the click event with this method:
function fireClick(node){
if (document.createEvent) {
var evt = document.createEvent('MouseEvents');
evt.initEvent('click', true, false);
node.dispatchEvent(evt);
} else if (document.createEventObject) {
node.fireEvent('onclick') ;
} else if (typeof node.onclick == 'function') {
node.onclick();
}
}
From this post
Use a testing framework
This might be helpful - http://seleniumhq.org/ - Selenium is a web application automated testing system.
You can create tests using the Firefox plugin Selenium IDE
Manual firing of events
To manually fire events the correct way you will need to use different methods for different browsers - either el.dispatchEvent or el.fireEvent where el will be your Anchor element. I believe both of these will require constructing an Event object to pass in.
The alternative, not entirely correct, quick-and-dirty way would be this:
var el = document.getElementById('anchorelementid');
el.onclick(); // Not entirely correct because your event handler will be called
// without an Event object parameter.
IE9+
function triggerEvent(el, type){
var e = document.createEvent('HTMLEvents');
e.initEvent(type, false, true);
el.dispatchEvent(e);
}
Usage example:
var el = document.querySelector('input[type="text"]');
triggerEvent(el, 'mousedown');
Source: https://plainjs.com/javascript/events/trigger-an-event-11/
Please call trigger function any where and button will click.
<a href="#" id="myBtn" title="" >Button click </a>
function trigger(){
document.getElementById("myBtn").click();
}
Fair warning:
element.onclick() does not behave as expected. It only runs the code within onclick="" attribute, but does not trigger default behavior.
I had similar issue with radio button not setting to checked, even though onclick custom function was running fine. Had to add radio.checked = "true"; to set it. Probably the same goes and for other elements (after a.onclick() there should be also window.location.href = "url";)

Categories