Die Entstehung der interaktiven Treehouse-Anzeige

Avatar of Chris Coyier
Chris Coyier am

DigitalOcean bietet Cloud-Produkte für jede Phase Ihrer Reise. Starten Sie mit 200 $ kostenlosem Guthaben!

Es ist für jeden, der diese Seite besucht, wahrscheinlich offensichtlich: Treehouse ist unser Hauptsponsor. Das gefällt mir. Nicht nur, weil sie strukturiertes Lernen anbieten, das die Lernmaterialien ergänzt, die ich hier habe, sondern auch, weil ein einziger Hauptsponsor bedeutet, dass wir mehr Zeit für die Integration von Anzeigen auf unterhaltsame Weise haben.

Im Gespräch mit den Leuten von Treehouse erfuhren sie, dass sie neue jQuery-basierte Inhalte hatten, die sie bewerben wollten. Ich dachte mir, ich würde einen kleinen interaktiven Bereich erstellen, um den Leuten Appetit auf das zu machen, was jQuery leisten kann, und sie neugierig auf mehr zu machen.

Gehen wir durch, wie es gebaut wurde.

Die Idee

  1. Ein „Leinwand“-Bereich (nicht <canvas>, sondern einfach ein Bereich, in dem Dinge passieren)
  2. Eine Reihe von fünf einfachen jQuery-Code-Schnipseln
  3. Klicke auf einen Button, Code wird ausgeführt, etwas passiert auf der Leinwand
  4. Du kannst den Code sehen, den du gleich ausführen wirst
  5. Es sollte visuell klar sein, dass der angezeigte Code derjenige ist, der ausgeführt wird und die Änderungen vornimmt
  6. Die gesamte Leinwand ist währenddessen eine klickbare Anzeige für Treehouse (Nutzer müssen nicht mit den jQuery-Sachen interagieren, es ist immer noch nur eine Anzeige)
  7. Du kannst die interaktiven Schritte nach Abschluss neu starten

Hier ist das Endergebnis. Demo auf CodePen

Das HTML

Relevante Stellen kommentiert

<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="//css-tricks.de/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>

Das CSS

Nicht viel Interessantes hier, also lass uns nicht zu lange dabei verweilen. Im Grunde habe ich die Anzeige so gestaltet, dass sie bereits verwendete Stile von CSS-Tricks wiederverwendet, damit die Anzeige sich wie zu Hause fühlt. Der Codeblock verwendet die gleiche Code-Formatierung wie überall sonst auf der Seite. Das Modul hat wie alle anderen Module eine Leiste oben. Der Button hat den globalen Button-Stil. So etwas.

Ich habe einen gepunkteten Rahmen um die „Leinwand“ gelegt, da ich dachte, das verleiht ihr ein etwas bearbeitbares/zielgerichtetes Gefühl.

Das JavaScript

Jetzt müssen wir diese Sache zum Laufen bringen. Das bedeutet…

  1. Wenn der Button geklickt wird…
  2. Führe den Code aus
  3. Inkrementiere den Schritt
  4. Wenn es der letzte Schritt ist, richte alles für den Neustart ein

Die Struktur

Das JavaScript, das wir schreiben, ist im Wesentlichen ein „Modul“. Alles hängt zusammen. Wir sollten ihm eine Struktur geben, um das offensichtlich zu machen. Der beste Weg, den ich kenne, ist, ein Objekt zu erstellen, das alles enthält.

var TreehouseAd = {

  // Everything related to this ad goes in here.

};

Das sieht normalerweise ungefähr so aus

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();

Die Verwendung einer solchen Struktur führt zu lesbarerem, besser testbarem Code und fühlt sich einfach insgesamt besser an als ein Haufen disparater Funktionen und Variablen (Spaghetti).

Die „Schritte“

Wir werden fünf Schritte haben. Das heißt, die verschiedenen jQuery-Teile, die ausgeführt werden und verschiedene Dinge mit unserer Anzeigen-Leinwand tun.

  1. Füge der Verknüpfung die Klasse „button“ hinzu
  2. Bewege sie ein wenig nach oben (demonstriert Animation)
  3. Ändere den Text des Buttons
  4. Füge ein neues Element hinzu
  5. Füge dem gesamten Anzeige-Element eine Klasse hinzu, um ihm ein fertiges Aussehen zu verleihen

Wir müssen auch zwei Dinge mit jedem Code-Schnipsel tun

  1. Anzeigen
  2. Ausführen

Wir möchten den Code nicht an zwei separaten Stellen pflegen, also tun wir das einfach nicht. Wir speichern die fünf verschiedenen Code-Schnipsel in einem Array (geben ihm eine Struktur, anstatt fünf separater Variablen). Jeder Eintrag im Array ist ein Code-String. Wir können diesen String abrufen und anzeigen oder abrufen und eval() ausführen.

Ich weiß. Eval() ist böse. Wir könnten es vermeiden, indem wir den Beispielcode an verschiedenen Stellen pflegen. Oder gibt es eine bessere Idee?
var TreehouseAd = {

   ...

   codeText: [

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

   ...

}

Das endet dann so

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!')" 
  
],

Beachte all die Schrägstriche und „n“ am Ende der Zeilen? Ein String, der in JavaScript mit „\“ endet, bedeutet nur „dieser String wird in der nächsten Zeile fortgesetzt.“ Er erscheint nicht im String selbst. „\n“ bedeutet „neue Zeile“, in unserem Fall brauchen wir das, damit der Code korrekt formatiert ist, wenn wir ihn in das <pre>-Element einfügen. Er hat keine Auswirkungen auf eval().

Die Animation

Um visuell klarzumachen, was passiert, wenn man auf den „Code ausführen“-Button klickt, dachte ich, es wäre eine coole Idee, wenn der Code nach oben gleitet und auf der „Leinwand“ verblasst. Die Schritte werden dann zu

  1. Erstelle eine Kopie/Duplikat des Code-Bereichs direkt über dem vorhandenen
  2. Animierte Position nach oben, während sie verblasst
  3. Entferne die Kopie, wenn fertig
  4. Warte, bis die Code-Text-Änderung erfolgt, nachdem die Animation abgeschlossen ist

Ich betrachte diese Aktion als eine „Funktionseinheit“. Das bedeutet, sie verdient eine eigene Funktion als Teil unseres Objekts.

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(); 
    });

  },

  ...

};

Die Innereien

Der endgültige Code sieht so aus. Er enthält die Logik für die Ausführung des Codes und das Zurücksetzen und so weiter.

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();

Das Ende

Noch einmal, die Demo auf CodePen

Du kannst auch eine modifizierte Version davon live auf dieser Seite sehen! Wir haben sie schon ein paar Mal aus Spaß geändert. Wahrscheinlich werden wir das auch weiterhin tun. Vielen Dank an Treehouse, dass ich solche Spaßprojekte machen darf. Schaut sie euch an, wenn ihr ein Fan von CSS-Tricks seid.