Execute JavaScript code automation
The Execute JavaScript code automation runs your own JavaScript in the visitor's browser when they interact with a widget element. Use it to push events to your analytics, set a cookie, call a third-party script, or control the widget itself. Your code receives a small context object with the widget's element, the visitor and helper functions.
Available on: all platforms, except Webflow sites connected through the Webflow integration (see below).
How it works
- You add Execute JavaScript code to an element and write your code in the JavaScript code editor.
- When the element's event happens, the code runs immediately in the visitor's browser, without waiting for the event to reach Personizely.
- The context variables below are available as local constants.
Context variables
// Available inside your code as local constants
const variationId: number // ID of the widget variation the visitor sees
const el: HTMLElement // the widget's root element on the page
const type: 'popup' | 'callout' | 'bar' | 'embedded'
const visitor: {
getId(): string // Personizely visitor ID
get(field: string): any // a default field, e.g. visitor.get('email')
getCustomFieldValue(fieldId: number): any // a custom field by its ID
}
function close(): void // closes the widget
function changeStep(stepId: number): void // shows another step of the widget
const helpers: {
readCookie(name: string): string | undefined
createCookie(name: string, value: string | null, seconds?: number): void
}Variable | Notes |
|---|---|
variationId | Useful to tell variations apart in your own analytics during a widget A/B test |
el | Query inside the widget with el.querySelector(...) |
type | The widget's type |
visitor | Default field IDs include email, firstName, lastName, phone, country, city, utmSource and the others listed in Visitor fields. Values just submitted in this form are already included |
close() | Closes the widget as the close button would; counts as a close for Frequency |
changeStep(stepId) | Takes the numeric step ID |
helpers.createCookie | Sets a cookie on your top-level domain, shared with subdomains. seconds defaults to your website's cookie lifetime (default 1 year). Pass null as the value to delete the cookie |
helpers.readCookie | Returns the cookie's value, or undefined |
The public JavaScript API (window.ply) is also available, for example ply.trackGoal(...) or ply.runCampaign(...). See the JavaScript API.
The same context is available to the widget variation's custom JS, which runs each time the widget is displayed. See Widget display options.
Configuration
Option | What it does | Values / default |
|---|---|---|
JavaScript code | The code to run | JavaScript |
Behavior details and limitations
- Errors are silent. If your code throws, the error is swallowed so the widget keeps working. Open the page with ?ply_debug=1 to see "There's an error in your JS code" in the console, or wrap your code in try/catch and log the error yourself.
- Don't redeclare the context names. const el = ... or let visitor at the top level of your code is a syntax error, and the whole snippet won't run. Use other names.
- Synchronous start. The code runs as a plain function body. You can use promises and return, but not top-level await; wrap async work in an async function and call it.
- It runs every time. Each click or submit runs the code again.
- Forms. On a Form, the code runs on a successful submit, at the same time as the submission is sent.
- Content Security Policy. If your site has a CSP, it must allow 'unsafe-eval' for scripts for this automation to run. See Content Security Policy.
- Webflow. When the Webflow integration is connected, custom JavaScript, including this automation, doesn't run. Install the script tag manually instead. See Install on Webflow.
- Preview runs it. Browser automations run in preview, so guard code that sends data to production systems.
Examples
Push a signup to Google Tag Manager:
window.dataLayer = window.dataLayer || []
window.dataLayer.push({
event: 'newsletter_signup',
widget_variation: variationId,
email_domain: (visitor.get('email') || '').split('@')[1]
})Remember a choice for 30 days and close after 2 seconds:
helpers.createCookie('prefers_wholesale', '1', 60 * 60 * 24 * 30)
setTimeout(close, 2000)Go to a step based on your own logic:
const cartTotal = window.myStore?.cart?.total || 0
changeStep(cartTotal > 100 ? 4512 : 4513)Troubleshooting
- Nothing happens → the code threw an error (check with ?ply_debug=1), a context name was redeclared, or your CSP blocks 'unsafe-eval'.
- visitor.get('email') is empty → the visitor hasn't submitted an email yet, or the field is a custom field: use getCustomFieldValue(id).