Theme Framework How the LeaseLeads theme frontend works

Drive a component from HFCM

Reach a live component instance from a script injected outside the theme, and handle async boot without race conditions.

On this page 5

Sometimes a page needs a one-off script that calls a component's methods — often injected through the HFCM plugin (Header Footer Code Manager) rather than living in the theme. The Globals registry exists precisely for this: it exposes every registered component at window.LeaseLeads_Theme.components, so an outside script can look up a class by moniker and reach a specific element's instance.

js
LeaseLeads_Theme.components.get('Intro').get(el).someMethod()

The catch is timing.

Components boot lazily and asynchronously — Deferred does the real instantiation on first interaction (WP-Rocket-delayed in production). A script in the header runs long before any instance exists. Put HFCM snippets that touch components in the footer, and even then assume the instance may not be there yet at eval time.

Warning

LeaseLeads_Theme.components.get('Intro') can return the class while .get(el) returns undefined — the class registered, but that element's instance hasn't been constructed yet. Never assume the instance exists the moment your script runs.

2. Prefer the lifecycle event

The robust approach is to wait for the element to tell you it's ready. Every instance fires an initialized lifecycle event on its element in two flavours: a generic module.initialized and a moniker-scoped intro.initialized. Both carry the live instance on event.detail.instance. Listen, and you get the instance exactly when it becomes usable — no polling, no race.

html
<!-- HFCM: Footer -->
<script>
  document.querySelectorAll('.intro').forEach(el => {
    el.addEventListener('intro.initialized', event => {
      const intro = event.detail.instance

      // The instance is fully booted and initialized here.
      intro.carousel?.go(0)
    })
  })
</script>

Use the moniker-scoped name (intro.initialized) when you know the component; use the generic module.initialized when a listener is shared across module types.

Important

The moniker is a public API contract. intro.initialized and components.get('Intro') both depend on the string Intro staying stable — it survives Vite's minification precisely so external scripts can rely on it. Renaming a component's moniker is a breaking change for any HFCM snippet.

3. Or pull the instance directly

If your script runs after boot — for example behind a user click, well past the deferred init — you can skip the event and read the instance straight from the registry. Guard for the not-yet-ready case.

html
<!-- HFCM: Footer -->
<script>
  document.querySelector('#open-intro')?.addEventListener('click', () => {
    const Intro = LeaseLeads_Theme.components.get('Intro')
    const el = document.querySelector('.intro')
    const intro = Intro?.get(el)

    if ( ! intro ) return   // not booted yet — bail rather than throw

    intro.carousel?.go(1)
  })
</script>

components.get('Intro') resolves by moniker (or Vite's built name); .get(el) returns the registered instance for that element, or undefined if it hasn't been constructed. Because the click happens long after page load, the instance is reliably present — but the guard keeps the snippet safe either way.

4. Guard against the registry not being ready

On a very fast interaction the theme's registry may not have attached yet. Globals dispatches a leaseleads.theme.loaded event on document (and sets window.LeaseLeads_Theme.loaded = true) once it is available, so the belt-and-braces pattern is:

html
<script>
  function ready(fn) {
    if ( window.LeaseLeads_Theme?.loaded ) return fn()
    document.addEventListener('leaseleads.theme.loaded', fn, { once: true })
  }

  ready(() => {
    document.querySelectorAll('.intro').forEach(el => {
      el.addEventListener('intro.initialized', e => e.detail.instance.carousel?.go(0))
    })
  })
</script>