Skip to content

Lazy-rendered charts can stay empty when Visualizer renderer scripts load out of order #1319

@pirate-bot

Description

@pirate-bot

Summary

Charts with Visualizer lazy rendering enabled can output valid chart placeholders and chart data but remain blank on the frontend after a page revisit or cached load.

Expected behavior: charts embedded by shortcode render when the page is viewed, including when lazy rendering is enabled for the chart.

Actual behavior: the chart containers may remain empty even though the chart data is present in the page source. This can make published charts appear to disappear after the page is updated and revisited later.

Impact: affected pages can show blank spaces instead of charts until conditions happen to trigger the renderer path successfully.

Customer context

Reported with Visualizer 4.0.2 and Visualizer Pro 2.0.1 on a public WPBakery/Salient page. The customer embedded two charts as raw HTML shortcodes, including [visualizer id="1474" lazy="no" class=""], and also tested without the shortcode lazy parameter.

The public page source for the reported URL contained chart placeholders for chart IDs 1474 and 1478, visualizer.charts data for both charts, and Visualizer renderer scripts converted to data-visualizer-script because per-chart lazy rendering was enabled.

Reproduction notes

Known evidence:

  1. Visit the reported page source while logged out or as a normal visitor.
  2. Observe two rendered Visualizer placeholders for chart IDs 1474 and 1478.
  3. Observe visualizer.charts data localized for those chart IDs.
  4. Observe visualizer-render-google-lib and visualizer-render-facade emitted as script[data-visualizer-script] rather than normal src scripts.
  5. The exact blank-chart runtime state was not reproduced in a browser session here, but repository inspection shows a concrete load-order race that matches the intermittent symptom.

Diagnosis

Conclusion

Visualizer's lazy-render script path can leave frontend charts blank because it removes src from Visualizer-owned renderer scripts and later loads those scripts concurrently through jQuery.getScript(). That bypasses the WordPress dependency order that originally placed the chart renderer before render-facade.js. If render-facade.js runs before the Google renderer has registered its visualizer:render:chart:start listener, the facade's document-ready render event can be missed, leaving the valid chart placeholder empty.

The reported page currently shows this path: chart containers for 1474 and 1478 are present, chart data is localized, and visualizer-pro-render, visualizer-customization, visualizer-render-google-lib, and visualizer-render-facade are emitted as data-visualizer-script tags without src. The same page has google-jsapi loaded normally.

Where this likely occurs

  • classes/Visualizer/Module/Frontend.php:90-121, Visualizer_Module_Frontend::script_loader_tag() transforms Visualizer script tags when $this->lazy_render_script is true; line 115 replaces src with data-visualizer-script for handles containing visualizer-, excluding only google-jsapi, chartjs, and visualizer-datatables.
  • classes/Visualizer/Module/Frontend.php:386-393, Visualizer_Module_Frontend::renderChart() keeps $this->lazy_render_script enabled when chart settings include lazy_load_chart, independent of the shortcode lazy attribute. The reported page source confirms lazy_load_chart: true for the affected charts.
  • classes/Visualizer/Module/Frontend.php:492-518, Visualizer_Module_Frontend::renderChart() registers visualizer-render-facade with dependencies returned by visualizer_assets_render; WordPress would normally order the facade after renderer dependencies.
  • classes/Visualizer/Render/Sidebar/Google.php:101-118, Visualizer_Render_Sidebar_Google::load_google_assets() registers visualizer-render-google-lib and returns it as a dependency for the facade.
  • classes/Visualizer/Module/Frontend.php:770-819, Visualizer_Module_Frontend::printFooterScripts() emits the lazy script loader. visualizerLoadScripts() loops over all script[data-visualizer-script] nodes and starts jQuery.getScript() calls without sequencing, then calls visualizerRefreshChart() after each individual script resolves.
  • js/render-facade.js:98-109, document-ready initializes chart display immediately when the facade script executes.
  • js/render-facade.js:131-172, displayChartsOnFrontEnd() marks non-shortcode-lazy charts as viz-facade-loaded and calls showChart(id), which triggers visualizer:render:chart:start.
  • js/render-google.js:561-624, the Google renderer registers the visualizer:render:chart:start listener. If this script loads after the facade has already fired the event, that initial render signal is not observed.
  • Git history: 53d1e0ac (Revert "fix: lazy loading not always working") changed the lazy script loader to the current jQuery.getScript() approach and is contained in v4.0.0, v4.0.1, and v4.0.2. ec7f84a6d (fix: multiple charts in same page not showing for different lib) changed the facade dependency/localization area and is also in the 4.0.x line.
  • visualizer-pro/inc/addon.php:516-562, Visualizer_Pro::loadFrontendResources() registers Pro frontend render scripts that are also affected by the same free-plugin script_loader_tag() path because the handles contain visualizer-. Pro appears involved as an affected companion script, not the primary shortcode renderer.

Engineering notes

  • The shortcode lazy="no" only affects the visualizer-lazy class path in renderChart(). It does not override the saved chart setting lazy_load_chart, which controls visualizer-lazy-render and the delayed script-loading branch.
  • The live page source had valid chart data for both affected charts, so the missing output is not explained by missing shortcode processing, missing chart posts, permissions hiding the chart, or absent localized data.
  • The failure mode is timing-sensitive because jQuery.getScript() requests multiple Visualizer scripts independently. Browser cache, CDN timing, or page-cache output can affect whether the facade or renderer script executes first.
  • This path is not limited to Google Charts in principle, but the inspected customer page uses Google chart types (column and line), and the concrete event-listener race was verified against render-google.js.
  • The Pro add-on adds additional visualizer-* scripts to the delayed loader on the reported page. The core chart render race is in the free plugin because the shortcode output, facade, lazy loader, and Google renderer live there.

Test coverage status

No relevant coverage was found during inspection for data-visualizer-script, visualizerLoadScripts(), visualizer-lazy-render, or lazy-render frontend script ordering in the free plugin tests. No relevant Playwright spec was found during inspection for this lazy-render path.

What to verify or explore next

  • Reproduce on Visualizer 4.0.2 with a Google chart whose saved settings have lazy_load_chart: true, embedded via shortcode with lazy="no" and via shortcode without the lazy attribute.
  • On a cold/cache-variant frontend load, observe whether visualizer-render-facade.js can execute before render-google.js, and whether visualizer:render:chart:start fires before the Google renderer listener exists.
  • Compare behavior against a version before 53d1e0ac or before v4.0.0 to confirm the version boundary.
  • Run or add a frontend/e2e check that asserts lazy-rendered Google charts become non-empty and receive visualizer-chart-loaded after the delayed Visualizer scripts are loaded.
  • Verify related Chart.js and DataTables lazy-render paths under the same delayed script loader, because the loader affects multiple visualizer-* renderer handles.

Confidence

Confidence: 88/100

The reported live page, installed versions, repository code, and recent git history all point to the same lazy-render script-loading path in Visualizer 4.0.x; no unrelated site-specific error is required for the empty chart placeholders observed in the page source.


Source: HelpScout #3335164196
Generated by bug-report-triage workflow (ID: bug-report-triage_6a16a49d2d20d9.61603971)

Metadata

Metadata

Assignees

No one assigned

    Type

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions