Description

Refactoring ScrewTurn Wiki’s JavaScript

The first step in refactoring ScrewTurn Wiki’s JavaScript is to extract it into separate JavaScript files. Not only will this allow us to test the JavaScript, but it also will improve page performance. This is due to decreased page size, as the browser can
cache the JavaScript files rather than downloading them with every dynamic page. This is mostly a simple operation of moving the JavaScript from MasterPage.master and Default.aspx to ~/JS/ScrewTurnWiki.js, and including a reference to ScrewTurnWiki.js in MasterPage.master.
(Moving the JavaScript from other pages to ScrewTurnWiki.js is left as an exercise for the reader.) ScrewTurn Wiki’s MasterPage.master calls Tools.GetIncludes() to automatically generate <script type=”text/javascript”/> tags for all JavaScript files in ~/JS/
and ~/Themes/<NAME>/, so we do not have to manually add ScrewTurnWiki.js.

After moving the scripts to ~/JS/ScrewTurnWiki.js, ScrewTurn Wiki’s main page looks like this:

Notice the “Name Size” box that appears at the bottom of the page beneath the Warning? This is the PageAttachmentsDiv that is supposed to be hidden. Here is the code to hide that:

Before moving the code, the above JavaScript was placed after the <div id=”PageAttachementsDiv”/> . When the code executed, the <div/> existed and was hidden properly. When the code is moved into ScrewTurnWiki.js, it is executed before the <div/> is created
and nothing happens. The <div/> is later created and remains visible. The solution is to simply move the above loose JavaScript into a doWindowLoad() function and execute that function when the window finishes loading:

// Additional code that needs to executed when the HTML DOM // is loaded and ready for manipulation }

ScrewTurn Wiki now looks and behaves the way it did before the JavaScript was relocated, as we see below.

Now that the JavaScript has been relocated into a separate file, we can use QUnit to test whether PageAttachmentsDiv is hidden when the page loads. In QUnitTests.html – our HTML page test harness – we create a dummy <div/> with the same ID:

<div id="PageAttachmentsDiv" style="width:100px;height:100px;"></div>

Then in our test, we explicitly call the doWindowLoad() function and verify that PageAttachmentsDiv is hidden:

Using dummy page elements in the test harness is easier than trying to test the actual page elements on the live page. To avoid polluting the test harness output with dummy elements, the containing <div/> can be marked as hidden (display:none;) or absolutely
positioned outside of the visible content (position:absolute;left:-1000px;top:-1000px;). The choice of technique depends on what you are trying to test, as hiding the <div/> sometimes interferes with layout-related tests.

Getting Jiggy with jQuery

With our QUnit test harness in place, we are ready to refactor ScrewTurn Wiki’s JavaScript using jQuery. Let’s start by taking a look at hiding the PageAttachmentsDiv:

(Note that you need to include a reference to the jQuery library in your page using a <script/> tag.) The jQuery version is terser, using the CSS-style “#” to indicate ID-based selection, and the more declarative “hide()” method. It might seem like a small
savings until you realize the power disguised behind the apparent simplicity. What if we want to hide all menus on the page with the CSS class of “menu”?

$('.menu').hide();

There is no need to walk the HTML DOM or iterate for-loops. jQuery handles all of this transparently for you.

jQuery uses CSS-like selectors to operate on collections of HTML DOM elements. So we can play even more powerful tricks, such as hiding all menus, but only if their direct parent is a <div/> with CSS class “toplevel”:

$('div.toplevel > .menu').hide();

jQuery is not only encouraging us to write less code, but also a more functional style of code. To make this clearer, consider hiding all menus using procedural-style code:

Start with the document element.

For each child element

If it is a menu, change its style to display: none.

Else, iterate through its children from Step 2.

A functional style says:

Hide all menus.

Functional-style code says what to do rather than how to do it. This is a subtle distinction, but an important one.