Skip to main content
Cypress App

trigger

Trigger any DOM event on an element, the window, or the document: mouse, keyboard, touch, pointer, drag and drop, or your app's own custom events. Use it when a handler listens for an event that action commands such as .click() and .type() don't fire.

Syntax​

.trigger(eventName)
.trigger(eventName, position)
.trigger(eventName, options)
.trigger(eventName, x, y)
.trigger(eventName, position, options)
.trigger(eventName, x, y, options)

Usage​

Correct Usage

cy.get('a').trigger('mousedown') // Trigger mousedown event on link

Incorrect Usage

cy.trigger('touchstart') // Errors, cannot be chained off 'cy'
cy.clock().trigger('mouseleave') // Errors, 'clock' does not yield DOM elements

Arguments​

eventName (String)

The name of the event to be triggered on the DOM element.

position (String)

The position where the event should be triggered. The center position is the default position. Valid positions are topLeft, top, topRight, left, center, right, bottomLeft, bottom, and bottomRight.

Diagram of the nine positions on an element's border box: topLeft, top, and topRight along the top edge; left, center, and right across the middle; and bottomLeft, bottom, and bottomRight along the bottom edge. Edge and corner positions sit just inside the outer edge of the border box, on the border when the element has one, and never in the margin. center is the default.

x (Number)

The distance in pixels from element's left to trigger the event.

y (Number)

The distance in pixels from element's top to trigger the event.

options (Object)

Pass in an options object to change the default behavior of .trigger().

OptionDefaultDescription
animationDistanceThresholdanimationDistanceThresholdThe distance in pixels an element must exceed over time to be considered animating.
bubblestrueWhether the event bubbles
cancelabletrueWhether the event is cancelable
eventConstructor'Event'The name of the constructor, as a string, that creates the event object (e.g. 'MouseEvent', 'KeyboardEvent'). It must exist on the application's window. See choosing an event constructor.
forcefalseForces the action, disables waiting for actionability
logtrueDisplays the command in the Command Log
scrollBehaviorscrollBehaviorViewport position to where an element should be scrolled before executing the command. Accepts a single alignment, a per-axis { block, inline } object, or false.
timeoutdefaultCommandTimeoutTime to wait for .trigger() to resolve before timing out
waitForAnimationswaitForAnimationsWhether to wait for elements to finish animating before executing the command.

You can also include arbitrary event properties (e.g. clientX, shiftKey). With the default Event constructor, they're attached to the event as is. With another constructor, the browser decides which ones it accepts. See choosing an event constructor.

When the subject is an element, .trigger() sets clientX, clientY, pageX, pageY, screenX, and screenY on the event from the position or x and y you pass. screenX and screenY carry the same viewport-relative values as clientX and clientY. Any of these properties you pass in options override the calculated ones.

clientX and clientY measure from the viewport, while pageX and pageY measure from the top of the document, so they differ once the page scrolls.

Yields ​

  • .trigger() yields the same subject it was given.
  • It is unsafe to chain further commands that rely on the subject after .trigger().

Examples​

Event Name​

Trigger a mouseover on the button​

The element must first reach an actionable state, such as being visible and not disabled.

cy.get('button').trigger('mouseover') // yields 'button'

Position​

Trigger a mousedown on the top right of a button​

cy.get('button').trigger('mousedown', 'topRight')

Click the trigger command in the Command Log to pin its snapshot. A red dot marks the point where the event fired, here the button's top-right corner.

Pinned trigger mousedown, topRight command with a red dot on the top-right corner of the highlighted Save button

Coordinates​

Specify explicit coordinates relative to the top left corner​

cy.get('button').trigger('mouseup', 15, 40)

Options​

Specify that the event should not bubble​

By default, the event bubbles up the DOM tree. Set bubbles: false to stop it at the element.

cy.get('button').trigger('mouseover', { bubbles: false })

A cy.spy() added as a listener on the document confirms the event stopped at the button.

const onDocumentMouseover = cy.spy().as('documentMouseover')
cy.document().then((doc) => {
doc.addEventListener('mouseover', onDocumentMouseover)
})

cy.get('button').trigger('mouseover', { bubbles: false })
cy.get('@documentMouseover').should('not.have.been.called')

Specify the exact clientX and clientY the event should have​

Passing clientX and clientY overrides the position .trigger() calculates from the element. Use it for events like mousemove, where the position needs to be outside the element.

cy.get('button').trigger('mousemove', { clientX: 200, clientY: 300 })

To check the properties a listener receives, add a cy.spy() as the listener and assert on its call.

const onMousemove = cy.spy().as('mousemove')
cy.get('button').then(($button) => {
$button[0].addEventListener('mousemove', onMousemove)
})

cy.get('button').trigger('mousemove', { clientX: 200, clientY: 300 })
cy.get('@mousemove').should('have.been.calledWithMatch', {
clientX: 200,
clientY: 300,
})

Force an event on a hidden element​

Custom controls often hide the native input they're built on. Without force, .trigger() waits for the input to become visible and fails when it times out. force: true skips the actionability checks and the scroll into view, and dispatches the event right away.

cy.get('[data-cy=volume-input]').then(($input) => {
$input[0].value = '60'
})
cy.get('[data-cy=volume-input]').trigger('change', { force: true })

cy.get('[data-cy=volume-label]').should('have.text', '60')
cy.get<HTMLInputElement>('[data-cy=volume-input]').then(($input) => {
$input[0].value = '60'
})
cy.get('[data-cy=volume-input]').trigger('change', { force: true })

cy.get('[data-cy=volume-label]').should('have.text', '60')

Forcing lets the test do something a user can't, so keep it for elements like this one, which receive events but are never visible.

Mouse Events​

Show and hide a tooltip on hover​

When a JavaScript handler shows the tooltip, trigger the events it listens for and assert on the result.

cy.get('[data-cy=help-icon]').trigger('mouseover')
cy.get('[data-cy=tooltip]').should('be.visible')

cy.get('[data-cy=help-icon]').trigger('mouseout')
cy.get('[data-cy=tooltip]').should('not.be.visible')

If the app listens for mouseenter and mouseleave instead, trigger those event names. A triggered event doesn't apply CSS :hover styles, so a tooltip shown only by a :hover rule stays hidden. See .hover() for workarounds.

Simulate a "long press" event​

Hold the press with mousedown, assert on what the long press shows, then release it with mouseup. The assertion retries until the menu appears, so the test holds the press only as long as the app needs, without a fixed cy.wait().

cy.get('.target').trigger('mousedown')
cy.get('.long-press-menu').should('be.visible')
cy.get('.target').trigger('mouseup')

Trigger a mousedown from a specific mouse button​

// Main button pressed (usually the left button)
cy.get('.target').trigger('mousedown', { button: 0 })
// Auxiliary button pressed (usually the middle button)
cy.get('.target').trigger('mousedown', { button: 1 })
// Secondary button pressed (usually the right button)
cy.get('.target').trigger('mousedown', { button: 2 })

Keyboard Events​

Open a command palette with a keyboard shortcut​

.trigger() fires one keyboard event with exactly the properties you pass, including modifier keys, without moving focus. Trigger it on the document to reach a global shortcut handler.

cy.document().trigger('keydown', { key: 'k', ctrlKey: true })
cy.get('[data-cy=command-palette]').should('be.visible')

Pass the properties the handler reads, such as key, code, or keyCode, along with ctrlKey, metaKey, shiftKey, or altKey.

To press a single key the way a user does, such as Escape to close a dialog, use cy.press() instead. It dispatches native key events, while events from .trigger() have isTrusted set to false, so a handler that checks isTrusted ignores them. cy.press() takes one key without modifiers. To type text, use .type().

cy.press(Cypress.Keyboard.Keys.ESC)
cy.get('[data-cy=dialog]').should('not.exist')

Change Events​

Interact with a range input (slider)​

To interact with a range input (slider), set its value property, then trigger the change event to signal it changed.

Some implementations listen for the input event instead, which fires as the user moves the slider.

cy.get('input[type=range]').then(($range) => {
$range[0].value = '25'
})
cy.get('input[type=range]').trigger('change')

cy.get('input[type=range]').siblings('p').should('have.text', '25')
cy.get<HTMLInputElement>('input[type=range]').then(($range) => {
$range[0].value = '25'
})
cy.get('input[type=range]').trigger('change')

cy.get('input[type=range]').siblings('p').should('have.text', '25')

Set a range input in a React controlled component​

React tracks an input's value itself, so setting the value property directly, as in the range input example, doesn't reach the component's change handler. Set the value through the element's native value setter, then trigger the event React listens for.

cy.get('input[type=range]').then(($range) => {
const descriptor = Object.getOwnPropertyDescriptor(
Object.getPrototypeOf($range[0]),
'value'
)
descriptor?.set?.call($range[0], 70)
})
cy.get('input[type=range]').trigger('input')

cy.get('input[type=range]').siblings('p').should('have.text', '70')

React's onChange listens for the input event. If the component listens for change instead, trigger change.

Vue's v-model reads the input's value directly, so the range input example works there with .trigger('input') in place of .trigger('change').

Drag and Drop Events​

Which events to trigger depends on how the app implements dragging: the HTML Drag and Drop API, mouse events, or pointer events. Each example below covers one of them.

Drag an item to a drop zone with dataTransfer​

HTML drag and drop handlers pass data through the event's dataTransfer property. Create one DataTransfer and pass the same object to every event, so the data the dragstart handler sets is there when the drop handler reads it.

const dataTransfer = new DataTransfer()

cy.get('[data-cy=card]').trigger('dragstart', { dataTransfer })
cy.get('[data-cy=done-column]').trigger('dragenter', { dataTransfer })
cy.get('[data-cy=done-column]').trigger('dragover', { dataTransfer })
cy.get('[data-cy=done-column]').trigger('drop', { dataTransfer })
cy.get('[data-cy=card]').trigger('dragend', { dataTransfer })

cy.get('[data-cy=done-column]').should('contain', 'Write docs')

Check that a drop zone accepts drops​

A drop zone accepts a drop only when its dragover handler cancels the event with preventDefault(). The event is cancelable by default, so a cy.spy() listener can check the event's defaultPrevented property after the handlers run.

const onDragover = cy.spy().as('dragover')
cy.get('[data-cy=drop-zone]').then(($zone) => {
$zone[0].addEventListener('dragover', onDragover)
})

cy.get('[data-cy=drop-zone]').trigger('dragover')
cy.get('@dragover').should('have.been.calledWithMatch', {
defaultPrevented: true,
})

With cancelable: false, preventDefault() has no effect and defaultPrevented stays false.

Drag an element with mouse events​

Many drag handlers listen for mousedown on the element, then follow mousemove and mouseup on the document. The events bubble, so triggering them on the element reaches document listeners too. Pass the position properties the handler reads at each step. With the default Event constructor, pageX and pageY reach the handler exactly as you pass them.

cy.get('[data-cy=draggable]').trigger('mousedown', {
button: 0,
pageX: 100,
pageY: 100,
})
cy.get('[data-cy=draggable]').trigger('mousemove', {
pageX: 300,
pageY: 150,
})
cy.get('[data-cy=draggable]').trigger('mouseup')

cy.get('[data-cy=draggable]')
.should('have.css', 'left', '200px')
.and('have.css', 'top', '50px')

Drag a slider thumb with pointer events​

Many drag and gesture libraries listen for pointer events and ignore pointers that aren't primary. Pass the same pointerId with every event so the library tracks one pointer from press to release.

const pointer = { pointerId: 1, pointerType: 'mouse', isPrimary: true }

cy.get('[data-cy=volume-thumb]').trigger('pointerdown', {
...pointer,
button: 0,
})
cy.get('[data-cy=volume-thumb]').trigger('pointermove', {
...pointer,
clientX: 400,
})
cy.get('[data-cy=volume-thumb]').trigger('pointerup', pointer)

cy.get('[data-cy=volume-value]').should('have.text', '80')

Touch Events for Mobile Testing​

Mobile web apps often handle swipes and other gestures with touch events. Touch handlers read finger positions from the touches and changedTouches lists rather than from clientX. With the default Event constructor, pass plain objects with the properties the handler reads. On touchend, the lifted finger is in changedTouches, and touches is empty.

cy.get('[data-cy=carousel]').trigger('touchstart', {
touches: [{ clientX: 300, clientY: 100 }],
})
cy.get('[data-cy=carousel]').trigger('touchmove', {
touches: [{ clientX: 50, clientY: 100 }],
})
cy.get('[data-cy=carousel]').trigger('touchend', {
touches: [],
changedTouches: [{ clientX: 50, clientY: 100 }],
})

cy.get('[data-cy=slide-2]').should('be.visible')

For mobile testing, pair this with cy.viewport(), such as cy.viewport('iphone-x'), to render your app's mobile layout. A desktop browser usually reports no touch support, so an app that checks for it before adding touch listeners, such as with 'ontouchstart' in window, doesn't respond to these events.

Custom Events​

Trigger a custom event with a detail payload​

.trigger() fires any event name, including your app's own events. With the default Event constructor, the detail you pass is attached as is.

cy.get('[data-cy=cart]').trigger('cart:updated', { detail: { count: 3 } })
cy.get('[data-cy=cart-count]').should('have.text', '3')

If the handler checks event instanceof CustomEvent, add eventConstructor: 'CustomEvent', which also accepts detail.

Window and Document​

Trigger an event on the window or document​

.trigger() also accepts the window or document as its subject. It dispatches the event right away, without waiting for actionability, and doesn't add any coordinate properties to the event.

cy.window().trigger('beforeprint')
cy.document().trigger('visibilitychange')

Show an offline banner when the connection drops​

cy.window().trigger('offline')
cy.get('[data-cy=offline-banner]').should('be.visible')

cy.window().trigger('online')
cy.get('[data-cy=offline-banner]').should('not.be.visible')

This runs the app's offline and online listeners, but navigator.onLine doesn't change. It works when the app reacts to the events rather than reading navigator.onLine.

For scroll and resize, use .scrollTo() and cy.viewport() instead. They change the scroll position or window size and fire the real event, so the handler reads values that match it.

Notes​

Actionability​

The element must first reach actionability​

.trigger() is an "action command" that follows all the rules of Actionability.

Events​

Choose which event .trigger() fires​

.trigger() is a low-level utility that constructs and dispatches an event for you. Since it fires any event, Cypress makes no assumptions about how the event should look. Check which event your app's handler listens for, including handlers inside a third-party library, and pass the properties it reads.

How eventConstructor changes the properties you can set​

By default, .trigger() fires an Event. To fire another class, such as MouseEvent or KeyboardEvent, pass its name in the eventConstructor option.

cy.get('button').trigger('mouseover', { eventConstructor: 'MouseEvent' })

When the constructor is UIEvent or one of its subclasses, such as MouseEvent or KeyboardEvent, .trigger() sets the event's view property to the application's window.

With the default Event constructor, .trigger() attaches every property you pass to the event as is, including ones like pageX and pageY that a real mouse event calculates for itself. Handlers that only read properties, like the one in Drag an element with mouse events, work with this default.

When you set eventConstructor to a class such as MouseEvent, the browser builds the event, and that class decides which properties you can set. Properties the constructor accepts, such as clientX, button, and shiftKey, take your values. Read-only properties the browser calculates, such as pageX and offsetX, keep the browser's values, and .trigger() drops yours without an error.

// Event (default): pageX is attached as passed
cy.get('.target').trigger('mousedown', { pageX: 600 })

// MouseEvent: clientX is set, and the browser calculates pageX from it
cy.get('.target').trigger('mousedown', {
eventConstructor: 'MouseEvent',
clientX: 300,
})

This table shows where each mouse property's value comes from:

PropertyDefault EventMouseEvent
clientX, clientY, screenX, screenYYour valueYour value
button, buttons, movementX, movementYYour valueYour value
shiftKey, ctrlKey, altKey, metaKeyYour valueYour value
pageX, pageYYour valueCalculated from clientX, clientY, and scroll
offsetX, offsetYYour valueCalculated from the element's position
whichYour valueCalculated from button
x, yNot attached: .trigger() reads them as the position to fire atCalculated, equal to clientX and clientY

With KeyboardEvent, every property keyboard handlers usually read takes your value: key, code, keyCode, which, repeat, and the modifier keys.

Keep the default unless the handler needs a real instance of the class, for example when it checks event instanceof MouseEvent.

Differences​

How .trigger() differs from action commands​

In other words, what's the difference between:

cy.get('button').trigger('focus')
cy.get('button').focus()

// ... or ...

cy.get('button').trigger('click')
cy.get('button').click()

Both kinds of commands wait for the element to reach actionability first. The action commands then reproduce the full sequence of events a user's input produces, along with what the browser does in response. .trigger() dispatches only the event you name.

You callEvents firedWhat happens in the browser
.trigger('click')clickFocus doesn't move. With the default Event, nothing else happens. With eventConstructor: 'MouseEvent', a checkbox toggles and fires input and change, and a link is followed.
.click()pointerover, pointerenter, mouseover, mouseenter, pointermove, mousemove, pointerdown, mousedown, focus, focusin, pointerup, mouseup, clickFocus moves to the element, a checkbox toggles, and a link is followed.
.trigger('keydown', { key: 'a' })keydownFocus doesn't move, and no text is inserted, even with eventConstructor: 'KeyboardEvent'.
.type('a')The .click() sequence when the element isn't focused yet, then keydown, keypress, beforeinput, textInput, input, keyupFocus moves to the element, and a is inserted.
.trigger('focus')focusFocus doesn't move.
.focus()focus, focusinFocus moves to the element.

Your listeners run either way. Use the action command when you're testing what a user does, and .trigger() when a handler listens for an event no action command fires.

Rules​

Requirements ​

  • .trigger() requires being chained off a command that yields a single DOM element, the window, or the document. It errors when the subject contains more than one element.

Assertions ​

  • .trigger() automatically waits for the element to reach an actionable state. It skips this wait when the subject is the window or the document.
  • .trigger() automatically retries until all chained assertions have passed.

Timeouts ​

  • .trigger() can time out waiting for the element to reach an actionable state.
  • .trigger() can time out waiting for assertions you've added to pass.

Command Log​

Trigger a change event on input type='range'

cy.get('.trigger-input-range').then(($range) => {
$range[0].value = '25'
})
cy.get('.trigger-input-range').trigger('change')
cy.get<HTMLInputElement>('.trigger-input-range').then(($range) => {
$range[0].value = '25'
})
cy.get('.trigger-input-range').trigger('change')

The commands above display in the Command Log as:

Command Log showing two get commands for .trigger-input-range, followed by a trigger command for the change event

When you click trigger in the Command Log, the console outputs the following:

DevTools console after clicking the trigger command, showing Command: trigger, Yielded: the jQuery-wrapped range input, and Event options with clientX, clientY, screenX, screenY, and pageX values

History​

VersionChanges
15.20.0Option scrollBehavior now accepts a per-axis { block, inline } object and the 'start' and 'end' alignments
6.1.0Added option scrollBehavior
5.1.0Added option eventConstructor, and events from UIEvent constructors now include the view property
3.5.0Added screenX and screenY properties to events
0.20.0.trigger() command added

See also​