Skip to main content
Containers control when and how content appears to visitors. Use them for conditional visibility, lazy loading, split tests, and visitor-specific content.

Overview

Containers wrap page content and add behavior based on visitor actions, page events, or other conditions.

Available Containers

CTA Container

The CTA Container (call-to-action container) holds the part of a video sales letter (VSL) page that should stay hidden until the pitch: packages, prices, buy buttons, the guarantee. It opens when the page calls getStarted(). Most of the time that call comes from the video player at the pitch timestamp, and you can also call it from a button, a Page Events flow or your own script. Key Features:
  • Hidden when the page loads, by the page head CSS (display: none)
  • Opens (display: block) when getStarted() runs
  • Can scroll the visitor to itself when it opens
  • Stays open for returning visitors (see Returning visitors)

The classes

Everything is driven by CSS classes. The builder adds them for you; in code you write them yourself.
The TrackPlay V2 embed also ships a fallback getStarted() that remembers the focus-on-cta scroll in a scrolled_<video code> cookie for one day. The page’s main script replaces that fallback as soon as it loads, so on a normal page the rules in the table apply.

What getStarted() does

getStarted(focusOnCta = true) is a global function on every published page. Each call:
  1. Sets window.showed_video_cta = true.
  2. Saves the reveal for this browser: localStorage key video_cta_shown = true, plus a video_cta_shown cookie for 365 days.
  3. Opens every .video-cta-container and .show-on-video-cta, and hides every .hide-on-video-cta (all with !important).
  4. If focusOnCta is true and the CTA was not restored on this page load, scrolls to the first .video-cta-container.focus-on-cta. Without that class nothing scrolls.
  5. Fires the video-cta-shown window event with { focus_on_cta, timestamp }. ef.onCta() listens for it.
  6. Runs the nodes after any CTA: On Shown node in Page Events.
Calling it again is harmless for the containers. getStarted(false) opens the CTA without scrolling. window.showed_video_cta is true once the CTA has been shown on this page load, either by getStarted() or by the returning-visitor restore. Read it in your own scripts to check whether the visitor reached the pitch. The If Showed CTA Page Events node and live order popups set to wait for the CTA read the same flag.

How TrackPlay fires it at the pitch

  1. In TrackPlay, open the video’s Timed events tab and add an event: Event Name (for example cta) and Time (the pitch timestamp). When playback reaches that time the player emits an event with that name. Skipping ahead past the time fires it too.
  2. The ElasticFunnels embed listens for that name and calls getStarted():
    • TrackPlay Video (<trackplay-video>): listens for data-cta-event-name (default cta). Scrolling follows data-focus-on-cta, which defaults to on. Only the value 0 turns it off; false still scrolls.
    • TrackPlay Video V2 (<trackplay-video-v2>): listens for every event in its data-cta list, each with its own scroll setting. The builder fills that list with all of the video’s timed events. If the attribute is missing, it listens for cta without scrolling.
  3. Turn on Call Once Per Session and Call on Subsequent Visits in TrackPlay and a returning viewer who already passed the pitch gets a <event name>_subsequent event straight away. The embed answers it with getStarted(false), so the CTA opens without a scroll.
Event names must match exactly, including case.

Returning visitors

Two mechanisms bring the CTA back on later visits:
  • Saved reveal. On every page load the main script checks localStorage for video_cta_shown. If it is set, CTA containers open and hide-on-CTA content hides immediately, with no scroll and no video-cta-shown event. This key belongs to the domain, not to one page or one video, and it does not expire.
  • Open on return. Every TrackPlay embed writes showed_cta_<video code> to localStorage when the page loads. If the key was already there from an earlier load, the embed calls getStarted(false) about one second after load. A returning visitor gets the CTA whether or not they reached the pitch on the first visit, so someone who left early and came back to buy does not have to sit through the video again.
Open on return is why the offer is already open when you reload your own page. To see the page as a first-time visitor, use a fresh private window for every run.
To reset a test browser, delete video_cta_shown and showed_cta_<video code> from localStorage, the video_cta_shown cookie, and TrackPlay’s trackplay_<event code> cookie.

Use it in a code page (.ef)

In a page you write in code, use the class names directly. A minimal VSL page:
The same fallback without code is a Page Events flow: On Page Load → Wait Seconds (1500) → If Showed CTA → output 2 → Show Video CTA. Tick Scroll to CTA on the last node if the page should jump. See Page Events nodes.
A fallback timer reveals the offer the same way the video does, so it is saved for that browser too. Set it later than the pitch timestamp so it only catches visitors whose video never fired.
To open the CTA from a button that is visible before the pitch (a “Skip to the offer” link, for example), call getStarted() from its click handler or from the Interactions Panel:

Use it in the builder

  1. Add the video. Drag Trackplay Video V2 (category External Integrations) onto the page. In its TrackPlay Video settings, pick your integration under Select TrackPlay Integration, then pick the video under Select a video.
  2. Choose which events open the CTA. The settings list the video’s timed events under CTA Events Available, each with its time. Every event listed opens the CTA. Tick Scroll to CTA Container on the events that should also scroll.
  3. Add the CTA Container. Drag CTA Container (category Offer) where the offer goes and build the packages inside it. The Container Settings panel confirms it is hidden when the page loads.
  4. Set its toggles. Focus on this when CTA is called adds focus-on-cta, the scroll target. Show to returning viewers adds cta-keep-visible-on-reload, the open-on-return setting.
  5. Optional: hide content at the pitch. Wrap it in Container: Hide on Video CTA (category Offer).
The page only scrolls when both are on: Scroll to CTA Container on the event, and Focus on this when CTA is called on a container.

Pitfalls

  • Everything inside is invisible until the pitch. The head CSS hides the container before any script runs, so its content does not show in a quick look at the live page, in a screenshot taken early, or to a visitor whose video never fired. Always give it a fallback (a timer, a skip link or a Page Events flow).
  • Widgets that measure themselves at load. A slider, carousel or countdown inside the container starts while it has no size. Check it after the reveal, or start it from ef.onCta().
  • Do not set display on the container yourself. On code pages the hiding rule is a plain display: none, so your own display: flex on .video-cta-container can show it at load. Put layout on a wrapper inside it. After the reveal the container is display: block !important.
  • Several videos on one page. There is one CTA for the whole page. Whichever video reaches a CTA event first opens every .video-cta-container, and there is no way to pair a container with one video. Each embed’s listener is attached to every TrackPlay player that loads on the page, so a timed event name used by a second video (a testimonial with its own cta event, for example) opens the offer too. Give events on secondary videos different names. Open on return also works per video, so any of the page’s videos can open the CTA on a later load.
  • The saved reveal covers the whole domain. A visitor who reached the pitch on one page sees CTA containers open at load on every other page of that domain, including the other arm of a split test. Test pitch timing in a fresh private window.
  • The builder lists every timed event. If the video has timed events for other jobs (a pixel, a bonus reveal), they open the CTA as well. Keep CTA events on the pitch, or remove the others from the video. A video with no timed events never opens the CTA by itself.
  • CTA: On Shown runs on every getStarted() call, including the open-on-return call about one second after load and _subsequent events. Keep the nodes after it safe to repeat.
  • Page classification. ElasticFunnels’ page analysis counts a builder video block, CTA Container or Hide on CTA as one of the signals that a page is a VSL. It reads builder component types, so a code page that uses only the class names does not give that signal. Its buy links still count.
Common Use Cases:
  • Reveal packages and buy buttons at the VSL pitch
  • Remove “keep watching” copy once the offer is on screen
  • Give visitors who skip around a button that opens the offer
  • Open the offer on a timer for visitors whose video never reaches the pitch

Hide on CTA Container

The Hide on CTA Container is the opposite of the CTA Container. It is visible when the page loads and hidden when getStarted() runs. In code it is any element with the class hide-on-video-cta; in the builder it is the Container: Hide on Video CTA block. Key Features:
  • Visible by default
  • Hidden (display: none !important) when getStarted() runs
  • Stays hidden for returning visitors once the CTA has been shown in that browser
  • The Hide Video CTA Page Events node shows it again (and hides the CTA Container)
Common Use Cases:
  • “Keep watching” or “the offer appears below” notes
  • A lead-in headline that should give way to the offer
  • Content that should not sit next to the buy buttons
Hiding the video itself with this container stops the visitor from finishing the VSL, and returning visitors never see the video again in that browser. Hide copy around the video, not the player, unless that is what you want.
Example Pattern:
Use both containers together to move visitors from the sales content to the offer without clutter.

Merchant Container

The Merchant Container displays content conditionally based on the active payment processor (merchant) for the current page view. Key Features:
  • Shows content only for specific merchants
  • Hides all non-matching merchant containers
  • Can be set per domain or overridden in Page Events
  • Useful for merchant-specific disclaimers, policies, and branding
How Merchants Are Determined:
  1. Domain-Level Setting: Default merchant set in Domain settings
  2. Page Events Override: Use the “Set Merchant” node in Page Events to override for specific conditions
Supported Merchants:
  • Buygoods
  • Clickbank
  • Digistore24
  • Other configured payment processors
Configuration:
  1. Add Merchant Container to your page
  2. Select Merchant from the container settings
  3. Add Content specific to that merchant
  4. Repeat for each merchant you want to support
Example Scenario: You have different footer disclaimers specific to each merchant:
Common Use Cases:
  • Merchant-specific disclaimers and legal policies
  • Payment processor-specific trust badges and seals
  • Different refund policies based on merchant
  • Merchant-specific support information and contact details
  • Custom branding elements per merchant
  • Merchant-specific terms and conditions
Setting Merchant in Page Events: Use the Set Merchant node to dynamically change which merchant container displays:
  1. Go to Page Events for your page
  2. Add a condition (Query Parameter, Script Rule, etc.)
  3. Connect to Set Merchant node
  4. Select the merchant to activate
  5. Merchant containers will show/hide accordingly
Resetting Merchant: Use the Clear Merchant node to reset back to the domain’s default merchant:
  1. Add Clear Merchant node in Page Events
  2. Connect it to a condition or event
  3. When triggered, clears any session merchant override
  4. Page returns to using the domain’s default merchant
This is useful for:
  • Resetting test merchants back to production
  • Clearing affiliate-specific merchants after purchase
  • Returning to default after special conditions expire
If no merchant is set (neither at domain level nor via Page Events), merchant containers will not display. Ensure you have a default merchant configured.

Split Test Container

The Split Test Container marks a section of the page whose content is chosen by a traffic split. It renders as a <split-test> element and holds no logic of its own — the variants and their weights live in Page Events. Key Features:
  • Marks one swappable slot, identified by its id
  • Filled by a Component Split Test node in Page Events
  • Its inner content is the default — what renders when no split test is running
  • Each served variant is tagged for analytics (data-sid / data-cid)
Exactly one of two things renders: the component the graph selected (replacing the container, wrapper and all), or — when nothing is bound — the default content on its own. So never write the variants inline inside the container; they can never all render. A single component inside a container is fine, because that is just the default.Containers are also filled before both template engines run, so wrapping one in @if or <template-if> does not stop it being filled — it only hides the result after the visitor has already been enrolled in the test. See Rendering pipeline.
How to Use:
  1. Add a Split Test Container to your page and fill in its Split Test Container Name trait — that name is what you pick from in Page Events.
  2. Create one saved component per variant.
  3. Configure in Page Events:
    • Add a Component Split Test node and select the container
    • Set Total Variants — a Traffic Distribution node is created for each one
    • Attach one Component node under each Traffic Distribution node, and mark one as the baseline
  4. Track Results in Analytics
Variants attach to Component nodes, not Dynamic Content nodes. Component Split Test → Traffic Distribution → Component is the only valid chain; the builder rejects anything else.
Common Use Cases:
  • Test different headlines
  • Compare pricing presentations
  • Test different images or videos
  • Optimize form layouts
Best Practices:
  • Test one element at a time
  • Run tests long enough for statistical significance
  • Track meaningful conversion goals
  • Document your test results
For complete split testing documentation, including setup and analysis, see Split Testing.

Lazy Load Container

The Lazy Load Container defers loading of content inside it until the visitor scrolls to that section of the page. Key Features:
  • Improves initial page load performance
  • Loads content “just in time” when visitor scrolls near it
  • Reduces bandwidth for content visitors never see
  • Loads when needed without interrupting the page
How It Works:
  1. Page loads with empty lazy load containers
  2. Visitor scrolls down the page
  3. When container enters viewport (or approaches it), content loads
  4. Content appears smoothly without page disruption
What to Lazy Load:
  • Below-the-fold content
  • Large images or videos
  • Comment sections
  • Secondary information
  • Resource-heavy widgets or embeds
What NOT to Lazy Load:
  • Above-the-fold content
  • Critical purchase buttons
  • Forms that need immediate interaction
  • Content needed for SEO
Performance Tips:
  • Use for content below the fold only
  • Combine with image optimization
  • Test on slow connections
  • Monitor Core Web Vitals impact
Don’t lazy load critical conversion elements like buy buttons or lead capture forms. Visitors should be able to interact with these immediately.

Some specialized containers are documented in dedicated pages:

Subscription Containers

Sub Pending, Sub Success, and Sub Error containers auto-toggle based on the result of a subscription modification API call (ef.sub.upgrade() / ef.sub.cancel()). Use them to build multi-screen subscription upsell flows. View Subscription Upsells Documentation →

Dynamic Container

The Dynamic Container marks a slot whose content is replaced with a saved component when a Page Events condition matches. Unlike the Split Test Container, the choice is driven by a condition you write, not by a traffic split. View Dynamic Content Documentation →

Whitelisted Containers

Whitelisted Container and Non-Whitelisted Container display different content based on visitor whitelisting status, commonly used for content protection and personalized visitor experiences. View Content Visibility Control Documentation →

Container Combination Patterns

Progressive Sales Flow

Combine multiple containers for a complete sales experience:

Performance-Optimized Page

Use lazy loading for better page speed:

Dynamic Merchant Selection

Adapt disclaimers and policies based on traffic source:

Best Practices

Container Organization

  1. Clear Naming: Name containers descriptively in the page builder
  2. Document Logic: Note which containers work together
  3. Test All Paths: Verify all show/hide conditions work correctly
  4. Consider Mobile: Ensure container behaviors work on all devices

Performance Guidelines

  1. Lazy Load Wisely: Use for below-fold content only
  2. Minimize Nesting: Don’t nest containers unnecessarily
  3. Test Load Times: Monitor impact on page speed
  4. Optimize Content: Compress images and minimize resources inside containers

User Experience

  1. Smooth Transitions: Avoid jarring content shifts when containers appear
  2. Visual Feedback: Let users know when content is loading
  3. Fallback Content: Have defaults for when conditions aren’t met
  4. Test Edge Cases: What happens if no merchant is set? Test it.

Development Workflow

  1. Start Simple: Add containers one at a time
  2. Test Immediately: Verify each container works before adding more
  3. Use Preview Mode: Test without affecting live traffic
  4. Clear Cache: Clear browser cache when testing changes

Troubleshooting

CTA Container Not Showing

Symptoms: CTA container remains hidden after trigger Possible Causes:
  • The TrackPlay timed event name does not match the name the embed listens for (exact match, case included)
  • A builder TrackPlay Video V2 whose video has no timed events, so nothing calls getStarted()
  • The content sits outside the element with the video-cta-container class
  • A JavaScript error on the page stops the main script
Solutions:
  1. Run getStarted() in the browser console. If the container opens, the page is fine and the trigger is the problem.
  2. Compare the event name in TrackPlay’s Timed events tab with data-cta-event-name (V1) or the CTA Events Available list (V2).
  3. Check the browser console for JavaScript errors
  4. Add a fallback timer or a skip button (see Use it in a code page)

CTA Opens Too Early or on Load

Symptoms: The offer is visible before the pitch, or right after the page loads Possible Causes:
  • The browser already saw the CTA on this domain (video_cta_shown in localStorage)
  • Open on return: the browser loaded this video’s page before (showed_cta_<video code>), so the CTA opens about one second after load. This is intended for returning visitors
  • Another timed event on the video, or on a second video on the page, opens it
  • Your own CSS sets display on .video-cta-container
Solutions:
  1. Test in a fresh private window
  2. Check the video’s timed events and rename the ones that are not the pitch
  3. Move layout CSS to a wrapper inside the container

Merchant Container Not Displaying

Symptoms: No merchant containers showing Possible Causes:
  • No merchant set at domain level
  • No merchant selected in Page Events
  • Merchant ID mismatch
Solutions:
  1. Check domain settings for default merchant
  2. Verify Set Merchant node in Page Events
  3. Confirm merchant containers have correct merchant assigned
  4. Check browser console for errors

Lazy Load Issues

Symptoms: Content not loading when scrolling Possible Causes:
  • Container too small to trigger
  • JavaScript not initializing properly
  • Content inside container has errors
Solutions:
  1. Ensure container has minimum height
  2. Check browser console for errors
  3. Test without lazy loading first
  4. Verify content loads normally outside container

Multiple Containers Conflicting

Symptoms: Unexpected show/hide behavior Possible Causes:
  • Overlapping container logic
  • Multiple merchants set incorrectly
  • CTA and Hide on CTA not paired properly
Solutions:
  1. Review Page Events flow carefully
  2. Test each container individually
  3. Check for conflicting JavaScript
  4. Simplify logic and add complexity gradually

Start with one clear container use case, then add more logic only when you can test each condition.