The Making of the Interactive Treehouse Ad

Published by Chris Coyier

It's likely obvious to anyone visiting this site: Treehouse is our primary sponsor. I like that. Not only because they offer structured learning that compliments the learning material I have here, but also because having a single main sponsor means we can spend more time integrating ads into the site in a fun way.

In talking to the folks over at Treehouse, they told me they had some new jQuery based content worth promoting. I thought I'd create a little interactive area to kind of wet people's whistle on what jQuery can do, and get them excited to learn more about it.

Let's go through how it was built.

The Idea

  1. Have a "canvas" (not <canvas>, just an area where things happen)
  2. Have a series of five bits of simple jQuery code
  3. Click a button, code runs, something happens in canvas
  4. You can see the code you are about to execute
  5. It should be visually clear the code you see is what is running and making the changes
  6. The whole time, the canvas area is a clickable ad for Treehouse (users aren't required to interact with the jQuery stuff, it's still just an ad)
  7. You can start over with the interactive steps when completed

Here's what we end up with. Demo on CodePen:

The HTML

Relevant bits commented:

<div class="ad">
   
  <!-- Our jQuery code will run on elements in here -->
  <div class="canvas" id="canvas">
    <a id="treehouse-ad" href="http://teamtreehouse.com" target="_blank">Team Treehouse!</a>
    <img src="http://cdn.css-tricks.com/wp-content/themes/CSS-Tricks-10/images/mike-standard.png" alt="Mike">
  </div>
  
  <div id="code-area-wrap">

    <h3>jQuery 
      <!-- We'll increment the "step", so we need an element to target -->
      <span id="step-counter">1/5</span>
    </h3>

    <!-- Code is visually displayed in here -->
    <pre class="code-area" id="code-area">var treehouse = 
$('#treehouse-ad')
  .addClass('button');</pre>

  </div>
  
  <div class="run-code-wrap">

    <!-- This will be pressed to run the code -->
    <a href="#" class="button" id="run-code">Run Code</a>

  </div>
  
</div>

The CSS

Not much interesting here, so let's not dwell on it. Essentially I made the ad re-use styles already in use on CSS-Tricks, so the ad feels at home. The block of code uses the same code styling as anywhere else on the site. The module has a bar on top like all other modules. The button is the global button style. That kind of thing.

I put a dotted border around the "canvas" as I thought that gives it a bit of an editable/target kind of feel.

The JavaScript

Now we need to make this thing work. Which means...

  1. When button is clicked...
  2. Execute the code
  3. Increment the step
  4. If it's the last step, put stuff in place to start over

The Structure

The JavaScript we write is essentially a "module". It's all related to each other. We should give it some structure to make that obvious. The best way I know how is to make an object that contains it all.

var TreehouseAd = {

  // Everything related to this ad goes in here.

};

That usually ends up something like this:

var TreehouseAd = {

  importantVariableOne: "foo",
  importantVariableTwo: "bar",

  init: function() {
    // The one function that will get called to kick things off

    this.bindUIActions();
  },

  bindUIActions: function() {
    // bind events here
    // have them call appropriately named functions
    
    $("button").on("click", function(event) {
       TreehouseAd.doSomething(event, this);
    });
  },

  doSomething: function(event, target) {
    // do something
  }

};

TreehouseAd.init();

Using structure like this makes for more readable code, more testable code, and just all around feels better than a bunch of disparate functions and variables (spagetti).

The "Steps"

We'll have five steps. As in, the different bits of jQuery that execute and do different things to our ad canvas.

  1. Add a "button" class to the link
  2. Move it up a little bit (demonstrating animation)
  3. Change the text of the button
  4. Add a new element
  5. Add class to the entire ad, giving it a finished feel

We'll also need to do two things with each bit of code:

  1. Display it
  2. Execute it

We'd rather not maintain the code in two separate places, so let's just not. We'll store the five different code bits in an array (giving it some structure, rather than five separate variables). Each item in the array is a string of code. We can grab that string and display it, or grab that string and eval() it.

I know. eval() is evil. We could avoid it by maintaining the code sample in different places. Or is there a better idea?
var TreehouseAd = {

   ...

   codeText: [

      "code for step 1",
      "code for step 2",
      ...
   ],

   ...

}

That ends up like:

codeText: [

  "treehouse = \n\
  $('#treehouse-ad')\n\
    .addClass('button');",
  
  "treehouse.animate({\n\
  top: '-40px'\n\
});",
  
  "treehouse.text(\n\
  'Learn jQuery at Treehouse!'\n\
);",
  
  "$('<div />', {\n\
  id: 'tagline',\n\
  text: "Hi, I'm Mike."\n\
}).insertAfter(treehouse);",
  
  "$('#canvas')\n\
 .addClass('all-done');\n\n\
console.log('Thanks for playing!')" 
  
],

Notice all the slashes and 'n's at the end of the lines? A string that ends in "\" in JavaScript just means "this string to be continued on next line." It doesn't end up in the string itself. "\n" means "new line," in our case, we need that so the code is formatted correctly when we place it inside the <pre> element. It won't effect the eval().

The Animation

To make it visually clear what is going on when you press the "Run Code" button, I thought having the code kind of slide up and fade into the "canvas" would be a cool idea. The steps then become:

  1. Make a clone/duplicate code area directly on top of existing one
  2. Animate position upwards while fading out
  3. Remove clone when done
  4. Wait to change code text until animation has completed

I think of this action as one "chunk" of functionality. Meaning it deserves it's own function as part of our object.

var TreehouseAd = {

  ...

  codeArea: $("#code-area"),
  delay: 400,

  animateCode: function() {

   this.codeArea
    .clone()
    .addClass("clone")
    .insertAfter(this.codeArea)
    .animate({
      top: "-60px",
      opacity: 0
    }, TreehouseAd.delay, function() {
      $(".clone").remove(); 
    });

  },

  ...

};

The Guts

The final code ends up like this. This has the logic in it for running the code and resetting and all that.

var TreehouseAd = {
  
  step: 0,
  delay: 400,
  codeArea: $("#code-area"),
  counter: $("#step-counter"),

  init: function() {
    this.bindUIActions();
  },
  
  codeText: [
  
    "treehouse = \n\
    $('#treehouse-ad')\n\
      .addClass('button');",
    
    "treehouse.animate({\n\
    top: '-40px'\n\
  });",
    
    "treehouse.text(\n\
    'Learn jQuery at Treehouse!'\n\
  );",
    
    "$('<div />', {\n\
    id: 'tagline',\n\
    text: "Hi, I'm Mike."\n\
  }).insertAfter(treehouse);",
    
    "$('#canvas')\n\
   .addClass('all-done');\n\n\
  console.log('Thanks for playing!')" 
    
  ],
  
  bindUIActions: function() {
    
    $("#run-code").on("click", function(e) {
      e.preventDefault();
      TreehouseAd.animateCode();
      TreehouseAd.runCode();
    });
                      
  },
                      
  animateCode: function() {
      
   this.codeArea
    .clone()
    .addClass("clone")
    .insertAfter(this.codeArea)
    .animate({
      top: "-60px",
      opacity: 0
    }, TreehouseAd.delay, function() {
      $(".clone").remove(); 
    });
      
  },
    
  runCode: function() {
    
    setTimeout(function() {
  
      if (TreehouseAd.step < 5) {
         
        eval(TreehouseAd.codeText[TreehouseAd.step]);
        
        TreehouseAd.step++;
        
        TreehouseAd.counter.text((TreehouseAd.step+1) + "/5");
        TreehouseAd.codeArea.text(TreehouseAd.codeText[TreehouseAd.step]);
                
      }
      
      if (TreehouseAd.step == 6) {
          
        // reset canvas
        treehouse
          .text("Team Treehouse!")
          .removeClass()
          .removeAttr("style");
        
        $("#tagline").remove();
        $("#canvas").removeClass("all-done");
        $("#run-code").text("Run Code");
        
        TreehouseAd.step = 0;
        TreehouseAd.codeArea.text(TreehouseAd.codeText[TreehouseAd.step]);
        TreehouseAd.counter.text((TreehouseAd.step+1) + "/5");
        
      }
      
      if (TreehouseAd.step == 5) {
        
        $("#run-code").text("Start Over");
        TreehouseAd.codeArea.text("");
        TreehouseAd.counter.text("Done!");
        
        TreehouseAd.step++;
        
      }
    
    }, TreehouseAd.delay);
    
  }
  
}; 

TreehouseAd.init();

The End

Again, the demo on CodePen:

You can also see a modified version of it live on this site! We've changed it up a few times already for funzies. Probably will keep doing so. Big thanks to Treehouse for letting me do fun stuff like this. Check them out if you are a fan of CSS-Tricks.