<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Simulating DOM Events</title>
<link rel="stylesheet" href="http://fonts.googleapis.com/css?family=PT+Sans:400,700,400italic,700italic">
<link rel="stylesheet" href="../../build/cssgrids/cssgrids-min.css">
<link rel="stylesheet" href="../assets/css/main.css">
<link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
<link rel="shortcut icon" type="image/png" href="../assets/favicon.png">
<script src="../../build/yui/yui-min.js"></script>
</head>
<body>
<!--
<a href="https://github.com/yui/yui3"><img style="position: absolute; top: 0; right: 0; border: 0;" src="https://s3.amazonaws.com/github/ribbons/forkme_right_darkblue_121621.png" alt="Fork me on GitHub"></a>
-->
<div id="doc">
<div id="hd">
<h1><img src="http://yuilibrary.com/img/yui-logo.png"></h1>
</div>
<a href="#toc" class="jump">Jump to Table of Contents</a>
<h1>Simulating DOM Events</h1>
<div class="yui3-g">
<div class="yui3-u-3-4">
<div id="main">
<div class="content"><div class="intro">
<p>
When creating automated tests for your application or modules, you need
to be able to mock user events. The DOM supports creating native events
that behave essentially the same as user generated events, though
without the associated browser default behaviors (e.g. following a link
on click).
</p>
<p>
The <code>event-simulate</code> module adds the <code>Y.Event.simulate</code> method for
working with raw DOM nodes, but for most cases, the
<code>node-event-simulate</code> module is the right choice, since it allows you
to call the <code>simulate</code> method directly from the <code>Node</code>.
</p>
</div>
<h2 id="simulating-mouse-events">Simulating Mouse Events</h2>
<p>
There are seven mouse events that can be simulated:
</p>
<ul>
<li><code>click</code></li>
<li><code>dblclick</code></li>
<li><code>mousedown</code></li>
<li><code>mouseup</code></li>
<li><code>mouseover</code></li>
<li><code>mouseout</code></li>
<li><code>mousemove</code></li>
</ul>
<p>
Each event is fired by calling <code>simulate()</code> and passing in two
arguments: the type of event to fire and an optional object specifying
additional information for the event. To simulate a click on the document's
body element, for example, the following code can be used:
</p>
<pre class="code prettyprint">YUI().use('node-event-simulate', function(Y) {
Y.one("body").simulate("click");
});</pre>
<p>
This code simulates a click with all of the default properties on the
<code>event</code> object. To specify additional information, such as the
Shift key being down, the second argument must be used and the exact DOM
name for the event property specified (there is browser-normalizing logic
that translates these into browser-specific properties when necessary):
</p>
<pre class="code prettyprint">Y.one("body").simulate("click", { shiftKey: true });</pre>
<p>
In this updated example, a click event is fired on the document's body
while simulating that the Shift key is down.
</p>
<p>
The extra properties to specify vary depending on the event being simulated
and are limited to this list:
</p>
<dl>
<dt><code>detail</code></dt>
<dd>
Indicates the number of times a button was clicked (DOM-compliant
browsers only).
</dd>
<dt><code>screenX</code>, <code>screenY</code></dt>
<dd>
Coordinates of the mouse event in relation to the entire screen
(DOM-compliant browsers only).
</dd>
<dt><code>clientX</code>, <code>clientY</code></dt>
<dd>
Coordinates of the mouse event in relation to the browser client
area.
</dd>
<dt><code>ctrlKey</code>, <code>altKey</code>, <code>shiftKey</code>, <code>metaKey</code></dt>
<dd>
The state of the Ctrl, Alt, Shift, and Meta keys, respectively
(true for down, false for up).
</dd>
<dt><code>button</code></dt>
<dd>
The button being used for the event, 0 for left (default), 1 for
right, 2 for center.
</dd>
<dt><code>relatedTarget</code></dt>
<dd>
the element the mouse moved from (during a <code>mouseover</code> event) or to
(during a <code>mouseout</code> event).
</dd>
</dl>
<pre class="code prettyprint">YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myDiv");
//simulate a click Alt key down
node.simulate("click", { altKey: true});
//simulate a double click with Ctrl key down
node.simulate("dblclick", { ctrlKey: true });
//simulate a mouse over
node.simulate("mouseover", { relatedTarget: document.body });
//simulate a mouse out
node.simulate("mouseout", { relatedTarget: document.body });
//simulate a mouse down at point (100,100) in the client area
node.simulate("mousedown", { clientX: 100, clientY: 100 });
//simulate a mouse up at point (100,100) in the client area
node.simulate("mouseup", { clientX: 100, clientY: 100 });
//simulate a mouse move at point (200, 200) in the client area
node.simulate("mousemove", { clientX: 200, clientY: 200 });
});</pre>
<h2 id="simulating-key-events">Simulating Key Events</h2>
<p>There are three key event simulations available:</p>
<ul>
<li><code>keyup</code></li>
<li><code>keydown</code></li>
<li><code>keypress</code></li>
</ul>
<p>
As with the mouse events, key events are simulated using
<code>simulate()</code>. For <code>keyup</code> and <code>keydown</code>,
the <code>keyCode</code> property must be specified; for
<code>keypress</code>, the <code>charCode</code> property must be included.
In many cases, <code>keyCode</code> and <code>charCode</code> may be the
same value to represent the same key (97, for instance, represents the
"A" key as well as being the ASCII code for the letter
"a"). For example:
</p>
<pre class="code prettyprint">YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myDiv");
//simulate a keydown on the A key
node.simulate("keydown", { keyCode: 97 });
//simulate a keyup on the A key
node.simulate("keyup", { keyCode: 97 });
//simulate typing "a"
node.simulate("keypress", { charCode: 97 });
});</pre>
<p>
Key events also support the <code>ctrlKey</code>, <code>altKey</code>,
<code>shiftKey</code>, and <code>metaKey</code> event properties.
</p>
<p>
<strong>Note:</strong> Due to differences in browser implementations, key
events may not be simulated in the same manner across all browsers. For
instance, when simulating a keypress event on a textbox, only Firefox will
update the textbox with the new character of the key that was simulated to
be pressed. For other browsers, the events are still registered and all
event handlers are called, however, the textbox display and
<code>value</code> property are not updated. These differences should go
away as browser support for simulated events improves in the future.
</p>
<h2 id="simulating-ui-events">Simulating UI Events</h2>
<p>There are several UI event simulations available:</p>
<ul>
<li><code>blur</code></li>
<li><code>change</code></li>
<li><code>focus</code></li>
<li><code>resize</code></li>
<li><code>scroll</code></li>
<li><code>select</code></li>
</ul>
<p>
As with the other events, UI events are simulated using
<code>simulate()</code>. There are no properties that are required to
simulate UI events as these events don't carry extra information. Some
examples:
</p>
<pre class="code prettyprint">YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myInput");
//simulate a change event
node.simulate("change");
//simulate a select event
node.simulate("select");
});</pre>
<h2 id="simulating-touch-gestures">Simulating Touch Gestures</h2>
<p>
There are several high level gesture simulations primarily targeted for
smartphones, tablets, and other touch-enabled devices. Single touch gestures
such as <code>tap</code> and <code>flick</code> are simulated using Mouse Events on desktop or mobile
devices where creating Touch Events are not supported. All gesture simulations
are done by calling the <code>simulateGesture()</code> method against a Node instance. The
method is asynchronous by default so an optional callback function can be
passed.
</p>
<dl>
<dt><code>tap</code></dt>
<dd>
Single finger gesture to simulate a tap. Default is to simulate
one tap but it can be configured to simulate any number of taps.
</dd>
<dt><code>doubletap</code></dt>
<dd>
Single finger gesture to simulate double taps.
</dd>
<dt><code>press</code></dt>
<dd>
Single finger gesture to simulate a long press.
</dd>
<dt><code>move</code></dt>
<dd>
Single finger gesture to simulate a move. It simulates moving
one finger straight in any direction.
</dd>
<dt><code>flick</code></dt>
<dd>
Single finger gesture to simulate the flick gesture. It simulates
moving one finger with a certain velocity along either an X or Y
axis.
</dd>
<dt><code>pinch</code></dt>
<dd>
Two finger gesture to simulate pinch and spread gestures.
</dd>
<dt><code>rotate</code></dt>
<dd>
Two finger gesture to simulate a rotate.
</dd>
</dl>
<p>
Gesture can be simulated by calling <code>simulateGesture()</code> and
passing the following arguments: the name of the gesture to simulate, an
optional object to define gesture properties, and an optional callback function.
The available properties vary per gesture.
</p>
<p>If the location of the finger is not given, the center of the node element is
used by default. This default behavior can be overridden by passing coordinates
into the optional object. The coordinate values are relative to the top/left
corner of the node element.
</p>
<h3 id="single-touch-gestures-tap-double-tab-and-press">Single Touch Gestures: Tap, Double Tab and Press</h3>
<p>
The following code simulates tap, double tap and press gestures:
</p>
<pre class="code prettyprint">YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myElement");
//simulate tap gesture
node.simulateGesture("tap");
//simulate double-tap gesture
node.simulateGesture("doubletap");
//simulate press gesture
node.simulateGesture("press", {
hold: 3000 // press and hold for 3000ms
});
// simulate tap with options and callback
node.simulateGesture("tap", {
point: [30, 30], // tap (30, 30) relative to the top/left of the node
hold: 3000, // hold for 3sec in a tap
times: 2, // tap 2 times
delay: 500 // delay time before next tap starts
}, function() {
Y.log("I was called.");
});
});</pre>
<h4 id="valid-options">Valid Options</h4>
<p>
Optional properties for the <code>tap</code> gesture:
</p>
<dl>
<dt><code>point</code></dt>
<dd>
Indicates the [x,y] coordinates where the tap should be
simulated. Default is the center of the node element.
</dd>
<dt><code>hold</code></dt>
<dd>
The hold time in milliseconds. This is the time between
<code>touchstart</code> and <code>touchend</code> event generation.
</dd>
<dt><code>times</code></dt>
<dd>
Indicates the number of taps. Default is 1.
</dd>
<dt><code>delay</code></dt>
<dd>
The number of milliseconds before the next tap simulation
happens. This is valid only when <code>times</code> is more than 1.
</dd>
</dl>
<p>
Optional properties for the <code>doubletap</code> gesture:
</p>
<dl>
<dt><code>point</code></dt>
<dd>
Indicates the [x, y] coordinates where the doubletap should be
simulated. Default is the center of the node element.
</dd>
</dl>
<p>
The <code>press</code> gesture is essentially a single tap with the <code>hold</code> property
defined. Optional properties for the <code>press</code> gesture:
</p>
<dl>
<dt><code>point</code></dt>
<dd>
Indicates the [x, y] coordinates where the tap should be
simulated. Default is the center of the node element.
</dd>
<dt><code>hold</code></dt>
<dd>
The hold time in milliseconds. This is the time between
<code>touchstart</code> and <code>touchend</code> event generation.
</dd>
</dl>
<h3 id="single-touch-gestures-move-and-flick">Single Touch Gestures: Move and Flick</h3>
<p>
The following code can be used To simulate various gestures of moving one
finger:
</p>
<pre class="code prettyprint">YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myElement");
//simulate moving a finger 200 pixels along the x-axis
//to the right for one second (default)
node.simulateGesture("move");
//simulate moving a finger from the center of the node
//to a point 70 pixels to the right and 50 pixels down over 2000ms
node.simulateGesture("move", {
path: {
xdist: 70,
ydist: -50
} ,
duration: 2000
});
//simulate a flick to the right (default)
node.simulateGesture("flick");
//simulate a flick down 100 pixels over 50ms
node.simulateGesture("flick", {
axis: y
distance: -100
duration: 50
});
});</pre>
<h4 id="valid-options2">Valid Options</h4>
<p>
Optional properties for the <code>move</code> gesture:
</p>
<dl>
<dt><code>path</code></dt>
<dd>
Indicates the path of the finger movement. It's an object with three
optional properties: <code>point</code>, <code>xdist</code> and <code>ydist</code>. The <code>point</code> is
the start point and defaults to the center of the node element.
<code>xdist</code> and <code>ydist</code> indicate the distance moved in pixels along the
x- and y-axis. A negative distance value indicates moving to left
for <code>xdist</code> and down for <code>ydist</code>.
</dd>
<dt><code>duration</code></dt>
<dd>
The duration of the gesture in milliseconds.
</dd>
</dl>
<p>
Optional properties for the <code>flick</code> gesture:
</p>
<dl>
<dt><code>point</code></dt>
<dd>
Indicates the [x, y] coordinates where the flick should be
simulated. Default is the center of the node element.
</dd>
<dt><code>axis</code></dt>
<dd>
Valid values are either "x" or "y". Indicates moving axis. The flick
moves in only 4 directions (left, right, up and down).
</dd>
<dt><code>distance</code></dt>
<dd>
Distance to move (in pixels).
</dd>
<dt><code>duration</code></dt>
<dd>
The duration of the gesture in milliseconds. This value may be
adjusted if it is below the minimum velocity to be a flick gesture.
</dd>
</dl>
<h3 id="two-finger-gestures-pinch-and-rotate">Two Finger Gestures: Pinch and Rotate</h3>
<p>
The <code>pinch</code> gesture is used to simulate the pinching and spreading of two
fingers. During a pinch simulation, rotation is also possible. Essentially
<code>pinch</code> and <code>rotate</code> simulations share the same base implementation to allow
both pinching and rotation at the same time. The only difference is <code>pinch</code>
requires <code>start</code> and <code>end</code> option properties while <code>rotate</code> requires <code>rotation</code>
option property.
</p>
<p>
The <code>pinch</code> and <code>rotate</code> gestures can be described as placing 2 fingers along a
circle. Pinching and spreading can be described by start and end circles while
rotation occurs on a single circle. If the radius of the start circle is greater
than the end circle, the gesture becomes a pinch, otherwise it is a spread spread.
</p>
<p>
The following code can be used to simulate two finger gestures:
</p>
<pre class="code prettyprint">YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myElement");
//simulate a pinch: "r1" and "r2" are required
node.simulateGesture("pinch", {
r1: 100, // start circle radius at the center of the node
r2: 50 // end circle radius at the center of the node
});
//simulate a spread: same as "pinch" gesture but r2>r1
node.simulateGesture("pinch", {
r1: 50,
r2: 100
});
//simulate rotating a node 75 degrees counter-clockwise
node.simulateGesture("rotate", {
rotation: -75
});
//simulate a pinch and a rotation at the same time.
//fingers start on a circle of radius 100 px, placed at top/bottom
//fingers end on a circle of radius 50px, placed at right/left
node.simulateGesture("pinch", {
r1: 100,
r2: 50,
start: 0
rotation: 90
});
});</pre>
<h4 id="valid-options3">Valid Options</h4>
<p>
The optional properties available for <code>pinch</code> and <code>rotate</code> gestures are the
same:
</p>
<dl>
<dt><code>center</code></dt>
<dd>
The center of the circle where the two fingers are placed. Default
is the center of the node element.
</dd>
<dt><code>r1</code></dt>
<dd>
Required for <code>pinch</code> gestures but optional for <code>rotate</code>. Pixel radius
of the start circle. If omitted in <code>rotate</code> gestures, default is
a fourth of the node element width or height, whichever is smaller.
</dd>
<dt><code>r2</code></dt>
<dd>
Required for <code>pinch</code> gestures but optional for <code>rotate</code> gestures.
Pixel radius of the end circle. If omitted in <code>rotate</code> gestures,
default is a fourth of the node element width or height, whichever
is smaller.
</dd>
<dt><code>duration</code></dt>
<dd>
The duration of the gesture in milliseconds.
</dd>
<dt><code>start</code></dt>
<dd>
Start degree of the first finger for the rotation gesture. Default
is 0 (i.e., 12:00 on a clock).
</dd>
<dt><code>rotation</code></dt>
<dd>
Degrees to rotate from the start degree. Negative value means
rotation of counter-clockwise direction.
</dd>
</dl>
<h3 id="gesture-simulation-on-ios">Gesture Simulation on iOS</h3>
<p>
If the gesture simulation is called in iOS, it generates not only touch events
but also <a href="http://developer.apple.com/library/safari/#documentation/UserExperience/Reference/GestureEventClassReference/GestureEvent/GestureEvent.html">
iOS specific gesture events</a>: <code>gesturestart</code>, <code>gesturechange</code>
and <code>gestureend</code>.
</p>
<h3 id="customizing-default-gesture-properties">Customizing Default Gesture Properties</h3>
<p>
You can define custom default behaviors for gesture simulations by modifying the
following <code>Y.GestureSimulation.defaults</code> object properties:
</p>
<ul>
<li>HOLD_TAP</li>
<li>DELAY_TAP</li>
<li>HOLD_PRESS</li>
<li>MIN_HOLD_PRESS</li>
<li>MAX_HOLD_PRESS</li>
<li>DISTANCE_MOVE</li>
<li>DURATION_MOVE</li>
<li>MAX_DURATION_MOVE</li>
<li>MIN_VELOCITY_FLICK</li>
<li>DISTANCE_FLICK</li>
<li>DURATION_FLICK</li>
<li>MAX_DURATION_FLICK</li>
<li>DURATION_PINCH</li>
</ul>
<p>
And an example:
</p>
<pre class="code prettyprint">YUI().use('node-event-simulate', function(Y) {
var node = Y.one("#myElement");
Y.GestureSimulation.defaults = Y.merge(Y.GestureSimulation.defaults, {
HOLD_TAP: 3000
});
//now touchend event will be generated after 3 sec from the touchstart
//event generation.
node.simulateGesture("tap");
});</pre>
<h2 id="caveats-and-coming-soons">Caveats and Coming Soons</h2>
<h3 id="faking">Don't use simulation in user facing code</h3>
<p>
Event simulation is for automated testing. Your application should respond
to real user events. For reasons
<a href="#only-what-you-ask-for">mentioned below</a>, it can be easy to get
your application into a confused runtime state when some callbacks have
been executed but not others.
</p>
<p>
Typically, event simulation is sought to trigger certain callbacks. If a
function needs to respond to user action or be called programmatically, it
should be written accordingly and called directly in the latter case.
Often a better solution is to extract the core logic from the event handler
into a separate function and call that method from the event handler and
from the other part of the application that was going to use simulation.
</p>
<p>
In some cases, simulation is wanted because there may be any number of
subscriptions on a node, and all applicable callbacks should be triggered.
If this is the case, investigate using <a
href="../event-custom/index.html">custom events</a>, instead.
</p>
<p>
The bottom line is, reliance on event simulation in production code is a
warning sign that the architecture is not scaling. The affected code
should be refactored before it becomes a larger problem.
</p>
<h3 id="only-what-you-ask-for">Only what you ask for</h3>
<p>
In many cases, events happen in groups (<code>mousedown</code>, <code>mouseup</code>, <code>click</code>, or
<code>keydown</code>, <code>keyup</code>, <code>keypress</code>). If you simulate an event that is
typically part of a group or is often followed by other events, <em>the
other events will NOT be generated</em> for free.
</p>
<p>
For example, if you simulate a <code>click</code> event on a submit button, you only
simulate the <code>click</code> event. The preceding <code>mousedown</code> and <code>mouseup</code>, as
well as the subsequently expected 'submit' are neither simulated or fired
natively.
</p>
<h3 id="no-synthetic-event-simulation-yet">No synthetic event simulation yet</h3>
<p>
The <a href="synths.html">Synthetic event system</a> doesn't yet support
defining simulation. In most cases, though, synthetic events are triggered
by other DOM events that can be simulated, so it's often possible to
trigger them by simulating the underlying events. But that ignores the
point that synthetic events are supposed to mask that abstraction for your
benefit.
</p>
<p>
Support for synthetic event simulation is on the roadmap.
</p>
</div>
</div>
</div>
<div class="yui3-u-1-4">
<div class="sidebar">
<div id="toc" class="sidebox">
<div class="hd">
<h2 class="no-toc">Table of Contents</h2>
</div>
<div class="bd">
<ul class="toc">
<li>
<a href="#simulating-mouse-events">Simulating Mouse Events</a>
</li>
<li>
<a href="#simulating-key-events">Simulating Key Events</a>
</li>
<li>
<a href="#simulating-ui-events">Simulating UI Events</a>
</li>
<li>
<a href="#simulating-touch-gestures">Simulating Touch Gestures</a>
<ul class="toc">
<li>
<a href="#single-touch-gestures-tap-double-tab-and-press">Single Touch Gestures: Tap, Double Tab and Press</a>
<ul class="toc">
<li>
<a href="#valid-options">Valid Options</a>
</li>
</ul>
</li>
<li>
<a href="#single-touch-gestures-move-and-flick">Single Touch Gestures: Move and Flick</a>
<ul class="toc">
<li>
<a href="#valid-options2">Valid Options</a>
</li>
</ul>
</li>
<li>
<a href="#two-finger-gestures-pinch-and-rotate">Two Finger Gestures: Pinch and Rotate</a>
<ul class="toc">
<li>
<a href="#valid-options3">Valid Options</a>
</li>
</ul>
</li>
<li>
<a href="#gesture-simulation-on-ios">Gesture Simulation on iOS</a>
</li>
<li>
<a href="#customizing-default-gesture-properties">Customizing Default Gesture Properties</a>
</li>
</ul>
</li>
<li>
<a href="#caveats-and-coming-soons">Caveats and Coming Soons</a>
<ul class="toc">
<li>
<a href="#faking">Don't use simulation in user facing code</a>
</li>
<li>
<a href="#only-what-you-ask-for">Only what you ask for</a>
</li>
<li>
<a href="#no-synthetic-event-simulation-yet">No synthetic event simulation yet</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
<div class="sidebox">
<div class="hd">
<h2 class="no-toc">Examples</h2>
</div>
<div class="bd">
<ul class="examples">
<li data-description="Use the Event Utility to attach simple DOM event handlers.">
<a href="basic-example.html">Simple DOM Events</a>
</li>
<li data-description="Using the synthetic event API to create a DOM event that fires in response to arrow keys being pressed.">
<a href="synth-example.html">Creating an Arrow Event for DOM Subscription</a>
</li>
<li data-description="Supporting cross-device swipe gestures, using the event-move gesture events">
<a href="swipe-example.html">Supporting A Swipe Left Gesture</a>
</li>
</ul>
</div>
</div>
<div class="sidebox">
<div class="hd">
<h2 class="no-toc">Examples That Use This Component</h2>
</div>
<div class="bd">
<ul class="examples">
<li data-description="Creating an accessible menu button using the Focus Manager Node Plugin, Event's delegation support and mouseenter event, along with the Overlay widget and Node's support for the WAI-ARIA Roles and States.">
<a href="../node-focusmanager/node-focusmanager-button.html">Accessible Menu Button</a>
</li>
<li data-description="Shows how to extend the base widget class, to create your own Widgets.">
<a href="../widget/widget-extend.html">Extending the Base Widget Class</a>
</li>
<li data-description="Example Photo Browser application.">
<a href="../dd/photo-browser.html">Photo Browser</a>
</li>
<li data-description="Portal style example using Drag & Drop Event Bubbling and Animation.">
<a href="../dd/portal-drag.html">Portal Style Example</a>
</li>
<li data-description="Use IO to request data over HTTP.">
<a href="../io/get.html">HTTP GET to request data</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="../assets/vendor/prettify/prettify-min.js"></script>
<script>prettyPrint();</script>
<script>
YUI.Env.Tests = {
examples: [],
project: '../assets',
assets: '../assets/event',
name: 'event',
title: 'Simulating DOM Events',
newWindow: '',
auto: false
};
YUI.Env.Tests.examples.push('basic-example');
YUI.Env.Tests.examples.push('synth-example');
YUI.Env.Tests.examples.push('swipe-example');
YUI.Env.Tests.examples.push('node-focusmanager-button');
YUI.Env.Tests.examples.push('widget-extend');
YUI.Env.Tests.examples.push('photo-browser');
YUI.Env.Tests.examples.push('portal-drag');
YUI.Env.Tests.examples.push('get');
</script>
<script src="../assets/yui/test-runner.js"></script>
</body>
</html>