I am writing some UI tests using Selenium and i have a JavaScript Tree control, using the Dojo toolkit.
I have implemented a context menu for each node of the tree using the examples that Dojo provide, but I need the Selenium test to "invoke" the right click on the tree node, but I cannot get this to work. The tests simply do not simulate the right-click event through JavaScript, and the context menu does not show up.
Has anyone had any experience in invoking the right click on a context menu using Dojo and Selenium? Or have any ideas as to how to do it?
try this instead, reason what things didn't quite work is that the context menu is in fact bound to the oncontextmenu event.
function contextMenuClick(element){
var evt = element.ownerDocument.createEvent('MouseEvents');
var RIGHT_CLICK_BUTTON_CODE = 2; // the same for FF and IE
evt.initMouseEvent('contextmenu', true, true,
element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
false, false, false, RIGHT_CLICK_BUTTON_CODE, null);
if (document.createEventObject){
// dispatch for IE
return element.fireEvent('onclick', evt)
}
else{
// dispatch for firefox + others
return !element.dispatchEvent(evt);
}
}
Just for good measure, here is a bit of doco on the parameters:
var myEvt = document.createEvent('MouseEvents');
myEvt.initMouseEvent(
'click' // event type
,true // can bubble?
,true // cancelable?
,window // the event's abstract view (should always be window)
,1 // mouse click count (or event "detail")
,100 // event's screen x coordinate
,200 // event's screen y coordinate
,100 // event's client x coordinate
,200 // event's client y coordinate
,false // whether or not CTRL was pressed during event
,false // whether or not ALT was pressed during event
,false // whether or not SHIFT was pressed during event
,false // whether or not the meta key was pressed during event
,1 // indicates which button (if any) caused the mouse event (1 = primary button)
,null // relatedTarget (only applicable for mouseover/mouseout events)
);
Great question!
I did some research, and it seems like you can fire a mouse event like is shown here, and make it a right-click by setting the button or which property to 2 (documented here).
Perhaps this code will work:
function rightClick(element){
var evt = element.ownerDocument.createEvent('MouseEvents');
var RIGHT_CLICK_BUTTON_CODE = 2; // the same for FF and IE
evt.initMouseEvent('click', true, true,
element.ownerDocument.defaultView, 1, 0, 0, 0, 0, false,
false, false, false, RIGHT_CLICK_BUTTON_CODE, null);
if (document.createEventObject){
// dispatch for IE
return element.fireEvent('onclick', evt)
}
else{
// dispatch for firefox + others
return !element.dispatchEvent(evt);
}
}
Here is a more correct version if you do not care about where the context menu gets fired up
function fireContextMenu(el) {
var evt = el.ownerDocument.createEvent("HTMLEvents")
evt.initEvent('contextmenu', true, true) // bubbles = true, cancelable = true
if (document.createEventObject) {
return el.fireEvent('oncontextmenu', evt)
}
else {
return !el.dispatchEvent(evt)
}
}
If you do, we may have to use the previous one, fix up it's behaviour in IE, and populate the screenX, screenY, clientX, clientY etc appropriately
I am trying this in firefox and chrome, but dispatching the contextmenu event doesn't make browser to open context menu. Event is triggered because my callback for oncontextmenu is fired, but context menu is still missing. Anybody have an idea, because I used all code samples from above?
Related
How does a website recognize keystrokes, mouse movement?
Is there a way to send a command ("like pressing down your left mouse button) via JavaScript, without actually pressing down your mouse button?
If my question is too unclear, I'm very happy to explain further. I'm new and just trying to start somewhere but am lost.
Can you recommend me some good learning material, so I can read into it, thank you very much.
Mouse, Keyboard, and other Events
Sites recognize keyboard and mouse 'events' by subscribing a function to them.
You can do that thru html like so: onkeypress="keypressFunction()", onmousemove="mousemoveFunction()", onclick="clickFunction()"... and other events
<div onclick="clickFunction()">Clickable</div>
Of course these functions keypressFunction(), mousemoveFunction(), clickFunction() need to exist somewhere in your site, whether inside
<script>
function clickFunction(){ alert('clicked!') }
</script>
or included from file: <script src="myscripts.js"></script> .
You can also subscribe to events using just javascript:
//Write `document` instead of `element` to apply to whole document
//Or you can find element by id like document.getElementById('id')
//You can of course use any other method of finding elements such
// as querySelector or use variables you already made before
element.onkeypress = function(eventArgs){
eventArgs = eventArgs || window.event;
// use eventArgs.keyCode to get which key
};
Or, more common and safe, subscribe with addEventListener:
element.addEventListener('keypress', function(eventArgs){
eventArgs = eventArgs || window.event;
// use eventArgs.keyCode to get which key
});
Note you dont have to write the prefix on in the event names (eg onkeypress) if using addEventListener.
You can of course also use already made functions:
element.onkeypress = myFunction;
and
element.addEventListener('keypress', myFunction);
All of these events usually pass an event-specific parameter to give more data about what exactly happened in the event.
For example, onclick passes MouseEvent args, so you can know where the mouse was (X and Y coords on screen) when the thing was clicked, were the alt/shift/ctrl keys held, and which mouse button was clicked (left, right, middle).
Keyboard events have their own event args with info on which keyboard key was pressed, if its being held, and so on. You can find all event arguments here.
Simulating events
Some basic events, such as a mouse click on an element, can be simulated with just element.click();, but that doesnt give you much control over the event args that are getting passed.
To properly send an event, you need to create a browser event object, and dispatch it on an element:
//Call oncontextmenu (right mouse click) on an element:
var element = document.getElementById('Id_here');
if (window.CustomEvent) {
element.dispatchEvent(new CustomEvent('contextmenu'));
} else if (document.createEvent) {
var ev = document.createEvent('HTMLEvents');
ev.initEvent('contextmenu', true, false);
element.dispatchEvent(ev);
} else { // Internet Explorer
element.fireEvent('oncontextmenu');
}
With that event object you can pass some data, here is simulating a keypress:
var element = document.getElementById('Id_here');
var keyboardEvent = document.createEvent("KeyboardEvent");
var initMethod = typeof keyboardEvent.initKeyboardEvent !== 'undefined' ? "initKeyboardEvent" : "initKeyEvent";
keyboardEvent[initMethod](
"keydown", // event type: keydown, keyup, keypress
true, // bubbles
true, // cancelable
window, // view: should be window
false, // ctrlKey
false, // altKey
false, // shiftKey
false, // metaKey
65, // keyCode: unsigned long - the virtual key code, else 0. 65 - 'a'
0 // charCode: unsigned long - the Unicode character associated with the depressed key, else 0
);
element.dispatchEvent(keyboardEvent);
Jquery gives some nice functions to make simulating events easier, but you can find those all over stack overflow, not even to mention google. Just search js simulating keypress/mouse, js subsribe to key/mouse event, and all the other things you can imagine.
I have an accepted answer but am open to better ones
Be gentle, this is my first question here
I am building a web browser plugin to automate a series of processes for my organization. I pretty much have everything handled but this one problem that I have been stuck on for two days... and its like really simple I think.
In a lookup field in our ERP, you must press Shift + Right to cycle through certain options.
I am attempting to trigger this or send this any kind of way that I can.
Jquery version 2.0.3
The required event only fires on key up
The required event is not firing when I simulate input
I suspect it needs to be targeted at the input, or perhaps its related to not being trusted/being simulated
It occurs to me, that as I am using a browser extension, perhaps it is something I can simulate from this? I dont know if thats a good way to put it... I wonder what the extension could do here that plain JS or JQ on a page could not.
Update(Dec 11): So per above thought, I am looking into modifying the Jquery framework that is being used. I have saved a local copy as an local override and used these two resources to implement.
Override Javascript file in chrome
https://www.ghacks.net/2018/02/27/make-permanent-changes-to-web-pages-with-chromes-overrides-dev-tool/
I am in process of determining if local overrides are persistent (Edit: they appear to be after restart of computer, lets see if its a long term solution) but I was able to console.log my code and see it in the console.
Next steps will be modifying the handler to perform the actions I need. and/or feed the information I want fed to the system.
Update(Dec 12): probably dont need to override the whole file with this answer how to override a javascript function
Here is my function that triggers the event handler (with no result) which was modified from here
function simulateKey (currentTarget, isTrusted, key, code, keyCode, type, modifiers) {
var evtName = (typeof(type) === "string") ? "key" + type : "keydown";
var modifier = (typeof(modifiers) === "object") ? modifier : {};
console.log("In simulate key function");
var event = document.createEvent("HTMLEvents");
event.initEvent(evtName, true, false);
event.keyCode = keyCode;
event.key = key;
event.code = code;
event.isTrusted = isTrusted;
event.Target = currentTarget;
for (var i in modifiers) {
event[i] = modifiers[i];
}
document.dispatchEvent(event);
}
Here is how I use it (a little hardcoded at the moment, just for testing purposes)
function changeLookup(Lookup) {
console.log("Change Lookup");
var event_object = Lookup;
console.log("Key Event Firing");
$("input[data-name='Entity.Customer.Key']").focus();
simulateKey(Lookup, true, "ArrowRight", "ArrowRight", 39, "up", {shiftKey: true });
console.log("Key Event Fired");
}
I have looked at or tried the following solutions
Trigger a built in event in javascript?
Arrow key pressed while shift key is held down
Is it possible to simulate key press events programmatically?
How to trigger event in JavaScript?
Execute Key Press Event
Firing a Keyboard Event in Safari, using JavaScript
I have reviewed the following documentation
https://developer.mozilla.org/en-US/docs/Web/API/KeyboardEvent
https://developer.mozilla.org/en-US/docs/Web/API/Event
https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/dispatchEvent
I am at my wits end here...
The answer was found here Keydown Simulation in Chrome fires normally but not the correct key
I modified it to keyup and juggled some of the parameters so the shift key was pressed... Seems like OP in the other thread did some strange stuff with the parameters in such a way as to press ctrl and shift... So I fixed that for the initKeyboardEvent but not for initKeyEvent
keyUpEventSim = {};
keyUpEventSim.keyup = function(target, k, ctrlKey, altKey, shiftKey, metaKey) {
var oEvent = document.createEvent('KeyboardEvent');
// Chromium Hack
Object.defineProperty(oEvent, 'keyCode', {
get : function() {
return this.keyCodeVal;
}
});
Object.defineProperty(oEvent, 'which', {
get : function() {
return this.keyCodeVal;
}
});
if (oEvent.initKeyboardEvent) {
oEvent.initKeyboardEvent("keyup", true, false, document.defaultView, false, false, ctrlKey, altKey, shiftKey, metaKey);
} else {
oEvent.initKeyEvent("keyup", true, false, document.defaultView, false, false, true, false, k, 0);
}
oEvent.keyCodeVal = k;
if (oEvent.keyCode !== k) {
alert("keyCode mismatch " + oEvent.keyCode + "(" + oEvent.which + ")");
}
target.dispatchEvent(oEvent);
}
called it as follows
function changeLookup(Lookup) {
Lookup.focus();
keyUpEventSim.keyup(Lookup,39, false, false, true, false);
}
Before starting this question, i have to say I really searched everywhere to find the answer but nothing found. Also tried manythings like dispatchevent, postmessage, ... but nothing worked.
Here is the case.
I have a main windows, where I have 4 simple buttons, like downarrow, uparrow, left and right arrow. I want to create a simulation of events pass to the iframe which is in this main window.
In that iframe is a page loaded where is an Eventhandler and react on the arrows.
I tried following but did not worked
var event = document.createEvent('KeyboardEvent'); // create a key event define the event
event.initKeyboardEvent("keypress", // typeArg,
true, // canBubbleArg,
true, // cancelableArg,
null, // viewArg, Specifies UIEvent.view. This value may be null.
false, // ctrlKeyArg,
false, // altKeyArg,
false, // shiftKeyArg,
false, // metaKeyArg,
39, // keyCodeArg (39 is the right arrow key ),
0); // charCodeArg);
document.getElementById('vid').dispatchEvent(event);
Is there anybody who has an idea how I can solve this issue?
Use postMessage. It works perfectly.
document.getElementById('vid').contentWindow.postMessage(event);
Finally I have sorted out the issue. I have used parent.document on my iframe to catch the events from the parnet side and create them again on iframe and it works great!
you want something like this:
var iframe = document.getElementById('something');
var iframeEvent = new Event('iframe-keypress');
document.addEventListener('keypress', function (e) {
iframe.dispatchEvent(iframeEvent);
});
iframe.addEventListener('iframe-keypress', function (e) {
console.log(e);
});
listen for the event on the document then pass down a custom event to the iframe.
jsfiddle - http://jsfiddle.net/rfkqe64j/
This works, but with jQuery.
window.addEventListener("keydown", (evt) => {
const {type, key} = evt;
parent.$("body").trigger(parent.$.Event(type, {key}));
})
Interestingly, parent.$.Event(evt) directly doesn't work.
I think this jquery forum thread should help
https://forum.jquery.com/topic/how-to-pass-mouse-events-to-an-iframe
Does anybody know of a method to trigger an event in Prototype, as you can with jQuery's trigger function?
I have bound an event listener using the observe method, but I would also like to be able to fire the event programatically.
event.simulate.js fits your needs.
I've used this several times and it works like a charm. It allows you to manually trigger native events, such as click or hover like so:
$('foo').simulate('click');
The great thing about this is that all attached event handlers will still be executed, just as if you would have clicked the element yourself.
For custom events you can use the standard prototype method Event.fire().
I don't think there is one built in to Prototype, but you can use this (not tested but should at least get you in the right direction):
Element.prototype.triggerEvent = function(eventName)
{
if (document.createEvent)
{
var evt = document.createEvent('HTMLEvents');
evt.initEvent(eventName, true, true);
return this.dispatchEvent(evt);
}
if (this.fireEvent)
return this.fireEvent('on' + eventName);
}
$('foo').triggerEvent('mouseover');
I found this post helpful... http://jehiah.cz/archive/firing-javascript-events-properly
It covers a way to fire events in both Firefox and IE.
function fireEvent(element,event){
if (document.createEventObject){
// dispatch for IE
var evt = document.createEventObject();
return element.fireEvent('on'+event,evt)
}
else{
// dispatch for firefox + others
var evt = document.createEvent("HTMLEvents");
evt.initEvent(event, true, true ); // event type,bubbling,cancelable
return !element.dispatchEvent(evt);
}
}
The answers here are true for "Normal" events, that is events which are defined by the User Agent, but for custom events you should use prototype's "fire" method. e.g.
$('something').observe('my:custom', function() { alert('Custom'); });
.
.
$('something').fire('my:custom'); // This will cause the alert to display
I'm writing web application which should support both mouse and touch interactions.
For testing I use touch screen device with Windows 7. I've tried to sniff touch events in latest Firefox and Chrome canary and got the following results:
On touch Firefox fires touch and corresponding mouse event.
Chrome fires touchstart/mousedown, touchend/mouseup pairs, but mousemove fired in very strange manner: one/two times while touchmove.
All mouse events handled as always.
Is there any way to handle mouse and touch evens simultaneously on modern touch screens? If Firefox fires a pair of touch and mouse event what happens on touchmove with mousemove in Chrome? Should I translate all mouse events to touch or vice versa? I hope to find right way to create responsive interface.
You can't really predict in advance which events to listen for (eg. for all you know a USB touch screen could get plugged in after your page has loaded).
Instead, you should always listen to both the touch events and mouse events, but call preventDefault() on the touch events you handle to prevent (now redundant) mouse events from being fired for them. See http://www.html5rocks.com/en/mobile/touchandmouse/ for details.
You should rather check availability of touch interface and bind events according to that.
You can do something like this:
(function () {
if ('ontouchstart' in window) {
window.Evt = {
PUSH : 'touchstart',
MOVE : 'touchmove',
RELEASE : 'touchend'
};
} else {
window.Evt = {
PUSH : 'mousedown',
MOVE : 'mousemove',
RELEASE : 'mouseup'
};
}
}());
// and then...
document.getElementById('mydiv').addEventListener(Evt.PUSH, myStartDragHandler, false);
If you want to handle both in same time and browser does not translate well touch events into mouse events, you can catch touch events and stop them - then corresponding mouse event shouldn't be fired by browser (you won't have double events) and you can fire it yourself as mouse event or just handle it.
var mydiv = document.getElementsById('mydiv');
mydiv.addEventListener('mousemove', myMoveHandler, false);
mydiv.addEventListener('touchmove', function (e) {
// stop touch event
e.stopPropagation();
e.preventDefault();
// translate to mouse event
var clkEvt = document.createEvent('MouseEvent');
clkEvt.initMouseEvent('mousemove', true, true, window, e.detail,
e.touches[0].screenX, e.touches[0].screenY,
e.touches[0].clientX, e.touches[0].clientY,
false, false, false, false,
0, null);
mydiv.dispatchEvent(clkEvt);
// or just handle touch event
myMoveHandler(e);
}, false);
The solutions on this thread are outdated - for those (like me) who still land here in 2021, there is a new W3 specification for pointer events. These events combine mouse and touch into one.
https://developer.mozilla.org/en-US/docs/Web/API/Pointer_events
https://www.w3.org/TR/pointerevents/
MouseEvents and TouchEvents do not technically provide exactly the same functionality, but for most purposes , they can be used interchangeably. This solution does not favor one over the other, as the user may have both a mouse and a touch screen. Instead, it allows the user to use which ever input device they wish, as long as they wait at least five seconds before changing inputs. This solution ignores mouse pointer emulation on touchscreen devices when the screen is tapped.
var lastEvent = 3 ;
var MOUSE_EVENT = 1;
var TOUCH_EVENT = 2 ;
element.addEventListener('touchstart', function(event)
{
if (lastEvent === MOUSE_EVENT )
{
var time = Date.now() - eventTime ;
if ( time > 5000 )
{
eventTime = Date.now() ;
lastEvent = TOUCH_EVENT ;
interactionStart(event) ;
}
}
else
{
lastEvent = TOUCH_EVENT ; ;
eventTime = Date.now() ;
interactionStart(event) ;
}
}) ;
element.addEventListener('mousedown', function(event)
{
if (lastEvent === TOUCH_EVENT )
{
var time = Date.now() - eventTime ;
if ( time > 5000 )
{
eventTime = Date.now() ;
lastEvent = MOUSE_EVENT ;
interactionStart(event) ;
}
}
else
{
lastEvent= MOUSE_EVENT ;
eventTime = Date.now() ;
interactionStart(event) ;
}
}) ;
function interactionStart(event) // handle interaction (touch or click ) here.
{...}
This is by no means a win all solution, I have used this a few times , and have not found problems with it, but to be fair i usually just use it to start animation when a canvas it tapped , or to provide logic to turn a div into a button. I leave it to you all to use this code , find improvements and help to improve this code.(If you do not find a better solution ).
I found this thread because I have a similar & more complex problem:
supposing we create a js enabled scrollable area with arrows NEXT/PREVIOUS which we want not only to respond to touch and mouse events but also to fire them repeatedly while the user continues to press the screen or hold down his/her mouse!
Repetition of events would make my next button to advance 2 positions instead one!
With the help of closures everything seems possible:
(1) First create a self invoking function for variable isolation:
(function(myScroll, $, window, document, undefined){
...
}(window.myScroll = window.myScroll || {}, jQuery, window, document));
(2) Then, add your private variables that will hold internal state from setTimeout():
/*
* Primary events for handlers that respond to more than one event and devices
* that produce more than one, like touch devices.
* The first event in browser's queue hinders all subsequent for the specific
* key intended to be used by a handler.
* Every key points to an object '{primary: <event type>}'.
*/
var eventLock = {};
// Process ids based on keys.
var pids = {};
// Some defaults
var defaults = {
pressDelay: 100 // ms between successive calls for continuous press by mouse or touch
}
(3) The event lock functions:
function getEventLock(evt, key){
if(typeof(eventLock[key]) == 'undefined'){
eventLock[key] = {};
eventLock[key].primary = evt.type;
return true;
}
if(evt.type == eventLock[key].primary)
return true;
else
return false;
}
function primaryEventLock(evt, key){
eventLock[key].primary = evt.type;
}
(4) Attach your event handlers:
function init(){
$('sth').off('mousedown touchstart', previousStart).on('mousedown touchstart', previousStart);
$('sth').off('mouseup touchend', previousEnd).on('mouseup touchend', previousEnd);
// similar for 'next*' handlers
}
Firing of events mousedown and touchstart will produce double calls for handlers on devices that support both (probably touch fires first). The same applies to mouseup and touchend.
We know that input devices (whole graphic environments actually) produce events sequentially so we don't care which fires first as long a special key is set at private eventLock.next.primary and eventLock.previous.primary for the first events captured from handlers next*() and previous*() respectively.
That key is the event type so that the second, third etc. event are always losers, they don't acquire the lock with the help of the lock functions eventLock() and primaryEventLock().
(5) The above can be seen at the definition of the event handlers:
function previousStart(evt){
// 'race' condition/repetition between 'mousedown' and 'touchstart'
if(!getEventLock(evt, 'previous'))
return;
// a. !!!you have to implement this!!!
previous(evt.target);
// b. emulate successive events of this type
pids.previous = setTimeout(closure, defaults.pressDelay);
// internal function repeats steps (a), (b)
function closure(){
previous(evt.target);
primaryEventLock(evt, 'previous');
pids.previous = setTimeout(closure, defaults.pressDelay);
}
};
function previousEnd(evt){
clearTimeout(pids.previous);
};
Similar for nextStart and nextEnd.
The idea is that whoever comes after the first (touch or mouse) does not acquire a lock with the help of function eventLock(evt, key) and stops there.
The only way to open this lock is to fire the termination event handlers *End() at step (4): previousEnd and nextEnd.
I also handle the problem of touch devices attached in the middle of the session with a very smart way: I noticed that a continuous press longer than defaults.pressDelay produces successive calls of the callback function only for the primary event at that time (the reason is that no end event handler terminates the callabck)!
touchstart event
closure
closure
....
touchend event
I define primary the device the user is using so, all you have to do is just press longer and immediately your device becomes primary with the help of primaryEventLock(evt, 'previous') inside the closure!
Also, note that the time it takes to execute previous(event.target) should be smaller than defaults.pressDelay.
(6) Finally, let's expose init() to the global scope:
myScroll.init = init;
You should replace the call to previous(event.target) with the problem at hand: fiddle.
Also, note that at (5b) there is a solution to another popular question how do we pass arguments to a function called from setTimeout(), i.e. setTimeout(previous, defaults.pressDelay) lacks an argument passing mechanism.
I have been using this jQuery helper to bind both touch and click events.
(function ($) {
$.fn.tclick = function (onclick) {
this.bind("touchstart", function (e) { onclick.call(this, e); e.stopPropagation(); e.preventDefault(); });
this.bind("click", function (e) { onclick.call(this, e); }); //substitute mousedown event for exact same result as touchstart
return this;
};
})(jQuery);