Outspeaking.com

where words mean things

Building a Site Tutorial with Bootstrap and JavaScript

When we built Trendshare, we knew our ideal user: novice investors like our fathers. They're both smart, hard-working men who've earned every dollar they saved, but they've never felt confident enough to manage their own investments. Rather than let them continue to sink money paying brokers to get them lousy returns, we wanted to help them get the knowledge and confidence to put most of their money into low-cost index funds and a few percent for rare-but-important value investing bargains.

When we started to build the site, we thought the most valuable part would be the individual stock analysis pages which use discounted cash flow and owner earnings to calculate both a fair and a margin of safety price for individual stocks (assuming a desired interest rate of 15%—aggressive, but not unreasonable). As you can see from the Coca-Cola stock valuation, the result has a lot of facts and figures. If you know what's going on it all makes sense, but there's a lot to learn before you can evaluate the stock at a glance.

As things turned out, the most valuable part of the site became the investor's guide which explains principles, strategies, ratios, and events in investing, but that's a different story for another time.

When we began, we thought the site might derive enough revenue from monthly subscriptions to the members-only analysis section. (We've since pivoted after realizing that the revenue from minimal ads on pages such as the risks of trading penny stocks was greater than our expected subscription revenue and required a lot less customer support.) To make that analysis page work, we built a very simple tour of Trendshare features using Twitter's Bootstrap and a little bit of custom JavaScript. It's simple and easy and we've been pleased with the results.

Bootstrap Features Used in the Tour

We started with Bootstrap 2 and its JavaScript plugins. Bootstrap popovers are little windows that pop up with extra information. More important for our purposes, they look like little dialog balloons in comics, so they point at unique elements on a page. They can contain arbitrary HTML. For our purposes, we wanted little buttons for "Next item in tour" and "Finished with tour".

A Simple Bootstrap Popover

To add a popover to an element, call $( '#identifier' ).popover( options ) from JavaScript. For example, if you have a button that will upload everything in your Documents/ directory to the NSA and that button has a CSS identifier of "i-have-nothing-to-hide", you might create a popover with the code:

$( '#i-have-nothing-to-hide' ).popover({
    'title': 'About to Publish Everything',
    'content': 'Either you work for iGoogFace or you are a true patriot. Are you sure you really want to upload your Mark Zuckerberg Meets the Smurfs fanfic to the NSA?',
    'trigger': 'hover',
});

You can't prevent people from doing awkward things, but at least you can warn them.

Managing a Sequence of Popovers

You begin to see how we built the tour. Every element which needs explanation needs a unique CSS id, so we modified the template for stock analysis pages. While we did that, we made careful note of the positions of each element relative to each other because we'd need them later.

We knew we were like to add and remove and reorder the popovers so the tour flows well, so we didn't want to hardcode a bunch of calls to add popovers. Furthermore, the popovers have a specific ordering, so we wanted each popover to have a "Next" button or a "Done" button to move to the next item or to end the tour. That meant we'd have to change the HTML within the popover to add the button depending on the popover's position within the tour.

For all this to work, we needed a couple of things: an array of popover options (titles, contents, positions) and some helper functions to set everything up when the document is ready.

The array is easy:

var popovers = [];

var makePopover = function( selector, options ) {
    var object        = $( selector );
    options.animation = false;
    object.popover( options );
    popovers.push( object );
    return object;
};

makePopover( '#trendshare_stock_analysis', {
    'title': 'Trendshare Stock Analysis',
    'content': "<p>Welcome to the heart of Trendshare, the stock analysis page! This page shows everything we know about the stocks we've analyzed. There's a lot of information here, but you'll pick it up pretty quickly. Click on the NEXT button to go through it one step at a time!</p>",
    'trigger': 'manual',
    'placement': 'bottom',
});

Instead of adding each group of popover options to the array directly, we use the makePopover helper function to create and add the Bootstrap popover objects to the array themselves. This saves a little time later on. The first argument to this function is the CSS id of the element being explained.

With all of the popover objects in an array, making buttons is easy with a helper:

var makeButton = function( i ) {
    var text = i < popovers.length - 1 ? 'NEXT →' : 'DONE';
    return '<br /><p><input type="button" value="' + text
         + '" id="tourNavButton' + i + '" /></p>';
}

Buttons get added only as necessary. (It seemed like a good idea at the time. It certainly makes figuring out the last button easier.)

Finally, there's a helper function to display a popover. It's not great JavaScript, but it does close over itself in a fun way:

var showPopover;
showPopover = function( i ) {
    var obj          = popovers[i];
    var button       = $( makeButton( i ) );
    obj.popover( 'toggle' );
    var new_position = $( '.popover' ).offset();
    var content      = $( '.popover-content' );
    button.appendTo( content );
    window.scrollTo( new_position.left, new_position.top - 60 );

    button.click(function () {
        obj.popover( 'toggle' );
        content.detach();
        i++;
        if (i < popovers.length)
        {
            showPopover( i );
        }
    });
};

The first half of the code creates the button, makes the popover visible, and scrolls the page so that the popover is within the current viewable area. The second half of the function attaches a click handler to the newly-created button to hide the current popover destroy the new content (with the current button), and then displays the next popover in the sequence (if it exists).

Starting the entire sequence is easy:

showPopover( 0 );

From there, users can follow the tour by clicking the Next buttons or closing popovers altogether.

Concluding Thoughts

This code isn't great—definitely not the best JavaScript we've ever written—but it meets the site's needs. It fits into the Bootstrap framework without requiring much else. It uses existing CSS. It only requires a little bit of markup on real pages.

Adding to the tutorial does mean modifying the JavaScript file. In practice, it'd probably be better to put the popup text somewhere in the DOM of the tutorial page, but that didn't work so well for this situation. (Trendshare also has a help mode available on every stock analysis page, so putting the popup text in the JavaScript worked best for us.)

For an afternoon's worth of work, we believe we improved the site's introduction to novice users. Even if the site's first revenue model didn't work out the way we had planned (seriously, the site would be profitable even if it all it did was to warn people away from penny stocks!), building this simple tutorial system was worth it.