I'm trying to automatize some tasks in JavaScript and I need to use a InputEvent, but when I use normal event, I'm getting event.isTrusted = false and my event is doing nothing. Here is my event code:
var event = new InputEvent('input', {
bubbles: true,
cancelable: false,
data: "a"
});
document.getElementById('email').dispatchEvent(event);
This code should put "a" into a textField with id "email", but when event.isTrusted = false, this code is doing nothing. I'm testing it in Chrome Developer Tools in Sources tab with Event Listener Breakpoints (I checked only keyboard>input breakpoint and it shows me all attributes of used event).
I checked all attributes from real keyboard click and only thing that is different is event.isTrusted.
What can I change or what can I do to get event.isTrusted = true?
The isTrusted read-only property of the Event interface is a boolean that is true when the event was generated by a user action, and false when the event was created or modified by a script or dispatched via dispatchEvent.
Source: MDN
You may have misunderstood the concept of the Input Event, the event is triggered after the user type in the input. Manually triggering the event will not make the inputs change their values, is the changing of the values that makes the input trigger's the event not the opposite.
If you really want to change the value of the inputs with a custom event you can do something like this:
let TargetInput = document.getElementById('target')
let Button = document.getElementById('btnTrigger');
Button.addEventListener('click',function(e){
Trigger();
}, false);
TargetInput.addEventListener('input',function(e){
if(!e.isTrusted){
//Mannually triggered
this.value += e.data;
}
}, false);
function Trigger(){
var event = new InputEvent('input', {
bubbles: true,
cancelable: false,
data: "a"
});
TargetInput.dispatchEvent(event);
}
Target: <input type="text" id="target">
<hr>
<button id="btnTrigger">Trigger Event</button>
Any addEventListener call after the following code is executed will have isTrusted set to true.
Element.prototype._addEventListener = Element.prototype.addEventListener;
Element.prototype.addEventListener = function () {
let args = [...arguments]
let temp = args[1];
args[1] = function () {
let args2 = [...arguments];
args2[0] = Object.assign({}, args2[0])
args2[0].isTrusted = true;
return temp(...args2);
}
return this._addEventListener(...args);
}
Note: This is a very "hacky" way to go about doing this.
Unfortunately, you cannot generate event programmatically with isTrusted=true in Google Chrome and others modern browser.
See https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted
Pupeeteer might help. It generates trusted events.
In browsers, input events could be divided into two big groups:
trusted vs. untrusted.
Trusted events: events generated by users interacting with the page,
e.g. using a mouse or keyboard. Untrusted event: events generated by
Web APIs, e.g. document.createEvent or element.click() methods.
Websites can distinguish between these two groups:
using an Event.isTrusted event flag
sniffing for accompanying events.
For example, every trusted 'click' event is preceded by 'mousedown'
and 'mouseup' events.
For automation purposes it’s important to
generate trusted events. All input events generated with Puppeteer are
trusted and fire proper accompanying events.
I've found it's not possible to set isTrusted to true BUT, depending on your need, a potential solution would be to create a local override in DevTools and remove the conditional in code for the script. This will tell Chrome to use your version (with the isTrusted check removed) instead of the site's JS file.
Related
Is there a way I could identify the source file of a specific event?
My events are being removed, because the usage of document.open usage in the code. This is something can't change. I am trying to re-use my events, but because of another limitation I have, I need to know which events are coming from different JS files.
EventTarget.prototype.addEventListenerBase = EventTarget.prototype.addEventListener;
EventTarget.prototype.addEventListener = function(type,listener,params)
{
var isFromSourceX = "nameoffile.js"
var worker_events = ['DOMContentLoaded',
'beforeunload', "blue","devicemotion","deviceorientation",
"error","focus","load","message","orientationchange",
"resize","scroll","storage","click"];
var _this=this;
var _isEventExists = window._stackedListeners.filter(function(item){ return item.type==type && item.target==_this; })[0]===undefined?false:true;
var isHtmlElement = this instanceof HTMLElement;
if (worker_events.indexOf(type)>-1 && !_isEventExists && isFromSourceX) {
window._stackedListeners.push({
target: _this,
type: type,
listener: listener,
params: params
});
this.addEventListenerBase(type, listener, params);
}
};
})(self);
Eventually, I'll be pushing all needed events to an array to later attach them on the web page. But the problem as mentioned, is that I need to identify the source (to exclude external events) in the webpage.
p.s: I did not chose to work with document.open :)
Any ideas?
Thanks.
If you tend to use Chrome Dev Tools you will be able to see all the events associated with the specific type if you open the Source tab. On the right pane in Event listener breakpoints you could check the event category that you are interested in. By executing specific ones you will be able to see the source file.
It does not give you all the event listeners out of the box but it could be helpful if you are trying to diagnose your issue. You could also find event listeners on a dom node by inspecting it. On the right pane there should be a list of event listeners for it.
Hope I gave you some glimpse.
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 need to attach an Event called render to a panel element, that does nothing but being dispatched to warn all the listeners whenever panel is rendering.
Following the The old-fashioned way section of this link, I came up with this code:
/**
* **Static** Re-draw the layer panel to represent the current state of the layers.
* #param {Element} panel The DOM Element into which the layer tree will be rendered
*/
static renderPanel(panel) {
// Create the event.
var render_event = document.createEvent('Event');
// Define that the event name is 'render'.
render_event.initEvent('render', true, true);
// Listen for the event.
panel.addEventListener('render', function (e) {
// e.target matches panel
}, false);
panel.dispatchEvent(render_event);
This seems to have worked but as this is my first time doing this, I am not quite sure how to check the correctness of this method.
Looking inside the console I can see my panel element dispatching the render Event, but I'd like to ask if there's something I am missing or to be worried about before moving on.
To debug the result, I tried add an event listener to the document element like document.addEventListener("render",console.log("ciao")), which in turn printed ciao once in the console, but only just once.
I thought I would be able to see as many "ciao" in the console as the times the render Event was triggered, but this does not seem the case.
If you're trying to check everytime your event is fired, the second argument of addEventListener (taking into account what you're willing to achieve) should be a function callback using an event object as argument, like this for example:
document.addEventListener("render", function(e) { console.log("ciao"); });
In your example you're executing console.log("ciao"), not passing a function reference (anonymous or not), this is why it executes only one time: when the page loads/evaluates your script.
mdn guide on creating and dispatching custom events (same as your link)
The old fashioned method seems to still be working fine when I tried it, I saw the document event listener console log each time I triggered the event.
The updated way is:
panel.dispatchEvent(new CustomEvent('render'));
let div = document.querySelector('div');
div.addEventListener('old-event', () => {console.log('Old-fashinoed event caught')});
div.addEventListener('new-event', () => {console.log('New-fashioned event caught')});
let oldEvent = document.createEvent('Event');
oldEvent.initEvent('old-event', true, true);
let newEvent = new CustomEvent('new-event');
setInterval(() => {
div.dispatchEvent(oldEvent);
div.dispatchEvent(newEvent);
}, 1000);
<div>I emit an old-fashioned and a new-fashioned event every 1 second</div>
I'm trying to automatize some tasks in JavaScript and I need to use a InputEvent, but when I use normal event, I'm getting event.isTrusted = false and my event is doing nothing. Here is my event code:
var event = new InputEvent('input', {
bubbles: true,
cancelable: false,
data: "a"
});
document.getElementById('email').dispatchEvent(event);
This code should put "a" into a textField with id "email", but when event.isTrusted = false, this code is doing nothing. I'm testing it in Chrome Developer Tools in Sources tab with Event Listener Breakpoints (I checked only keyboard>input breakpoint and it shows me all attributes of used event).
I checked all attributes from real keyboard click and only thing that is different is event.isTrusted.
What can I change or what can I do to get event.isTrusted = true?
The isTrusted read-only property of the Event interface is a boolean that is true when the event was generated by a user action, and false when the event was created or modified by a script or dispatched via dispatchEvent.
Source: MDN
You may have misunderstood the concept of the Input Event, the event is triggered after the user type in the input. Manually triggering the event will not make the inputs change their values, is the changing of the values that makes the input trigger's the event not the opposite.
If you really want to change the value of the inputs with a custom event you can do something like this:
let TargetInput = document.getElementById('target')
let Button = document.getElementById('btnTrigger');
Button.addEventListener('click',function(e){
Trigger();
}, false);
TargetInput.addEventListener('input',function(e){
if(!e.isTrusted){
//Mannually triggered
this.value += e.data;
}
}, false);
function Trigger(){
var event = new InputEvent('input', {
bubbles: true,
cancelable: false,
data: "a"
});
TargetInput.dispatchEvent(event);
}
Target: <input type="text" id="target">
<hr>
<button id="btnTrigger">Trigger Event</button>
Any addEventListener call after the following code is executed will have isTrusted set to true.
Element.prototype._addEventListener = Element.prototype.addEventListener;
Element.prototype.addEventListener = function () {
let args = [...arguments]
let temp = args[1];
args[1] = function () {
let args2 = [...arguments];
args2[0] = Object.assign({}, args2[0])
args2[0].isTrusted = true;
return temp(...args2);
}
return this._addEventListener(...args);
}
Note: This is a very "hacky" way to go about doing this.
Unfortunately, you cannot generate event programmatically with isTrusted=true in Google Chrome and others modern browser.
See https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted
Pupeeteer might help. It generates trusted events.
In browsers, input events could be divided into two big groups:
trusted vs. untrusted.
Trusted events: events generated by users interacting with the page,
e.g. using a mouse or keyboard. Untrusted event: events generated by
Web APIs, e.g. document.createEvent or element.click() methods.
Websites can distinguish between these two groups:
using an Event.isTrusted event flag
sniffing for accompanying events.
For example, every trusted 'click' event is preceded by 'mousedown'
and 'mouseup' events.
For automation purposes it’s important to
generate trusted events. All input events generated with Puppeteer are
trusted and fire proper accompanying events.
I've found it's not possible to set isTrusted to true BUT, depending on your need, a potential solution would be to create a local override in DevTools and remove the conditional in code for the script. This will tell Chrome to use your version (with the isTrusted check removed) instead of the site's JS file.
I would like to write some tests for some input filtering code in a text box. For most tests, I can just call setValue and trigger the change event, which is easy to do. However, in this case, because I want to test that the input gets filtered out (or not), I can't just setValue() directly.
I tried dispatching keydown, keyup, keypress, textinput events. I can see that the handlers for them are being called, but the text doesn't actually show in the text box Note that this only "works" in Firefox, I understand the code would look different for other browsers.
function dispatch(target, eventType, charCode) {
var evt = document.createEvent("KeyboardEvent");
evt.initKeyEvent(
eventType,
true,
true,
window,
false,
false,
false,
false,
charCode,
0
);
target.dispatchEvent(evt);
}
var id = document.getElementById('id');
id.onkeydown = id.onkeyup = id.onkeypress = function() {console.log(arguments)}
dispatch(id, 'keydown', 65);
dispatch(id, 'keyup', 65);
dispatch(id, 'keypress', 65);
dispatch(id, 'textinput', 65);
// I can see the handlers were called but it doesn't display in the text box
I understand this has restrictions because we don't want web apps to just pretend like they are acting for the user. However, this is for testing my own application and I could launch Firefox with a specific profile and install plugins, or even write my own if I know it will help.
What I am after is to avoid using Selenium, I want to keep Java out of my JS tests because not only is it slow, but I have to re-implement a lot of the DOM querying in Java.
After all this, the question is, does anybody know how to get that code to actually modify the input? Tweaking settings, installing plugins?
List of questions that don't answer my question
Simulating user input for TDD JavaScript
Definitive way to trigger keypress events with jQuery
How to send a key to an input text field using Javascript?
Is it possible to simulate key press events programmatically?
I just found out that the following code does work in Chrome at least. No go in firefox or IE http://jsfiddle.net/D2s5T/14/
function dispatch(target, eventType, char) {
var evt = document.createEvent("TextEvent");
evt.initTextEvent (eventType, true, true, window, char, 0, "en-US");
target.focus();
target.dispatchEvent(evt);
}
dispatch(el, "textInput", "a");