Programmatically dispatch keyboard (e.g. keypress) events using JavaScript - javascript

I am trying to dispatch events programatically for keyboard and mouse events.
So far there are few things I have tried. Right now I am able to dispatch mouse events programatically and see the changes:
const element = document.getElementById("element");
const event = new Event('click', {bubbles: true});
element.dispatchEvent(event);
Above method is working fine for Mouse Event. And I have tried following method for keyboard events:
const element = document.getElementById("input-element");
const event = new KeyboardEvent('keypress', {'key': 'e'});
element.dispatchEvent(event);
It seems here that the event is being executed, but the values are not being updated in the input field.

There are a number of events that occur when you press a key. None of them result in an input value being changed, but you can use this function to approximate what happens when a key is pressed.
const element = document.getElementById("input-element");
const key = 'e';
var success = triggerKey(key, element);
console.log(success?'success':'fail');
function triggerKey(key, element){
if(!/^[a-z]$/.test(key)) return false;
if(!['INPUT','TEXTAREA'].includes(element.tagName)) return false;
const events = ['keydown', 'keypress', 'textInput', 'keyup'];
events.forEach(event_name=>{
const opts = 'textInput' === event_name ? {
inputType: 'insertText',
data: key
} : {
key: key,
code: `Key${key.toUpperCase()}`
};
const event = 'textInput' === event_name ?
new InputEvent('input', opts) :
new KeyboardEvent(event_name, opts);
element.dispatchEvent(event);
if('textInput' === event_name) element.value += key;
});
return true;
}
<input id="input-element">

Related

`e.getModifierState is not a function` error on triggered keyup event

I am trying to detect whether the client has their CapsLock enabled by temporarily creating a hidden input field, triggering a keyup event on that input, and then checking the boolean value of e.getModifierState('CapsLock').
The function I've written is below, which currently throws the error Uncaught TypeError: e.getModifierState is not a function on the line which includes e.getModifierState and I'm struggling to diagnose why this is occurring. I'm open to any help here!
let checkCaps = () => {
let tempInput = document.createElement('input');
let keyupEvent = new Event('keyup');
tempInput.type = 'hidden';
tempInput.id = 'check-caps';
let capsOn = false;
tempInput.addEventListener('keyup', e => {
e.which = 65;
capsOn = e.getModifierState('CapsLock');
})
tempInput.dispatchEvent(keyupEvent);
tempInput.remove();
return capsOn;
}
checkCaps(); // testing the function
The class Event is the base class for all specific event classes.
If you want to generate a keyboard event, then you must use the KeyboardEvent class like this:
let checkCaps = () => {
let tempInput = document.createElement('input');
let keyupEvent = new KeyboardEvent('keyup');
tempInput.type = 'hidden';
tempInput.id = 'check-caps';
let capsOn = false;
tempInput.addEventListener('keyup', e => {
e.which = 65;
capsOn = e.getModifierState('CapsLock');
})
tempInput.dispatchEvent(keyupEvent);
tempInput.remove();
return capsOn;
}
checkCaps(); // testing the function
If tested this and the code runs, but I always get false for getModifierState('CapsLock')
It seems that for you must specify the current value self-generated KeyboardEvents. But this isn't what you want.
It seems that other people had the same issue and there is no solution without real user input.
See: How to detect Caps Lock state on page load (that is, not on keypress) with JavaScript?

is there a single event object for both keydown & keyup

We would like to raise a flag variable within keydown event object, and capture it within keyup is this possible to do so without using an outside variable?
Example :
var keydown = e => {
e.flag = true;
}
var keyup = e => {
e.flag // undefined
}
document.addEventListener('keydown',keydown);
document.addEventListener('keyup',keyup);
The problem is that it seems not to be the same event being fired for both keyup and keydown...
we know we can...
var flag = false;
var keydown = e => {
flag = true;
}
var keyup = e => {
if( flag ) // do something
}
Both this seems less clean rather then using the event itself...
is there anyway to get a single event object for both keydown / keyup?

Is there there an initial keypress event in JavaScript? [duplicate]

I want to have a onkeydown event fire a function only once. for that function to fire again, the user has to release the key and press/hold again.
I know its fairly simple but I'm new at JS. Also I prefer to avoid using jQuery or other libs.
One more thing, this should work for both ie and firefox.
I'm surprised it's not mentioned, there's also event.repeat:
document.addEventListener('keydown', (e) => {
if (e.repeat) return;
console.log(e.key);
});
This will only fire once per each keypress, since event.repeat turns true after holding the key down.
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent/key#keyboardevent_sequence
You could set a flag:
var fired = false;
element.onkeydown = function() {
if(!fired) {
fired = true;
// do something
}
};
element.onkeyup = function() {
fired = false;
};
Or unbind and rebind the event handler (might be better):
function keyHandler() {
this.onkeydown = null;
// do something
}
element.onkeydown = keyHandler;
element.onkeyup = function() {
this.onkeydown = keyHandler;
};
More information about "traditional" event handling.
You might also want to use addEventListener and attachEvent to bind the event handlers. For more information about that, have a look at quirksmode.org - Advanced event registration models.
There's a "once" parameter you can use
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
Eg:
element.addEventListener('keydown', function(event) {
doSomething()
}, {once: true});
It'll remove it as soon as it's been called.
Alternatively you can use removeEventListener if it's a named function
Here is a method that uses addEventListener and removeEventListener
var textBox = document.getElementById("textBox");
function oneKeyDown(){
$("body").append("<h1>KeyDown<h1>"); //just to show the keypress
textBox.removeEventListener('keydown', oneKeyDown, false);
}
function bindKeyDown(){
textBox.addEventListener('keydown', oneKeyDown, false);
}
textBox.addEventListener('keyup', bindKeyDown, false)
bindKeyDown();
Code example on jsfiddle.
One note, for IE you will need to use attachEvent, detachEvent.
Here you go:
test.onkeydown = function() {
if ( this.className === 'hold' ) { return false; }
this.className = 'hold';
// call your function here
};
test.onkeyup = function() {
this.className = '';
};
Live demo: http://jsfiddle.net/simevidas/xAReL/2/
JQuery's one will help you.
What it does is, bind the eventHandler to event, and when event occurs, it runs the eventHandler and unbinds it, so that its not fired at next event.
as stated in the other answers, there is no 'onkeyfirstdown' or similar event to listen for.
the best solution is to keep track of which keys are already down in a js-object:
var keysdown = {};
element.addEventListener('keydown', function(evt) {
if(!(evt.key in keysdown)) {
keysdown[evt.key] = true;
// key first pressed
}
});
element.addEventListener('keyup', function(evt) {
delete keysdown[evt.key];
});
this way, you will not be skipping 'keyfirstpressed' events if more than one key is held down.
(many of the other solutions posted here will only fire when no other keys are down).
Here is my solution that will only run the function you pass it when a key is FIRST pressed on the target (eg window or some input field). If the user wants to trigger a key again, they'll have to release it and press it again.
Vanilla JS
const onKeyPress = (func, target = window) => {
// persistent "store" to track what keys are being pressed
let pressed = {};
// whenever a keydown event is fired ontarget element
const onKeyDown = (event) => {
// if key isn't already pressed, run func
if (!pressed[event.which])
func(event);
// add key to store
pressed = { ...pressed, [event.which]: true };
};
// whenever a keyup event is fired on the window element
const onKeyUp = (event) => {
const { [event.which]: id, ...rest } = pressed;
// remove key from store
pressed = rest;
};
// add listeners
target.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
// return a function that can be called to remove listeners
return () => {
target.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
};
};
And then to use it:
const removeListener = onKeyPress((event) => console.log(event.which + ' key pressed'))
removeListener(); // when you want to remove listeners later
React and React Hooks
import { useState } from 'react';
import { useEffect } from 'react';
import { useCallback } from 'react';
export const useKeyPress = (func, target = window) => {
// persistent "store" to track what keys are being pressed
const [pressed, setPressed] = useState({});
// whenever a keydown event is fired ontarget element
const onKeyDown = useCallback(
(event) => {
// if key isn't already pressed, run func
if (!pressed[event.which])
func(event);
// add key to store
setPressed({ ...pressed, [event.which]: true });
},
[func, pressed]
);
// whenever a keyup event is fired on the window element
const onKeyUp = useCallback((event) => {
// remove key from store
const { [event.which]: id, ...rest } = pressed;
setPressed(rest);
}, [pressed]);
useEffect(() => {
// add listeners when component mounts/changes
target.addEventListener('keydown', onKeyDown);
window.addEventListener('keyup', onKeyUp);
// cleanup/remove listeners when component unmounts/changes
return () => {
target.removeEventListener('keydown', onKeyDown);
window.removeEventListener('keyup', onKeyUp);
};
}, [target, onKeyDown, onKeyUp]);
};
And then to use it:
import { useKeyPress } from 'wherever';
useKeyPress((event) => console.log(event.which + ' key pressed'))

Remove All Event Listeners of Specific Type

I want to remove all event listeners of a specific type that were added using addEventListener(). All the resources I'm seeing are saying you need to do this:
elem.addEventListener('mousedown',specific_function);
elem.removeEventListener('mousedown',specific_function);
But I want to be able to clear it without knowing what it is currently, like this:
elem.addEventListener('mousedown',specific_function);
elem.removeEventListener('mousedown');
That is not possible without intercepting addEventListener calls and keep track of the listeners or use a library that allows such features unfortunately. It would have been if the listeners collection was accessible but the feature wasn't implemented.
The closest thing you can do is to remove all listeners by cloning the element, which will not clone the listeners collection.
Note: This will also remove listeners on element's children.
var el = document.getElementById('el-id'),
elClone = el.cloneNode(true);
el.parentNode.replaceChild(elClone, el);
If your only goal by removing the listeners is to stop them from running, you can add an event listener to the window capturing and canceling all events of the given type:
window.addEventListener(type, function(event) {
event.stopImmediatePropagation();
}, true);
Passing in true for the third parameter causes the event to be captured on the way down. Stopping propagation means that the event never reaches the listeners that are listening for it.
Keep in mind though that this has very limited use as you can't add new listeners for the given type (they will all be blocked). There are ways to get around this somewhat, e.g., by firing a new kind of event that only your listeners would know to listen for. Here is how you can do that:
window.addEventListener('click', function (event) {
// (note: not cross-browser)
var event2 = new CustomEvent('click2', {detail: {original: event}});
event.target.dispatchEvent(event2);
event.stopPropagation();
}, true);
element.addEventListener('click2', function(event) {
if (event.detail && event.detail.original) {
event = event.detail.original
}
// Do something with event
});
However, note that this may not work as well for fast events like mousemove, given that the re-dispatching of the event introduces a delay.
Better would be to just keep track of the listeners added in the first place, as outlined in Martin Wantke's answer, if you need to do this.
You must override EventTarget.prototype.addEventListener to build an trap function for logging all 'add listener' calls. Something like this:
var _listeners = [];
EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type, listener)
{
_listeners.push({target: this, type: type, listener: listener});
this.addEventListenerBase(type, listener);
};
Then you can build an EventTarget.prototype.removeEventListeners:
EventTarget.prototype.removeEventListeners = function(targetType)
{
for(var index = 0; index != _listeners.length; index++)
{
var item = _listeners[index];
var target = item.target;
var type = item.type;
var listener = item.listener;
if(target == this && type == targetType)
{
this.removeEventListener(type, listener);
}
}
}
In ES6 you can use a Symbol, to hide the original function and the list of all added listener directly in the instantiated object self.
(function()
{
let target = EventTarget.prototype;
let functionName = 'addEventListener';
let func = target[functionName];
let symbolHidden = Symbol('hidden');
function hidden(instance)
{
if(instance[symbolHidden] === undefined)
{
let area = {};
instance[symbolHidden] = area;
return area;
}
return instance[symbolHidden];
}
function listenersFrom(instance)
{
let area = hidden(instance);
if(!area.listeners) { area.listeners = []; }
return area.listeners;
}
target[functionName] = function(type, listener)
{
let listeners = listenersFrom(this);
listeners.push({ type, listener });
func.apply(this, [type, listener]);
};
target['removeEventListeners'] = function(targetType)
{
let self = this;
let listeners = listenersFrom(this);
let removed = [];
listeners.forEach(item =>
{
let type = item.type;
let listener = item.listener;
if(type == targetType)
{
self.removeEventListener(type, listener);
}
});
};
})();
You can test this code with this little snipper:
document.addEventListener("DOMContentLoaded", event => { console.log('event 1'); });
document.addEventListener("DOMContentLoaded", event => { console.log('event 2'); });
document.addEventListener("click", event => { console.log('click event'); });
document.dispatchEvent(new Event('DOMContentLoaded'));
document.removeEventListeners('DOMContentLoaded');
document.dispatchEvent(new Event('DOMContentLoaded'));
// click event still works, just do a click in the browser
Remove all listeners on a global event
element.onmousedown = null;
now you can go back to adding event listeners via
element.addEventListener('mousedown', handler, ...);
This solution only works on "Global" events. Custom events won't work. Here's a list of all global events: https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers
I know this is old, but I had a similar issue with no real answers, where I wanted to remove all keydown event listeners from the document. Instead of removing them, I override the addEventListener to ignore them before they were even added, similar to Toms answer above, by adding this before any other scripts are loaded:
<script type="text/javascript">
var current = document.addEventListener;
document.addEventListener = function (type, listener) {
if(type =="keydown")
{
//do nothing
}
else
{
var args = [];
args[0] = type;
args[1] = listener;
current.apply(this, args);
}
};
</script>
A modern way to remove event listeners without referencing the original function is to use AbortController. A caveat being that you can only abort the listeners that you added yourself.
const buttonOne = document.querySelector('#button-one');
const buttonTwo = document.querySelector('#button-two');
const abortController = new AbortController();
// Add multiple click event listeners to button one
buttonOne.addEventListener(
'click',
() => alert('First'),
{ signal: abortController.signal }
);
buttonOne.addEventListener(
'click',
() => alert('Second'),
{ signal: abortController.signal }
);
// Add listener to remove first button's listeners
buttonTwo.addEventListener(
'click',
() => abortController.abort()
);
<p>The first button will fire two alert dialogs when clicked. Click the second button to remove those listeners from the first button.</p>
<button type="button" id="button-one">Click for alerts</button>
<button type="button" id="button-two">Remove listeners</button>
Remove all listeners in element by one js line:
element.parentNode.innerHTML += '';
You cant remove a single event, but all? at once? just do
document.body.innerHTML = document.body.innerHTML
In the extreme case of not knowing which callback is attached to a window listener, an handler can be wrapper around window addEventListener and a variable can store ever listeners to properly remove each one of those through a removeAllEventListener('scroll') for example.
var listeners = {};
var originalEventListener = window.addEventListener;
window.addEventListener = function(type, fn, options) {
if (!listeners[type])
listeners[type] = [];
listeners[type].push(fn);
return originalEventListener(type, fn, options);
}
var removeAllEventListener = function(type) {
if (!listeners[type] || !listeners[type].length)
return;
for (let i = 0; i < listeners[type].length; i++)
window.removeEventListener(type, listeners[type][i]);
}
So this function gets rid of most of a specified listener type on an element:
function removeListenersFromElement(element, listenerType){
const listeners = getEventListeners(element)[listenerType];
let l = listeners.length;
for(let i = l-1; i >=0; i--){
removeEventListener(listenerType, listeners[i].listener);
}
}
There have been a few rare exceptions where one can't be removed for some reason.
You could alternatively overwrite the 'yourElement.addEventListener()' method and use the '.apply()' method to execute the listener like normal, but intercepting the function in the process. Like:
<script type="text/javascript">
var args = [];
var orginalAddEvent = yourElement.addEventListener;
yourElement.addEventListener = function() {
//console.log(arguments);
args[args.length] = arguments[0];
args[args.length] = arguments[1];
orginalAddEvent.apply(this, arguments);
};
function removeListeners() {
for(var n=0;n<args.length;n+=2) {
yourElement.removeEventListener(args[n], args[n+1]);
}
}
removeListeners();
</script>
This script must be run on page load or it might not intercept all event listeners.
Make sure to remove the 'removeListeners()' call before using.
var events = [event_1, event_2,event_3] // your events
//make a for loop of your events and remove them all in a single instance
for (let i in events){
canvas_1.removeEventListener("mousedown", events[i], false)
}

Why aren't my parameters getting passed through to a dispatched event?

I've set up an event listener like this...
window.addEventListener('message', parseMessage, false);
var parseMessage = function(rawMessage) {
console.log(rawMessage.cmd);
};
And then I'm triggering the event like this:
var event = new Event('message', {'cmd':"blerg!"});
window.dispatchEvent(event);
The problem is the console.log in parse message is logging out undefined when I am expecting to to log out "blerg!"
What I am I doing wrong here with the events, how to I pass the 'cmd' message through to the event?
Use CustomEvent instead of Event for creating custom events.
Specify your data in a 'details' object (see code).
I changed the event name because message is also used for the postMessage API. It didn't cause problems when running in Chrome, but I wouldn't use it.
var parseMessage = function(rawMessage) {
console.log(rawMessage);
console.log(rawMessage.detail.cmd);
};
// changed event name
window.addEventListener('myMessage', parseMessage, false);
// data should be in a 'details' object
var evt = new CustomEvent('myMessage', {
detail: {
'cmd' : "blerg!"
}
});
window.dispatchEvent(evt);
Here is an adjustment for IE >= 9 compatiblity (using document.createEvent() and CustomEvent::initCustomEvent()):
var evt = document.createEvent("CustomEvent");
evt.initCustomEvent('myMessage', false, false, {
'cmd': "blerg!"
});
For IE9/10 polyfill you can use this code provided by Mozilla:
https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent
(function () {
if (
typeof window.CustomEvent === "function" ||
// In Safari, typeof CustomEvent == 'object' but it otherwise works fine
this.CustomEvent.toString().indexOf('CustomEventConstructor')>-1
) { return; }
function CustomEvent ( event, params ) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
Also described here but with wrong URL:
https://stackoverflow.com/a/22946340/1736012

Categories