The Problem

Sometimes when rendering a web page using a javascript framework like Knockout, the user will see a flash of unstyled content before all of the pieces of the web page pop into place. This is called flash of unstyled content (FOUC). The cause of this flash is that the HTML in the web page is being rendered before the Javascript and CSS files have been downloaded and applied to the page. This can be prevalent in frameworks such as Knockout, Angular, and Ember, since the elements which are affected by the data-bind property are not evaluated until the framework has been instantiated on page load. This flash can be jarring and leads to a poor  user experience, potentially impacting your conversion rates.

The Pure HTML Solution

The solution is to hide the content until the bindings are evaluated, then display the hidden content to the user:

This works because initially the the content is hidden using the CSS style “display: none”.

     <div style='display:none' data-bind='visible: true'>

          <!-- Your content -->

     </div>

Meanwhile the user is displayed a spinner to indicate the loading is happening.

     <div data-bind='visible: false'>

          Loading<img src='loading.gif' alt='Loading Spinner'/>

     </div>

Once the Knockout framework is loaded and scans the page content, it will evaluate the data bindings that are on both of those div elements. After it has been evaluated, the visibility on the divs are flipped. This will hide the loading div element and show the content which has already been styled behind.

If the whole website is using knockout, it might be cumbersome to put this fix onto every page. Luckly if you are using ASP.NET MVC, you can create a handy HTML helper to automatically wrap the content with the HTML solution.

The C# Helper Solution

Create a class called BeginKnockoutLoadingContainerWriter and copy and paste the following code: 

This also includes the actual HTML helper class called BeginKnockoutLoadingContainer that will leverage the writer class we just created.

How does it work?

To use this helper in your MVC Razor views, use the new helper just like you would use the BeginForm helper:

Note: The namespace that the helper was created in may be needed to be imported into the view

(Ex:@usingMyNamespace.Knockout)

This works by creating an instance of the BeginKnockoutLoadingContainerWriter which runs its constructor method containing the code:

     // Write the html

     _viewContext.Writer.Write("<div>");

     _viewContext.Writer.Write("<div data-bind='visible: false'>");

     _viewContext.Writer.Write("Loading <img src='/assets/img/ajax-loader.gif' alt='Loading Spinner'/>");

     _viewContext.Writer.Write("</div>");

     _viewContext.Writer.Write("<div style='display:none' data-bind='visible: true'>");

Which outputs the following HTML:

     <div>

          <div data-bind='visible: false'>

               Loading<img src='loading.gif' alt='Loading Spinner'/>

          </div>

          <div style='display:nonedata-bind='visible: true'>

After the this, control is returned to the razor view. The razor parser will output the rest of the view contents that is within the using statement. When the closing brace of the BeginKnockoutLoadingContainer is encountered by the razor parser, the using statement disposes of the BeginKnockoutLoadingContainerWriter object.

     @using(Html.BeginKnockoutLoadingContainer()) {

          <!-- Your content -->

     }<!-- This bracket disposes of the object-->

When the class is disposed, the Dispose method gets called on the class because it implements the interface IDisposable:

     publicvoidDispose()

     {

          _viewContext.Writer.Write("</div></div>");

     }

Which outputs the closing div tags to the view after the content within the using statement:

          </div>

     </div>

Conclusion

FOUC can be an annoying distribution for a user. Creating this reusable helper can reduce the time to create pages which need FOUC prevention and create a single place to make updates to the implementation. Feel free to use this in your own project!

View all of the code snippets here.