← Back to the work journal

Work Journal

How I Fixed a Broken Funnel Caused by JavaScript Loading Order

How I traced a mobile-only production failure to JavaScript execution order, WordPress optimization, and a hidden dependency between two scripts.

24 July 2026 Matteo Arellano
javascriptwebsite optimizationconversion rate optimizationclient success story

Today I fixed a critical production issue that initially looked like a slow website.

The page itself was loading, the navigation worked and images appeared. Text was visible, however the CTA to move forward in the funnel was just broken. It wouldn’t load.

For many mobile visitors, the part of the page that actually allowed them to continue toward a purchase was either taking more than 5 minutes to appear or failing completely. For me that was the main blocker to solve.

This is a revenue-critical issue on websites where I’m most proud to solve, because my clients get to start selling shortly after this is solved.

The first symptom was misleading

The initial report was that the page was taking too long to load.

When I inspected the behavior more closely, I found that the browser was loading most of the page normally and then waiting on a few external assets. Some of those requests were slow or failing.

The interactive search flow was initialized with JavaScript that waited for the browser’s full load event:

window.addEventListener("load", function () {
  // Initialize the commercial interface
});

The full load event does not simply mean that the HTML is ready. It waits for the page’s dependent resources, including images, fonts, stylesheets, scripts, and other files.

That created a fragile dependency:

Slow external image
→ full page load is delayed
→ JavaScript initialization is delayed
→ the commercial interface is delayed
→ the visitor cannot continue

One broken image could therefore hold a revenue-critical feature hostage.

The page looked visually present, but the product was not operational.

The first correction exposed a second problem

I moved the initialization from the full load event to DOMContentLoaded.

document.addEventListener("DOMContentLoaded", function () {
  // Initialize the commercial interface
});

DOMContentLoaded fires when the browser has parsed the HTML and created the document structure. It does not wait for every image or font to finish downloading.

This was the correct lifecycle event for the job. The interface only needed its HTML container to exist. It did not need to wait for the rest of the page.

The result improved immediately on desktop.

Mobile still failed. I had to dig deeper and get better understanding.

A weaker debugging process could have stopped there and concluded that the third-party product was incompatible with mobile. Instead, the difference between desktop and mobile became the next clue.

The real issue was execution order

The interface depended on two separate pieces of JavaScript.

The first was an external software development kit. It created the functionality that the page needed:

window.ExternalLibrary

The second was the page’s own initialization code:

ExternalLibrary.Item.mount(...)

The dependency was strict:

External library loads
→ its methods become available
→ page initialization calls those methods
→ interface appears

The WordPress site also used a performance extension that optimized JavaScript for public visitors. Its job was to improve page speed by delaying, deferring, combining, or reordering scripts.

That can be useful for independent scripts.

It is dangerous when one script depends on another.

For mobile visitors, the optimization layer could delay the external library while allowing the inline initialization code to continue. The page was effectively doing this:

ExternalLibrary.Item.mount();

before ExternalLibrary existed.

The browser was being asked to use a tool that had not arrived yet.

This is a classic race condition. Two processes are running, and the result depends on which one finishes first.

On desktop, the external file might already be cached or load quickly enough. The code appears correct.

On mobile, with a different cache state, connection, device, and visitor context, the initialization can win the race and fail before its dependency is ready.

The same page can therefore seem healthy to the person editing it while being broken for the users who matter.

How I proved it

I did not want to keep changing production code based on guesses.

I compared the same page under two conditions:

Normal public page
→ mobile flow failed

Page with the optimization layer bypassed
→ mobile flow loaded correctly

That test isolated the problem.

The external library worked. The configuration worked. The mobile browser worked. The WordPress page worked.

The failure only appeared when the JavaScript optimization layer was involved.

This was the decisive moment because it converted a theory into evidence.

The solution

The final correction had several parts.

First, the interface now initializes when the document structure is ready instead of waiting for the entire page and all of its assets.

Second, the initialization code checks whether its external dependency is actually available before trying to use it.

Third, it avoids mounting the same interface repeatedly when users interact with the page.

Fourth, it provides safe default values when browser storage does not yet contain a language or currency preference.

Finally, the JavaScript optimization responsible for altering the execution sequence was disabled until it can be configured and tested without interfering with the commercial flow.

The priority was simple:

Correct functionality
→ reliable mobile experience
→ measurable conversion
→ optimization afterward

A faster broken page has no business value.

The business impact

Honestly, this was one of the biggest success stories with a client.

The problem sat directly between the landing page and the start of the purchase journey. Visitors could arrive, read the content, and still be unable to use the feature that moved them toward checkout.

The fix removed a sales-critical blocker, especially for mobile visitors.

I cannot yet claim a revenue increase before enough post-fix data exists. But the operational impact is already clear: a flow that could be unavailable is now usable again.

That is the difference between measuring a slow page and understanding where the business actually stops.

What this taught me

JavaScript performance is not only about reducing file size or improving a speed score.

It is also about dependency order.

A WordPress extension can make a page appear faster while silently breaking the sequence required by a commercial feature. A script marked as deferred can still be moved, transformed, or treated differently by another layer. A page can work for an authenticated administrator and fail for a normal visitor. Desktop testing can hide a mobile production incident.

The most important debugging question to me today was:

What must exist before this code is allowed to run?

Once that dependency became visible, the entire problem became easier to reason about.

The result came from following the chain all the way through:

Browser lifecycle
→ external assets
→ JavaScript dependency
→ WordPress optimization
→ mobile behavior
→ transactional availability
→ business impact

What really surprises me is how vast, complex and interdependent are modern web applications on external libraries, optimizations, and how one small misaligned or misconfiguration can throw away all the good stuff out there. Watch out for the extensions you use in your CMS pages! They might look good on paper but do no good to your own website if you don’t understand them deep enough.