SDK: Showing surveys
Survey modes
The root option picks the mode.
| Popup (default) | Embed | |
|---|---|---|
| How to get it | init() without root | init() with root: a CSS selector or an element |
| Where it shows | Floating card in the bottom-right corner | Inside your container, in the page flow |
| When it shows | By the dashboard rules: see When a popup shows | Right after init(), no triggers |
| Close button | Yes | No |
| Feedback button | Optional (dashboard setting) | No |
| On SPA navigation | Closed and set up again for the new URL | Stays |
| Several at once | One open, the rest wait in a queue | Yes, one per container |
| Callbacks | onRender, onSubmit, onClose | onRender, onSubmit |
open() / close() | Yes | No |
// popup
ftools('init', {
surveyId: '__SURVEY_ID__',
publishableKey: '__PUBLISHABLE_KEY__'
});
// embed, by selector
ftools('init', {
surveyId: '__SURVEY_ID__',
publishableKey: '__PUBLISHABLE_KEY__',
root: '#my-container'
});
// embed, by element
ftools('init', {
surveyId: '__SURVEY_ID__',
publishableKey: '__PUBLISHABLE_KEY__',
root: document.getElementById('my-container')
});Embed notes:
- The SDK looks up
rootafter the survey data loads, indocumentonly. It does not look inside another shadow root. - One container holds one survey.
- When you remove the container from the DOM, the SDK cleans the survey up.
When a popup shows
The survey's Settings → Triggers tab decides when a popup opens. Embed surveys ignore these settings.
Manual event tracking picks who opens the popup:
| Manual event tracking | What opens the popup | open() |
|---|---|---|
| Off (default) | The trigger: right after init() when no trigger is set, after a delay in seconds, or after a scroll depth in percent | Does nothing |
| On | Only your code | Opens the popup |
A scroll trigger fires when the visitor scrolls past the given percentage of the page. A page too short to scroll counts as 100%.
Target pages (Where to show this survey → Specific pages) limit the popup to some URLs. The SDK compares each pattern with the whole path, query and hash of the current URL, and * matches anything. So /pricing does not match /pricing?plan=pro; use /pricing* for that. With manual event tracking on, target pages don't apply: your code decides.
Two settings stop a popup from coming back:
| Setting | Effect |
|---|---|
| Display → Don't show again if dismissed | On: after the visitor closes the popup, it doesn't show to them again. Off: it shows again on the next page load or SPA navigation. |
| Repeat responses → Allow the same respondent to answer again | Off: a visitor who answered doesn't see the survey again. On: they can see it and answer again. |
Both rules recognize a returning visitor by the session the SDK keeps in localStorage (see Privacy and data). The answer rule also recognizes them across sessions by userId, if you pass it; the dismiss rule does not. open() with force: true ignores both rules.
One popup at a time. If several popup surveys are due, the first one opens and the rest wait in a queue. The next one opens when the current one closes.
Feedback button. With Show feedback button in bottom-right corner on, a button on the right edge of the page, near the bottom, opens the survey on click. It shows only while no popup is open.
Single-page apps
The SDK follows URL changes made with history.pushState, history.replaceState and the back and forward buttons. On every change of the path, query or hash:
- open popups are removed without
onClose; - triggers and target pages are checked again for the new URL;
- embed surveys stay as they are.
The SDK wraps the History API methods it finds at load time, so a router that patches them earlier keeps working.