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:
- Visit the reported page source while logged out or as a normal visitor.
- Observe two rendered Visualizer placeholders for chart IDs
1474 and 1478.
- Observe
visualizer.charts data localized for those chart IDs.
- Observe
visualizer-render-google-lib and visualizer-render-facade emitted as script[data-visualizer-script] rather than normal src scripts.
- 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)
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 shortcodelazyparameter.The public page source for the reported URL contained chart placeholders for chart IDs
1474and1478,visualizer.chartsdata for both charts, and Visualizer renderer scripts converted todata-visualizer-scriptbecause per-chart lazy rendering was enabled.Reproduction notes
Known evidence:
1474and1478.visualizer.chartsdata localized for those chart IDs.visualizer-render-google-libandvisualizer-render-facadeemitted asscript[data-visualizer-script]rather than normalsrcscripts.Diagnosis
Conclusion
Visualizer's lazy-render script path can leave frontend charts blank because it removes
srcfrom Visualizer-owned renderer scripts and later loads those scripts concurrently throughjQuery.getScript(). That bypasses the WordPress dependency order that originally placed the chart renderer beforerender-facade.js. Ifrender-facade.jsruns before the Google renderer has registered itsvisualizer:render:chart:startlistener, 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
1474and1478are present, chart data is localized, andvisualizer-pro-render,visualizer-customization,visualizer-render-google-lib, andvisualizer-render-facadeare emitted asdata-visualizer-scripttags withoutsrc. The same page hasgoogle-jsapiloaded 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_scriptis true; line 115 replacessrcwithdata-visualizer-scriptfor handles containingvisualizer-, excluding onlygoogle-jsapi,chartjs, andvisualizer-datatables.classes/Visualizer/Module/Frontend.php:386-393,Visualizer_Module_Frontend::renderChart()keeps$this->lazy_render_scriptenabled when chart settings includelazy_load_chart, independent of the shortcodelazyattribute. The reported page source confirmslazy_load_chart: truefor the affected charts.classes/Visualizer/Module/Frontend.php:492-518,Visualizer_Module_Frontend::renderChart()registersvisualizer-render-facadewith dependencies returned byvisualizer_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()registersvisualizer-render-google-liband 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 allscript[data-visualizer-script]nodes and startsjQuery.getScript()calls without sequencing, then callsvisualizerRefreshChart()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 asviz-facade-loadedand callsshowChart(id), which triggersvisualizer:render:chart:start.js/render-google.js:561-624, the Google renderer registers thevisualizer:render:chart:startlistener. If this script loads after the facade has already fired the event, that initial render signal is not observed.53d1e0ac(Revert "fix: lazy loading not always working") changed the lazy script loader to the currentjQuery.getScript()approach and is contained inv4.0.0,v4.0.1, andv4.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-pluginscript_loader_tag()path because the handles containvisualizer-. Pro appears involved as an affected companion script, not the primary shortcode renderer.Engineering notes
lazy="no"only affects thevisualizer-lazyclass path inrenderChart(). It does not override the saved chart settinglazy_load_chart, which controlsvisualizer-lazy-renderand the delayed script-loading branch.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.columnandline), and the concrete event-listener race was verified againstrender-google.js.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
lazy_load_chart: true, embedded via shortcode withlazy="no"and via shortcode without thelazyattribute.visualizer-render-facade.jscan execute beforerender-google.js, and whethervisualizer:render:chart:startfires before the Google renderer listener exists.53d1e0acor beforev4.0.0to confirm the version boundary.visualizer-chart-loadedafter the delayed Visualizer scripts are loaded.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)