Springen zu:

Diese Übersetzung ist unvollständig. Bitte helfen Sie, diesen Artikel aus dem Englischen zu übersetzen.

Nachdem alle Designziele und Programmierstandards festgelegt wurden, ist es an der Zeit, in die Programmierung unserer App einzusteigen.

HTML

Wie bei jedem Web Application Template benötigen wir eine HTML-Seite mit dem HTML5-Doctype, <head>, und dem <body> Element.

<!doctype html>
<html>
<head>
  <title></title>
 
  <meta charset="utf-8" />
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
 
  <meta name="viewport" content="width=device-width, initial-scale=1">
 
  <!-- stylesheets go here -->
 
</head>
<body>
 
  <!-- HTML app structure goes here -->
 
 
  <!-- javascript goes here -->
 
</body>
</html>

Wir werden jQuery Mobile als JavaScript-Framework verwenden. Das bedeutet, dass wir die von jQuery Mobile benötigten CSS- und JavaScript-Ressourcen einbinden müssen:

<!-- stylesheets -->
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
	
<!-- scripts -->
<script src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>

Mit unserer grundlegenden Anwendung boilerplate und jQuery Mobile Ressource an Ort und Stelle, ist es Zeit, um Inhalte und Widgets auf der Seite hinzuzufügen. Da wir uns entschieden haben, jQuery Mobile für unser Anwendungs-Framework zu verwenden, wird die HTML-Struktur unserer Anwendung den vorgeschriebenen Widgets folgen. Der erste Bereich enthält ein Suchfeld und eine Verlaufsliste:

<!-- home pane -->
<div data-role="page" id="home">
 
  <div data-role="header">
    <h1>Area Tweet</h1>
  </div>
 
  <div data-role="content">
    
    <div class="ui-body ui-body-b">
      <h2>Location Search</h2>
      <form id="locationForm">
        <input type="search" name="location" id="locationInput" placeholder="Your Location" />
        <button type="submit" data-role="button" data-theme="b">Search</button>
      </form>
    </div>
    
    <div id="prevLocationsContainer" class="opaque">
      <h2>
        Previous Locations
        <a href="#" id="clearHistoryButton" data-role="button" data-icon="delete" data-iconpos="notext"
                data-theme="b" data-inline="true" style="top: 5px;">Clear History</a>
      </h2>
      <ul id="prevLocationsList" data-role="listview" data-inset="true" data-filter="true"></ul>
    </div>
    
  </div>
</div>

Es gibt ein paar Dinge zu beachten mit dem obigen Code:

  • Die Struktur des HTML5 hält sich an die Semantik, was eine maximale Zugänglichkeit gewährleistet.
  • Wie bei jeder webbasierten Anwendung ordnen wir CSS-Klassen und IDs den Elementen zu, auf die wir mit JavaScript-Code zugreifen wollen.

Wenn Sie Fragen zu einzelnen jQuery Mobile-Fähigkeiten, Knotenattributen oder Strukturen haben, konsultieren Sie bitte die Seite jQuery Mobile Documentation.

Der zweite Bereich wird für die Anzeige einer Liste von Tweets verwendet:

<!-- tweets pane -->
<div data-role="page" id="tweets">
 
  <div data-role="header">
    <a href="#home" id="tweetsBackButton">Back</a>
    
    <h1><span id="tweetsHeadTerm"></span> Tweets</h1>
  </div>
 
  <ul id="tweetsList" data-role="listview" data-inset="true"></ul>
 
</div>

CSS

Das CSS unserer Web-App enthält eine CSS-Animation zum Einblenden beliebiger Elemente, sowie zum Überschreiben von jQuery Mobile-Stilen und allgemeinem Element-Styling.

/* animations */
@-moz-keyframes fadeIn {
  0%    { opacity: 0; }
  100%  { opacity: 1; }
}
@-webkit-keyframes fadeIn {
  0%    { opacity: 0; }
  100%  { opacity: 1; }
}

.fadeIn {
  -moz-animation-name: fadeIn;
  -moz-animation-duration: 2s;
 
  -webkit-animation-name: fadeIn;
  -webkit-animation-duration: 2s;
 
  opacity: 1 !important;
}

/* Custom CSS classes */
.clear        { clear: both; }
.hidden       { display: none; }
.opaque       { opacity: 0; }


/* Basic styling */
#prevLocationsContainer { margin-top: 40px; }

/* Customizing jQuery Styles */
#tweetsList li.ui-li-divider,
#tweetsList li.ui-li-static     { font-weight: normal !important; }


/* Tweet list */
#tweetsList li {
 
}

#tweetsList li .tweetImage {
  float: left;
  margin: 10px 10px 0 10px;
}

#tweetsList li .tweetContent {
  float: left;
  /* margin: 10px 0 0 0; */
}

#tweetsList li .tweetContent strong {
  display: block;
  padding-bottom: 5px;
}

/* Media Queries for device support */
@media screen and (max-device-width: 480px) {
  #prevLocationsContainer { margin-top: 10px; }
}

JavaScript

JavaScript ist die letzte wichtige Komponente, die unserer Webapplikation hinzugefügt wird. Im Folgenden finden Sie alle Möglichkeiten der App:

$(document).ready(function() {
 
  // Start from scratch on page load
  if(window.location.hash) {
    window.location = "index.html";
  }
 
  // Feature tests and settings
  var hasLocalStorage = "localStorage" in window,
    maxHistory = 10;
 
  // List of elements we'll use
  var $locationForm = $("#locationForm"),
    $locationInput = $("#locationInput"),
    
    $prevLocationsContainer = $("#prevLocationsContainer"),
    
    $tweetsHeadTerm = $("#tweetsHeadTerm"),
    $tweetsList = $("#tweetsList");
    
  // Hold last request jqXHR's so we can cancel to prevent multiple requests
  var lastRequest;
 
  // Create an application object
  app = {
    
    // App initialization
    init: function() {
      var self = this;
      
      // Focus on the search box
      focusOnLocationBox();
      
      // Add the form submission event
      $locationForm.on("submit", onFormSubmit);
      
      // Show history if there are items there
      this.history.init();
      
      // When the back button is clicked in the tweets pane, reset the form and focus
      $("#tweetsBackButton").on("click", function(e) {
        $locationInput.val("");
        setTimeout(focusOnLocationBox, 1000);
      });
      
      // Geolocate!
      geolocate();
      
      // When the tweets pane is swiped, go back to home
      $("#tweets").on("swiperight", function() {
        window.location.hash = "";
      });
      
      // Clear history when button clicked
      $("#clearHistoryButton").on("click", function(e) {
        e.preventDefault();
        localStorage.removeItem("history");
        self.history.hideList();
      })
    },
    
    // History modules
    history: {
      $listNode: $("#prevLocationsList"),
      $blockNode: $("#homePrev"),
      init: function() {
        var history = this.getItemsFromHistory(),
          self = this;
        
        // Add items to the list
        if(history.length) {
          history.forEach(function(item) {
            self.addItemToList(item);
          });
          self.showList();
        }
        
        // Use event delegation to look for list items clicked
        this.$listNode.delegate("a", "click", function(e) {
          $locationInput.val(e.target.textContent);
          onFormSubmit();
        });
      },
      getItemsFromHistory: function() {
        var history = "";
        
        if(hasLocalStorage) {
          history = localStorage.getItem("history");
        }
        
        return history ? JSON.parse(history) : [];
      },
      addItemToList: function(text, addToTop) {
        var $li = $("<li><a href='#'>" + text + "</a></li>"),
          listNode = this.$listNode[0];
          
        if(addToTop && listNode.childNodes.length) {
          $li.insertBefore(listNode.childNodes[0]);
        }
        else {
          $li.appendTo(this.$listNode);
        }
        
        this.$listNode.listview("refresh");
      },
      addItemToHistory: function(text, addListItem) {
        var currentItems = this.getItemsFromHistory(),
          newHistory = [text],
          self = this,
          found = false;
        
        // Cycle through the history, see if this is there
        $.each(currentItems, function(index, item) {
          if(item.toLowerCase() != text.toLowerCase()) {
            newHistory.push(item);
          }
          else {
            // We've hit a "repeater": signal to remove from list
            found = true;
            self.moveItemToTop(text);
          }
        });
        
        // Add a new item to the top of the list
        if(!found && addListItem) {
          this.addItemToList(text, true);
        }
        
        // Limit history to 10 items
        if(newHistory.length > maxHistory) {
          newHistory.length = maxHistory;
        }
        
        // Set new history
        if(hasLocalStorage) {
          // Wrap in try/catch block to prevent mobile safari issues with private browsing
          // http://frederictorres.blogspot.com/2011/11/quotaexceedederr-with-safari-mobile.html
          try {
            localStorage.setItem("history", JSON.stringify(newHistory));
          }
          catch(e){}
        }
        
        // Show the list
        this.showList();
      },
      showList: function() {
        $prevLocationsContainer.addClass("fadeIn");
        this.$listNode.listview("refresh");
      },
      hideList: function() {
        $prevLocationsContainer.removeClass("fadeIn");
      },
      moveItemToTop: function(text) {
        var self = this,
          $listNode = this.$listNode;
        
        $listNode.children().each(function() {
          if($.trim(this.textContent.toLowerCase()) == text.toLowerCase()) {
            $listNode[0].removeChild(this);
            self.addItemToList(text, true);
          }
        });
        
        $listNode.listview("refresh");
      }
    }
    
  };
 
  // Search submission
  function onFormSubmit(e) {
    if(e) e.preventDefault();
    
    // Trim the value
    var value = $.trim($locationInput.val());
    
    // Move to the tweets pane
    if(value) {
      
      // Add the search to history
      app.history.addItemToHistory(value, true);
      
      // Update the pane 2 header
      $tweetsHeadTerm.html(value);
      
      // If there's another request at the moment, cancel it
      if(lastRequest && lastRequest.readyState != 4) {
        lastRequest.abort();
      }
      
      // Make the JSONP call to Twitter
      lastRequest = $.ajax("http://search.twitter.com/search.json", {
        cache: false,
        crossDomain: true,
        data: {
          q: value
        },
        dataType: "jsonp",
        jsonpCallback: "twitterCallback",
        timeout: 3000
      });
    }
    else {
      // Focus on the search box
      focusOnLocationBox();
    }
    
    return false;
  }
 
  // Twitter reception
  window.twitterCallback = function(json) {
    
    var template = "<li><img src='{profile_image_url}' class='tweetImage' /><div class='tweetContent'>"
                   +"<strong>{from_user}</strong>{text}</div><div class='clear'></div></li>",
      tweetHTMLs = [];
      
    // Basic error handling
    if(json.error) { // Error for twitter
      showDialog("Twitter Error", "Twitter cannot provide tweet data.");
      return;
    }
    else if(!json.results.length) { // No results
      showDialog("Twitter Error", "No tweets could be found in your area.");
      return;
    }
    
    // Format the tweets
    $.each(json.results, function(index, item) {
      item.text = item.text.
            replace(/(https?:\/\/\S+)/gi,'<a href="$1" target="_blank">$1</a>').
            replace(/(^|\s)@(\w+)/g,'$1<a href="http://twitter.com/$2" target="_blank">@$2</a>').
            replace(/(^|\s)#(\w+)/g,'$1<a href="http://search.twitter.com/search?q=%23$2" target="_blank">#$2</a>')
      tweetHTMLs.push(substitute(template, item));
    });
    
    // Place tweet data into the form
    $tweetsList.html(tweetHTMLs.join(""));
    
    // Refresh the list view for proper formatting
    try {
      $tweetsList.listview("refresh");
    }
    catch(e) {}
    
    // Go to the tweets view
    window.location.hash = "tweets";
  };
 
  // Template substitution
  function substitute(str, obj) {
    return str.replace((/\\?{([^{}]+)}/g), function(match, name){
      if (match.charAt(0) == '\\') return match.slice(1);
      return (obj[name] != null) ? obj[name] : "";
    });
  }
 
  // Geolocates the user
  function geolocate() {
    if("geolocation" in navigator) {
      // Attempt to get the user position
      navigator.geolocation.getCurrentPosition(function(position) {
        // Set the address position
        if(position.address && position.address.city) {
          $locationInput.val(position.address.city);
        }
      });
    }
  }
 
  // Focuses on the input box
  function focusOnLocationBox() {
    $locationInput[0].focus();
  }
 
  // Modal function
  function showDialog(title, message) {
    $("#errorDialog h2.error-title").html(title);
    $("#errorDialog p.error-message").html(message);
    $.mobile.changePage("#errorDialog");
  }
 
  // Initialize the app
  app.init();
 
});

Wir werden nicht jeden Teil des Javascripts der App durchgehen, aber ein paar Punkte sind es wert, darauf hinzuweisen:

  • Das Skript verwendet Feature-Tests für die beiden localStorage undgeolocation. Die Anwendung versucht nicht, beide Funktionen zu verwenden, wenn sie nicht im Browser vorhanden sind.
  • Die geolocation-API wird verwendet, um den aktuellen Standort des Benutzers zu ermitteln, und die localStorage-API wird verwendet, um den Suchverlauf des Benutzers zu speichern.
  • Das JavaScript ist modular aufgebaut, um das Testen zu erleichtern.
  • Ein Durchzugsereignis wird im Tweetfenster abgehört. Wenn das Fenster durchgestrichen wird, wird der Benutzer zum ersten Fenster zurückgebracht.
  • Twitter ist die Quelle aller Daten. Für diese Anwendung wird keine eigene serverseitige Verarbeitung verwendet.

 

Schlagwörter des Dokuments und Mitwirkende

Mitwirkende an dieser Seite: matschibatschi
Zuletzt aktualisiert von: matschibatschi,