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
I work on Chrome extension, i need to update lot of inputs of an html page made with React from numbers readed from CSV. I cannot update the web site.
-
An example of input copied from the rendered website :
<td><input class="input input_small fpInput" value="29,4"></td>
-
How it's made (not sure 100% about that, had to read the uglified js source)
{
key: "render",
value: function () {
return s.a.createElement("input", {
className: "input input_small fpInput",
value: this.state.value,
onChange: this.handleChange,
onBlur: this.handleSubmit,
onFocus: this.handleFocus
})
}
}
-
Each time you change the input value a function is called and a POST is made to save it.
I want to trigger the onBlur() or onChange() from my extension after i changed the input value to trigger the POST
I tried this :
var el = document. ... .querySelector('input'); // the selector is simplied of course
el.value = 321;
el.onChange(); // ERROR onChange is not a function
el.onchange(); // ERROR onchange is not a function
el.handleChange(); // ERROR handleChange is not a function
Any idea please ?
You can't call a React component's method directly from the DOM element it has rendered. You need to trigger an event that bubbles up so that React can catch it at the document level and process it normally, as it would do with a real one.
✨ Document.execCommand():
As pointed out by #woxxom in the comments, the easiest way to do that might be to focus the inputs and then use Document.execCommand():
const input1 = document.getElementById('input1');
const input2 = document.getElementById('input2');
input1.focus();
document.execCommand('insertText', false, 'input1');
input2.focus();
document.execCommand('insertText', false, 'input2');
<input id="input1" />
<input id="input2" />
⚒️ Manually Dispatching Events:
Otherwise, you might try manually dispatching a change, input and/or blur event using the Event() constructor in those fields after you change their value.
Also, note Event()'s second optional argument contains a field, bubbles, that is false by default. You need that one to be true. Otherwise, this won't work, as React is listening for events on the document.
Additionally, you might need to use Element.setAttribute() too, as that will update the value attribute on the DOM (the initial value on the field), while element.value will update the current value (and so the display value). Usually, though, it's not needed. For more on this see What is the difference between properties and attributes in HTML?.
This approach might have some timing issues you might need to handle using setTimeout when updating multiple inputs, so if the first one works, I'd go for that one instead.
const input1 = document.getElementById('input1');
// This updates the value displayed on the input, but not the DOM (you can do that with setAttribute,
// but it's not needed here):
input1.value = 'input1';
// Dispatch an "input" event. In some cases, "change" would also work:
input1.dispatchEvent(new Event('input', { bubbles: true }));
// It looks like only one field will be updated if both events are dispatched
// straight away, so you could use a setTimeout here:
setTimeout(() => {
const input2 = document.getElementById('input2');
input2.value = 'input2';
input2.dispatchEvent(new Event('input', { bubbles: true }));
});
<input id="input1" />
<input id="input2" />
To elaborate a bit more on #varoons answer, which is factually correct albeit a bit short on explanation.
You can do so by injecting (dispatching, in browser terms) the event into the dom:
// Needs setAttribute to work well with React and everything, just `.value` doesn't cut it
// Also changed it to a string, as all attributes are strings (even for <input type="number" />)
el.setAttribute("value", "321");
// As #wOxxOm pointed out, we need to pass `{ bubbles: true }` to the options,
// as React listens on the document element and not the individual input elements
el.dispatchEvent(new Event("change", { bubbles: true }));
el.dispatchEvent(new Event("blur", { bubbles: true }));
This will actually call all the listeners, even those made with React (as is the case in your de-uglyfied code ;)) or made with simple element.onChange = () => {...} listeners.
Example: https://codesandbox.io/s/kml7m2nn4r
el.dispatchEvent(new CustomEvent("change"))
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.
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 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");