iPhone 5 Defect Could Cause Shipment Delays

Up until now the iPhone 5 rumors have been pleasant and hopeful, but as we draw closer to the rumored Apple announcement in October, various sources are reporting things may not be as cheery as they seem.

iPhone 5

John Paczkowski’s announcement about an Apple media event on October 4th, made fans hopeful that the wait for the iPhone 5 would finally be over. However, 9to5Mac reported earlier the iPhone 5 could suffer shortages in 2011 due to a manufacturing problem. More bad news came from DigiTimes who reinforced 9to5’s claim, adding that a bubble defect in the touch panels could be the cause for the delay.

“A defect has presented in some iPhone 5 touch panels produced by Wintek which may affect the ability to meet initial shipment targets for the launch which is expected in October, according to iPhone supply chain makers. In response, Wintek simply indicated that all of its products are being delivered on schedule.”

Wedge Partners analyst Brain Blair doesn’t think a glitch in the supply chain is a problem at all. He believes Apple only plans to release one iPhone model this year, the iPhone 4S, which is reportedly flying off the production line. Blair told BGR:

“We expect the focus of the new iPhone will be iOS 5, a speedier A5 processor and a higher resolution 8 MP camera with a small possibility of a larger 4 inch screen. We believe the casing will be largely similar to the iPhone with some particular modifications to the antennae [sic]. We don’t expect a second, dramatically different iPhone to accompany this as we don’t think Apple needs to have 3 models in the market to address the high end, mid-tier and low end since the iPhone 4 (with memory lowered to 8 GB) will drop to $99 and effectively attack those markets.”

A Sprint employee also seemed confused stating Sprint was gearing up for an iPhone 4 launch at the end of September, not an iPhone 5 launch, and T-Mobile Chief Marketing Officer Cole Brodman was quoted as saying “we’re not going to get the iPhone 5 this year.” Most assumed he meant T-Mobile was being left out of Apple’s plans, but it could mean the iPhone 5 won’t be ready until 2012. It’s also possible Sprint and T-Mobile will only be offering the iPhone 4 to its customers, while ATT and Verizon will be carrying the iPhone 5 first.

It’s hard to believe Apple would hold a big media event with new CEO Tim Cook to only announce a revamped iPhone 4. I’m sure the iPhone 5 will make a cameo if there is a conference in October, however, if it will be available in 2011 is still the big question.

Advertisements

Sorry, Nirvana fans: Google smells like Muppet spirit

It’s the 20th anniversary of Nirvana’s landmark Nevermind release, and the world is taking a moment to reflect.

“The album would go on to usher in rock’s great renaissance, cause a seismic shift in popular culture, bring the underground to the mainstream and make unwilling stars out of three rather scruffy guys from the Pacific Northwest,” writes MTV’s James Montgomery in one of many tributes to the iconic album.

But there’s one place where Nevermind isn’t getting its due.

Leading up to the anniversary, Seattle grunge fans were lobbying Google to dedicate one of its trademark Google Doodles to the occasion.

As the “Grunge Scene” Twitter account put it so eloquently this past week… “Hey @google Nirvana’s Nevermind 20th anniversary is Saturday. Don’t screw this up, let’s get the #Nevermind20GoogleDoodle.”

But as it happens, it’s also Jim Henson’s 75th birthday today.

And so this is what we’ve got on the Google home page this weekend.

We don’t know if Google actually considered a Nirvana tribute, but it’s probably safe to say that the idea of working with the late Muppet master’s family is probably a little more appealing than the prospect of dealing with Kurt Cobain’s survivors on such a project.

Name that Tech Tune: Test your music and tech knowledge

Our Name that Tech Tune contest is back this week with a special compound challenge, requiring you to use your knowledge of both the Seattle music scene and the technology community to come up with the answer.

And we’ve got a great prize to give away to the winner — Stumptown Vol. 1, a hardcover compendium of the comic book series illustrated by Matthew Southworth, one of our in-studio guests on this week’s GeekWire radio show and podcast. This copy is signed by both Matt and our other in-studio guest, William Katt, a.k.a. The Greatest American Hero. (Both are appearing at today’s Jet City Comic Show.)

If you’ve never played the contest before, here’s how it works: Listen to the sound below, and if you know it, send your answer to [email protected]. From the pool of correct answers, we’ll pick one person at random to win the book.

Here’s the key for solving this week’s challenge. You’ll first need to figure out the band. The answer we’re looking for is the name of the large technology company that currently employs one of the alumni of this band.

Here’s the MP3 if anyone has issues with the player.

For more context, here’s the full segment, with our commentary and comments from Matt Southworth.

Send your answer to [email protected] if you know the name of the big tech company that employs one of the alumni of that particular band.

We’ll give the answer and announce the winner on our next show. Stay tuned for this week’s show coming up at 7 a.m. today on 97.3 KIRO-FM in Seattle and on GeekWire.com.

By the way, the answer to last week’s Name that Tech Tune challenge was Justice League. Congrats to everyone who got it right and to our randomly chosen winner, Adam Click, who gets a pair of passes to the Jet City Comic Show today.

GeekWire Radio: Facebook, Netflix, comic books and the Greatest American Hero

Matthew Southworth and William Katt (Photo: Erynn Rose)

We’ve got a big menu of topics on this weekend’s GeekWire radio show and podcast, starting with a discussion of the latest changes at Facebook and Netflix, and what they’ll mean for users of those two services.

From there, we debate Microsoft’s decision to make an example of one of its (now former) employees over a series of tweets about an upcoming Windows Phone from Nokia.

Then we bring in two guests for a discussion of the comic book landscape: Matthew Southworth, the Seattle-based artist who illustrates the Stumptown comic books and William Katt, who played the The Greatest American Hero in the popular TV series in the 1980s, in addition to roles in Carrie and many other productions. (Both are appearing at today’s Jet City Comic Show in Seattle.)

In our final segment, we give the answer to last week’s Name that Tech Tune Challenge and serve up a new one, with a chance to win a hardcover copy of Stumptown Vol. 1, signed by both of our guests.

Listen to the full show above or directly via this MP3 file.  You can get every episode of the GeekWire show using this RSS feed, or subscribe in iTunes or Zune.

Headlines and links for more on the stories we discussed …

Netflix to spin off DVD mail business as Qwikster; CEO Reed Hastings admits he messed up

Poll: Do you accept Netflix’s apology?

New from Facebook: Timeline view, lifestyle apps, movies and music with friends

Does Google have monopoly power? Watch Eric Schmidt dance around this one

Windows Phone manager who tweeted inside info about Nokia device is out at Microsoft

Jet City Comic Show, 10 a.m. to 6 p.m. Saturday at Seattle Center

Brendan Wright’s past interview with Matt Southworth about Stumptown

Name that Tech Tune for a chance to win a signed Stumptown book.

Photo Gallery: The battle in Seattle (of the tomato variety)

There may be no better cure for the end of summer than cold beer, live music and hurling tomatoes at your friends.

Tomato Battle at the Pyramid Ale House Saturday was the messiest, happiest, and most entertaining event I have ever witnessed in Seattle. It was also a thrilling place for a photographer.

Telephoto lens and camera protector in tow I braved the flying red juice and managed to capture some shots from the action.

While my camera was unscathed, I wasn’t so lucky. I left the event smelling of my favorite vegetable and thinking how much I love this hilarious city.

Conceived by Startup Weekend’s Clint Nelsen and bankrolled by Seattle angel investor Andy Sack, Tomato Battle is looking to go global with its tomato tossing frivolity.

Look for it in a city near you soon.

tomato-2

tomato-3

tomato-4

tomato-5

tomato-6

tomato-7

tomato-8

tomato-9

tomato-10

tomato-13

tomato-14

tomato-15

tomato-16

tomato-17

tomato-18

tomato-21

tomato-20

tomato-1

Photography by Annie Laurie Malarkey[email protected]

WordPress Essentials: Interacting With The WordPress Database

While you already use many functions in WordPress to communicate with the database, there is an easy and safe way to do this directly, using the $wpdb class. Built on the great ezSQL class by Justin Vincent, $wpdb enables you to address queries to any table in your database, and it also helps you handle the returned data. Because this functionality is built into WordPress, there is no need to open a separate database connection (in which case, you would be duplicating code), and there is no need to perform hacks such as modifying a result set after it has been queried.

$wpdb class
The $wpdb class modularizes and automates a lot of database-related tasks.

In this article, I will show you how to get started with the $wpdb class, how to retrieve data from your WordPress database and how to run more advanced queries that update or delete something in the database. The techniques here will remove some of the constraints that you run into with functions such as get_posts() and wp_list_categories(), allowing you to tailor queries to your particular needs. This method can also make your website more efficient by getting only the data that you need — nothing more, nothing less.
[toggle title=”Getting Started”]

Getting Started

If you know how MySQL or similar languages work, then you will be right at home with this class, and you will need to keep only a small number of function names in mind. The basic usage of this class can be best understood through an example, so let’s query our database for the IDs and titles of the four most recent posts, ordered by comment count (in descending order).

?php
   $posts = $wpdb-get_results("SELECT ID, post_title FROM $wpdb-posts WHERE post_status = 'publish'
   AND post_type='post' ORDER BY comment_count DESC LIMIT 0,4")
?

As you can see, this is a basic SQL query, with some PHP wrapped around it. The $wpdb class contains a method (a method is a special name for functions that are inside classes), named get_results(), which will not only fetch your results but put them in a convenient object. You might have noticed that, instead of using wp_posts for the table’s name, I have used $wpdb-posts, which is a helper to access your core WordPress tables. More on why to use these later.

The $results object now contains your data in the following format:

Array
(
   [0] = stdClass Object
      (
         [ID] = 6
         [post_title] = The Male Angler Fish Gets Completely Screwed
      )

   [1] = stdClass Object
      (
         [ID] = 25
         [post_title] = 10 Truly Amazing Icon Sets From Germany
      )

   [2] = stdClass Object
      (
         [ID] = 37
         [post_title] = Elderberry Is Awesome
      )

   [3] = stdClass Object
      (
         [ID] = 60
         [post_title] = Gathering Resources and Inspiration With Evernote
      )

)

Retrieving Results From The Database

If you want to retrieve some information from the database, you can use one of four helper functions to structure the data.

get_results()

This is the function that we looked at earlier. It is best for when you need two-dimensional data (multiple rows and columns). It converts the data into an array that contains separate objects for each row.

?php
   $posts = $wpdb-get_results("SELECT ID, post_title FROM wp_posts WHERE post_status = 'future'
   AND post_type='post' ORDER BY post_date ASC LIMIT 0,4")

   // Echo the title of the first scheduled post
   echo $posts[0]-post_title;
?

get_row

When you need to find only one particular row in the database (for example, the post with the most comments), you can use get_row(). It pulls the data into a one-dimensional object.

?php
   $posts = $wpdb-get_row("SELECT ID, post_title FROM wp_posts WHERE post_status = 'publish'
   AND post_type='post' ORDER BY comment_count DESC LIMIT 0,1")

   // Echo the title of the most commented post
   echo $posts-post_title;
?

get_col

This method is much the same as get_row(), but instead of grabbing a single row of results, it gets a single column. This is helpful if you would like to retrieve the IDs of only the top 10 most commented posts. Like get_row(), it stores your results in a one-dimensional object.

?php
   $posts = $wpdb-get_col("SELECT ID FROM wp_posts WHERE post_status = 'publish'
   AND post_type='post' ORDER BY comment_count DESC LIMIT 0,10")

   // Echo the ID of the 4th most commented post
   echo $posts[3]-ID;
?

get_var

In many cases, you will need only one value from the database; for example, the email address of one of your users. In this case, you can use get_var to retrieve it as a simple value. The value’s data type will be the same as its type in the database (i.e. integers will be integers, strings will be strings).

?php
   $email = $wpdb-get_var("SELECT user_email FROM wp_users WHERE user_login = 'danielpataki' ")

   // Echo the user's email address
   echo $email;
?

Inserting Into The Database

To perform an insert, we can use the insert method:

$wpdb-insert( $table, $data, $format);

This method takes three arguments. The first specifies the name of the table into which you are inserting the data. The second argument is an array that contains the columns and their respective values, as key-value pairs. The third parameter specifies the data type of your values, in the order you have given them. Here’s an example:

?php
   $wpdb-insert($wpdb-usermeta, array("user_id" = 1, "meta_key" = "awesome_factor", "meta_value" = 10), array("%d", %s", "%d"));

   // Equivalent to:
   // INSERT INTO wp_usermeta (user_id, meta_key, meta_value) VALUES (1, "awesome_factor", 10);
?

If you’re used to writing out your inserts, this may seem unwieldy at first, but it actually gives you a lot of flexibility because it uses arrays as inputs.

Specifying the format is optional; all values are treated as strings by default, but including this in the method is a good practice. The three values you can use are %s for strings, %d for decimal numbers and %f for floats.

Updating Your Data

By now, you won’t be surprised to hear that we also have a helper method to update our data — shockingly, calledupdate(). Its use resembles what we saw above; but to handle the where clause of our update, it needs two extra parameters.

$wpdb-update( $table, $data, $where, $format = null, $where_format = null );

The $table$data and $format parameters should be familiar to you; they are the same as before. Using the $whereparameter, we can specify the conditions of the update. It should be an array in the form of column-value pairs. If you specify multiple parameters, then they will be joined with AND logic. The $where_format is just like $format: it specifies the format of the values in the $where parameter.

$wpdb-update( $wpdb-posts, array("post_title" = "Modified Post Title"), array("ID" = 5), array("%s"), array("%d") );

Other Queries

While the helpers above are great, sometimes performing different or more complex queries than the helpers allow is necessary. If you need to perform an update with a complex where clause containing multiple AND/OR logic, then you won’t be able to use the update() method. If you wanted to do something like delete a row or set a connection character set, then you would need to use the “general” query() method, which let’s you perform any sort of query.

$wpdb-query("DELETE FROM wp_usermeta WHERE meta_key = 'first_login' OR meta_key = 'security_key' ");

Protection And Validation

I hope I don’t have to tell you how important it is to make sure that your data is safe and that your database can’t be tampered with! Data validation is a bit beyond the scope of this article, but do take a look at what the WordPress Codex has to say about “Data Validation” at some point.

In addition to validating, you will need to escape all queries. Even if you are not familiar with SQL injection attacks, still use this method and then read up on it later, because it is critical.

The good news is that if you use any of the helper functions, then you don’t need to do anything: the query is escaped for you. If you use the query() method, however, you will need to escape manually, using the prepare()method.

$sql = $wpdb-prepare( 'query' [, value_parameter, value_parameter ... ] );

To make this a bit more digestible, let’s rewrite this basic format a bit.

$sql = $wpdb-prepare( "INSERT INTO $wpdb-postmeta (post_id, meta_key, meta_value ) VALUES ( %d, %s, %d )", 3342, 'post_views', 2290 )
$wpdb-query($sql);

As you can see, this is not that scary. Instead of adding the actual values where you usually would, you enter the type of data, and then you add the actual data as subsequent parameters.

Class Variables And Other Methods

Apart from these excellent methods, there are quite a few other functions and variables to make your life easier. I’ll show you some of the most common ones, but please do look at the WordPress Codex page linked to above for a full list of everything that $wpdb has to offer.

insert_id()

Whenever you insert something into a table, you will very likely have an auto-incrementing ID in there. To find the value of the most recent insert performed by your script, you can use $wpdb-insert_id.

$sql = $wpdb-prepare( "INSERT INTO $wpdb-postmeta (post_id, meta_key, meta_value ) VALUES ( %d, %s, %d )", 3342, 'post_views', 2290 )
   $wpdb-query($sql);

   $meta_id = $wpdb-insert_id;

num_rows()

If you’ve performed a query in your script, then this variable will return the number of results of your last query. This is great for post counts, comment counts and so on.

Table Names

All the core table names are stored in variables whose names are exactly the same as their core table equivalent. The name of your posts table (probably wp_posts) would be stored in the $posts variable, so you could output it by using $wpdb-posts.

We use this because we are allowed to choose a prefix for our WordPress tables. While most people use the defaultwp, some users want or need a custom one. For the sake of flexibility, this prefix is not hardcoded, so if you are writing a plug-in and use wp_postmeta in a query instead of $wpdb-postmeta, your code will not work on some websites.

If you want to get data from a non-core WordPress table, no special variable will be available for it. In this case, you can just write the table’s name as usual.

Error Handling

By calling the show_errors() or hide_errors() methods, you can turn error-reporting on or off (it’s off by default) to get some more info about what’s going on. Either way, you can also use the print_error() method to print the errors for the latest query.

$wpdb-show_errors();
   $wpdb-query("DELETE FROM wp_posts WHERE post_id = 554 ");

   // When run, because show_errors() is set, the error message will tell you that the "post_id" field is an unknown
   // field in this table (since the correct field is ID)

Building Some Basic Tracking With Our New $wpdb Knowledge

If you’re new to all of this, you probably get what I’m going on about but may be finding it hard to implement. So, let’s take the example of a simple WordPress tracking plug-in that I made for a website.

For simplicity’s sake, I won’t describe every detail of the plug-in. I’ll just show the database’s structure and some queries.

Our Table’s Structure

To keep track of ad clicks and impressions, I created a table; let’s call it “tracking.” This table records user actions in real time. Each impression and click is recorded in its own row in the following structure:

  • ID
    The auto-incremented ID.
  • time
    The date and time that the action occurred.
  • deal_id
    The ID of the deal that is connected to the action (i.e. the ad that was clicked or viewed).
  • action
    The type of action (i.e. click or impression).
  • action_url
    The page on which the action was initiated.
  • user_id
    If the user is logged in, their ID.
  • user_ip
    The IP of the user, used to weed out any naughty business.

This table will get pretty big pretty fast, so it is aggregated into daily statistics and flushed periodically. But let’s just work with this one table for now.

Inserting Data Into Our Tables

When a user clicks an ad, it is detected, and the information that we need is sent to our script in the form of a $_POSTarray, with the following data:

Array
(
   [deal_id] = 643
   [action] = click
   [action_url] = http://thisiswhereitwasclicked.com/about/
   [user_id] = 223
   [user_ip] = 123.234.223.12
)

We can then insert this data into the database using our helper method, like so:

$wpdb-insert('tracking', array("deal_id" = 643, "action" = "click", "action_url" = "http://thisiswhereitwasclicked.com/about/",
"user_id" = 223, "user_ip" = "123.234.223.12"), array("%d", %s", "%s", "%d", "%s"));

At the risk of going on a tangent, I’ll address some questions you might have about this particular example. You may be thinking, what about data validation? The click could have come from a website administrator, or a user could have clicked twice by mistake, or a bunch of other things might have happened.

We decided that because we don’t need real-time stats (daily stats is enough), there is no point to check the data at every insert. Data is aggregated into a new table every day around midnight, a low traffic time. Before aggregating the data, we take care to clean it up, taking out duplicates and so on. The data is, of course, escaped before being inserted into the table, because we are using a helper function; so, we are safe there.

Just deleting in bulk all at once the ones that are made by administrators is easier than checking at every insert. This takes a considerable amount of processing off our server’s shoulders.

Deleting Actions From a Blacklisted IP

If we find that the IP address 168.211.23.43 is being naughty-naughty, we could blacklist it. In this case, when we aggregate the daily data, we would need to delete all of the entries by this IP.

$sql = $wpdb-prepare("DELETE FROM tracking WHERE user_ip = %s ", '168.211.23.43');
   $wpdb-query($sql);

You have probably noticed that I am still escaping the data, even though the IP was received from a secure source. I would suggest escaping your data no matter what. First of all, proper hackers are good at what they do, because they are excellent programmers and can outsmart you in ways that you wouldn’t think of. Also, I personally have done more to hurt my own websites than hackers have, so I do these things as a safety precaution against myself as well.

Updating Totals

We store our ads as custom post types; and to make statistical reporting easier, we store the total amount of clicks that an ad receives separately as well. We could just add up all of the clicks in our tracking database for the given deal as well, so let’s look at that first.

$total = $wpdb-get_var("SELECT COUNT(ID) WHERE deal_id = 125 ");

Because getting a single variable is easier than always burdening ourselves with a more complex query, whenever we aggregate our data, we would store the current total separately. Our ads are stored as posts with a custom post type, so a logical place to store this total is in the postmeta table. Let’s use the total_clicks meta key to store this data.

$wpdb-update( $wpdb-postmeta, array("meta_value" = $total), array("ID" = 125), array("%d"), array("%d") );

   // note that this should be done with update_post_meta(), I just did it the way I did for example's sake

Final Thoughts And Tips

I hope you have gained a better understanding of the WordPress $wpdb class and that you will be able to use it to make your projects better. To wrap up, here are some final tips and tricks for using this class effectively.

  • I urge you to be cautious: with great power comes great responsibility. Make sure to escape your data and to validate it, because improper use of this class is probably a leading cause of hacked websites!
  • Ask only for the data that you need. If you will only be displaying an article’s title, there is no need to retrieve all of the data from each row. In this case, just ask for the title and the ID: SELECT title, ID FROM wp_posts ORDER BY post_date DESC LIMIT 0,5.
  • While you can use the query() method for any query, using the helper methods (insertupdateget_row, etc.) is better if possible. They are more modular and safer, because they escape your data automatically.
  • Take care when deleting records from a WordPress (or any other) database. When WordPress deletes a comment, a bunch of other actions also take place: the comment count in the wp_posts table needs to be reduced by one, all of the data in the comment_meta table needs to be deleted as well, and so on. Make sure to clean up properly after yourself, especially when deleting things.
  • Look at all of the class variables and other bits of information in the official documentation. These will help you use the class to its full potential. I also recommend looking at the ezSQL class for general use in your non-WordPress projects; I use it almost exclusively for everything I do.

Further Reading

[/toggle] 

Create An Animated Bar Graph With HTML, CSS And jQuery

People in boardrooms across the world love a good graph. They go nuts for PowerPoint, bullet points and phrases like “run it up the flagpole,” “blue-sky thinking” and “low-hanging fruit,” and everything is always “moving forward.” Backwards is not an option for people who facilitate paradigm shifts in the zeitgeist. Graphs of financial projections, quarterly sales figures and market saturation are a middle-manager’s dream.

screenshot

How can we as Web designers get in on all of this hot graph action? There are actually quite a few ways to display graphs on the Web. We could simply create an image and nail it to a Web page. But that’s not very accessible or interesting. We could use Flash, which is quite good for displaying graphs — but again, not very accessible. Besides, designers, developers and deities are falling out of love with Flash. Technologies such as HTML5 can do many of the same things without the need for a plug-in. The new HTML5 canvas element could even be adapted to the task. Plenty of charting tools are online that we might use. But what if we wanted something a little more tailored?

There are pros and cons to the wide range of resources available to us, but this tutorial will not explore them all. Instead, we’ll create our graph using a progressively enhanced sprinkling of CSS3 and jQuery. Because we can.
[faq title=”Read More”]

What Are We Making?

We’re making this. And more! Here are some possibilities on how you can extend the techniques explored in this tutorial:

  • A progress bar that indicates how long until the end of all humanity in the event of a zombie plague;
  • A graph indicating the decline in safe outdoor activities during a zombie plague;
  • A frighteningly similar graph indicating the decline in manners during a zombie plague;
  • The increase of people who were unaware of the zombie plague because they were sharing with all of their now-deceased friends on Facebook what they did on FarmVille.

Or you could create a graph or quota bar that simply illustrates something useful and less full of dread and zombies. So, let’s get on with it.

What You’ll Need

  • A text or HTML editor. Take your pick; many are out there.
  • jQuery. Practice safe scripting and get the latest one. Keep the jQuery website open so that you can look up the documentation as you go.
  • Possibly an image editor, such as Paint, to mock up what your graph might look like.
  • A modern and decent Web browser to preview changes.

That should do it. Please note that this tutorial is not designed as an introduction to either HTML, CSS, jQuery or zombies. Some intermediate knowledge of these three technologies and the undead is assumed.

The Mark-Up

You can create the underlying HTML for a graph in a number of ways. In this tutorial, we’ll start with a table, because it will make the most sense visually if JavaScript or CSS is not applied. That’s a big checkmark in the column for accessibility.

Quick! You’ve just been given some alarming figures. The population of tanned zombies is projected to spiral out of control in the next few years. The carbon tigers and blue monkeys are under immediate threat. Then the tanned zombies will probably come for us. But you’re just a designer. What could you possibly do to help?

I know! You could make a Web page that illustrates our imminent demise with nice, calming, smoothly animated graphics!

To begin, let’s put this data into a table, with columns for each year, and rows for the different species.

!doctype html
html lang="en"
   head
      meta charset="utf-8"
      meta name="viewport" content="width=1024"
      titleExample 01: No CSS/title
   /head

   body
      div id="wrapper"
         div
            h3Population of endangered species from 2012 to 2016/h3
            table id="data-table" border="1" cellpadding="10" cellspacing="0"
            summary="The effects of the zombie outbreak on the populations
            of endangered species from 2012 to 2016"
               captionPopulation in thousands/caption
               thead
                  tr
                     tdnbsp;/td
                     th scope="col"2012/th
                     th scope="col"2013/th
                     th scope="col"2014/th
                     th scope="col"2015/th
                     th scope="col"2016/th
                  /tr
               /thead
               tbody
                  tr
                     th scope="row"Carbon Tiger/th
                     td4080/td
                     td6080/td
                     td6240/td
                     td3520/td
                     td2240/td
                  /tr
                  tr
                     th scope="row"Blue Monkey/th
                     td5680/td
                     td6880/td
                     td6240/td
                     td5120/td
                     td2640/td
                  /tr
                  tr
                     th scope="row"Tanned Zombie/th
                     td1040/td
                     td1760/td
                     td2880/td
                     td4720/td
                     td7520/td
                  /tr
               /tbody
            /table
         /div
      /div
   /body
/html

View the example below to see how it looks naked, with no CSS or JavaScript applied. The accessibility of this table will enable people using screen readers to understand the data and the underlying message, which is “Run for your life! The zombies are coming!”

screenshot

The easy part is now out of the way. Now, let’s tap into the power of CSS and JavasScript (via jQuery) to really illustrate what the numbers are telling us. Technically, our aim is to create a graph that works in all modern browsers, from IE 8 on.

Did I say all modern browsers? IE 8 is lucky: it gets to hang out with the cool kids. Browsers that support CSS3 will get a few extra sprinkles.

“By Your Powers Combined…”

If you wish to summon Captain Planet, you may have to look elsewhere. If you want to learn how to combine CSS and jQuery to create a graph that illustrates our impending doom at the hands of a growing army of zombies who prefer bronzer over brains, then read on.

The first thing to do is style our table with some basic CSS. This is a nice safety net for people who haven’t enabled JavaScript in their browser.

screenshot

Getting Started In jQuery

We’ll use jQuery to create our graph on the fly, separate from the original data table. To do this, we need to get the data out of the table and store it in a more usable format. Then, we can add to our document new elements that use this data in order to construct our graph.

Let’s get started by creating our main createGraph() function. I’ve abbreviated some of the inner workings of this function so that you get a clearer picture of the structure. Don’t forget: you can always refer to the source code that comes with this tutorial.

Here’s our basic structure:

// Wait for the DOM to load everything, just to be safe
$(document).ready(function() {

   // Create our graph from the data table and specify a container to put the graph in
   createGraph('#data-table', '.chart');

   // Here be graphs
   function createGraph(data, container) {
      // Declare some common variables and container elements
      …

      // Create the table data object
      var tableData = {
         …
      }

      // Useful variables to access table data
      …

      // Construct the graph
      …

      // Set the individual heights of bars
      function displayGraph(bars) {
         …
      }

      // Reset the graph's settings and prepare for display
      function resetGraph() {
         …
         displayGraph(bars);
      }

      // Helper functions
      …

      // Finally, display the graph via reset function
      resetGraph();
   }
});

We pass two parameters to this function:

  1. The data, in the form of a table element;
  2. container element, where we’d like to place our graph in the document.

Next up, we’ll declare some variables to manage our data and container elements, plus some timer variables for animation. Here’s the code:

// Declare some common variables and container elements
var bars = [];
var figureContainer = $('div id="figure"/div');
var graphContainer = $('div/div');
var barContainer = $('div/div');
var data = $(data);
var container = $(container);
var chartData;
var chartYMax;
var columnGroups;

// Timer variables
var barTimer;
var graphTimer;

Nothing too exciting here, but these will be very useful later.

Getting The Data

Besides simply displaying the data, a good bar chart should have a nice big title, clearly labelled axes and a color-coded legend. We’ll need to strip the data out of the table and format it in a way that is more meaningful in a graph. To do that, we’ll create a JavaScript object that stores our data in handy little functions. Let’s give birth to ourtableData{} object:

// Create table data object
var tableData = {
   // Get numerical data from table cells
   chartData: function() {
      …
   },
   // Get heading data from table caption
   chartHeading: function() {
      …
   },
   // Get legend data from table body
   chartLegend: function() {
      …
   },
   // Get highest value for y-axis scale
   chartYMax: function() {
      …
   },
   // Get y-axis data from table cells
   yLegend: function() {
      …
   },
   // Get x-axis data from table header
   xLegend: function() {
      …
   },
   // Sort data into groups based on number of columns
   columnGroups: function() {
      …
   }
}

We have several functions here, and they are explained in the code’s comments. Most of them are quite similar, so we don’t need to go through each one. Instead, let’s pick apart one of them, columnGroups:

// Sort data into groups based on number of columns
columnGroups: function() {
   var columnGroups = [];
   // Get number of columns from first row of table body
   var columns = data.find('tbody tr:eq(0) td').length;
   for (var i = 0; i  columns; i++) {
      columnGroups[i] = [];
      data.find('tbody tr').each(function() {
         columnGroups[i].push($(this).find('td').eq(i).text());
      });
   }
   return columnGroups;
}

Here’s how it breaks down:

  • Create the columnGroups[] array to store the data;
  • Get the number of columns by counting the table cells (td) in the first row;
  • For each column, find the number of rows in the table body (tbody), and create another array to store the table cell data;
  • Then loop through each row and grab the data from each table cell (via the jQuery text() function), and then add it to the table cell data array.

Once our object is full of juicy data, we can start creating the elements that make up our graph.

Using The Data

Using the jQuery $.each function, we can now loop through our data at any point and create the elements that make up our graph. One of the trickier bits involves inserting the bars that represent each species inside the yearly columns.

Here’s the code:

// Loop through column groups, adding bars as we go
$.each(columnGroups, function(i) {
   // Create bar group container
   var barGroup = $('div/div');
   // Add bars inside each column
   for (var j = 0, k = columnGroups[i].length; j  k; j++) {
      // Create bar object to store properties (label, height, code, etc.) and add it to array
      // Set the height later in displayGraph() to allow for left-to-right sequential display
      var barObj = {};
      barObj.label = this[j];
      barObj.height = Math.floor(barObj.label / chartYMax * 100) + '%';
      barObj.bar = $('divspan' + barObj.label + '/span/div')
         .appendTo(barGroup);
      bars.push(barObj);
   }
   // Add bar groups to graph
   barGroup.appendTo(barContainer);
});

Excluding the headings, our table has five columns with three rows. For our graph, this means that for each column we create, three bars will appear in that column. The following image shows how our graph will be constructed:

screenshot

Breaking it down:

  • For each column, create a container div;
  • Loop inside each column to get the row and cell data;
  • Create a bar object (barObj{}) to store the properties for each bar, such as its label, height and mark-up;
  • Add the mark-up property to the column, applying a CSS class of '.fig' + j to color code each bar in the column, wrapping the label in a span;
  • Add the object to our bars[] array so that we can access the data later;
  • Piece it all together by adding the columns to a container element.

Bonus points if you noticed that we didn’t set the height of the bars. This is so that we have more control later on over how the bars are displayed.

Now that we have our bars, let’s work on labelling our graph. Because the code to display the labels is quite similar, talking you through all of it won’t be necessary. Here’s how we display the y-axis:

// Add y-axis to graph
var yLegend   = tableData.yLegend();
var yAxisList   = $('ul/ul');
$.each(yLegend, function(i) {
   var listItem = $('lispan' + this + '/span/li')
      .appendTo(yAxisList);
});
yAxisList.appendTo(graphContainer);

This breaks down as follows:

  • Get the relevant table data for our labels,
  • Create an unordered list (ul) to contain our list items;
  • Loop through the label data, and create a list item (li) for each label, wrapping each label in a span;
  • Attach the list item to our list;
  • Finally, attach the list to a container element.

By repeating this technique, we can add the legend, x-axis labels and headings for our graph.

Before we can display our graph, we need to make sure that everything we’ve done is added to our container element.

// Add bars to graph
barContainer.appendTo(graphContainer);

// Add graph to graph container
graphContainer.appendTo(figureContainer);

// Add graph container to main container
figureContainer.appendTo(container);

Displaying The Data

All that’s left to do in jQuery is set the height of each bar. This is where our earlier work, storing the height property in a bar object, will come in handy.

We’re going to animate our graph sequentially, one by one, uno por uno.

One possible solution is to use a callback function to animate the next bar when the last animation is complete. However, the graph would take too long to animate. Instead, our graph will use a timer function to display each bar after a certain amount of time, regardless of how long each bar takes to grow. Rad!

Here’s the displayGraph() function:

// Set the individual height of bars
function displayGraph(bars, i) {
   // Changed the way we loop because of issues with $.each not resetting properly
   if (i  bars.length) {
      // Animate the height using the jQuery animate() function
      $(bars[i].bar).animate({
         height: bars[i].height
      }, 800);
      // Wait the specified time, then run the displayGraph() function again for the next bar
      barTimer = setTimeout(function() {
         i++;
         displayGraph(bars, i);
      }, 100);
   }
}

What’s that you say? “Why aren’t you using the $.each function like you have everywhere else?” Good question. First, let’s discuss what the displayGraph() function does, then why it is the way it is.

The displayGraph() function accepts two parameters:

  1. The bars to loop through,
  2. An index (i) from which to start iterating (starting at 0).

Let’s break down the rest:

  • If the value of i is less than the number of bars, then keep going;
  • Get the current bar from the array using the value of i;
  • Animate the height property (calculated as a percentage and stored in bars[i].height);
  • Wait 100 milliseconds;
  • Increment i by 1 and repeat the process for the next bar.

“So, why wouldn’t you just use the $.each function with a delay() before the animation?”

You could, and it would work just fine… the first time. But if you tried to reset the animation via the “Reset graph” button, then the timing events wouldn’t clear properly and the bars would animate out of sequence.

I would like to be proven wrong, and if there is a better way to do this, feel free to sound off in the comments section.

Moving on, here’s resetGraph():

// Reset graph settings and prepare for display
function resetGraph() {
   // Stop all animations and set the bar's height to 0
   $.each(bars, function(i) {
      $(bars[i].bar).stop().css('height', 0);
   });

   // Clear timers
   clearTimeout(barTimer);
   clearTimeout(graphTimer);

   // Restart timer
   graphTimer = setTimeout(function() {
      displayGraph(bars, 0);
   }, 200);
}

Let’s break resetGraph() down:

  • Stop all animations, and set the height of each bar back to 0;
  • Clear out the timers so that there are no stray animations;
  • Wait 200 milliseconds;
  • Call displayGraph() to animate the first bar (at index 0).

Finally, call resetGraph() at the bottom of createGraph(), and watch the magic happen as we bask in the glory of our hard work.

Not so fast, sunshine! Before we go any further, we need to put some clothes on.

The CSS

The first thing we need to do is hide the original data table. We could do this in a number of ways, but because our CSS will load well before the JavaScript, let’s do this in the easiest way possible:

#data-table {
   display: none;
}

Done. Let’s create a nice container area to put our graph in. Because a few unordered lists are being used to make our graph, we’ll also reset the styles for those. Giving the #figure and .graph elements a position: relative is important because it will anchor the place elements exactly where we want in those containers.

/* Containers */

#wrapper {
   height: 420px;
   left: 50%;
   margin: -210px 0 0 -270px;
   position: absolute;
   top: 50%;
   width: 540px;
}

#figure {
   height: 380px;
   position: relative;
}

#figure ul {
   list-style: none;
   margin: 0;
   padding: 0;
}

.graph {
   height: 283px;
   position: relative;
}

Now for the legend. We position the legend right down to the bottom of its container (#figure) and line up the items horizontally:

/* Legend */

.legend {
   background:
   border-radius: 4px;
   bottom: 0;
   position: absolute;
   text-align: left;
   width: 100%;
}

.legend li {
   display: block;
   float: left;
   height: 20px;
   margin: 0;
   padding: 10px 30px;
   width: 120px;
}

.legend span.icon {
   background-position: 50% 0;
   border-radius: 2px;
   display: block;
   float: left;
   height: 16px;
   margin: 2px 10px 0 0;
   width: 16px;
}

The x-axis is very similar to the legend. We line up the elements horizontally and anchor them to the bottom of its container (.graph):

/* x-axis */

.x-axis {
   bottom: 0;
   color:
   position: absolute;
   text-align: center;
   width: 100%;
}

.x-axis li {
   float: left;
   margin: 0 15px;
   padding: 5px 0;
   width: 76px;
}

The y-axis is a little more involved and requires a couple of tricks. We give it a position: absolute to break it out of the normal flow of content, but anchored to its container. We stretch out each li to the full width of the graph and add a border across the top. This will give us some nice horizontal lines in the background.

Using the power of negative margins, we can offset the numerical labels inside the span so that they shift up and to the left. Lovely!

/* y-axis */

.y-axis {
   color:
   position: absolute;
   text-align: right;
   width: 100%;
}

.y-axis li {
   border-top: 1px solid
   display: block;
   height: 62px;
   width: 100%;
}

.y-axis li span {
   display: block;
   margin: -10px 0 0 -60px;
   padding: 0 10px;
   width: 40px;
}

Now for the meat in our endangered species sandwich: the bars themselves. Let’s start with the container element for the bars and the columns:

/* Graph bars */

.bars {
   height: 253px;
   position: absolute;
   width: 100%;
   z-index: 10;
}

.bar-group {
   float: left;
   height: 100%;
   margin: 0 15px;
   position: relative;
   width: 76px;
}

Nothing too complicated here. We’re simply setting some dimensions for the container, and setting a z-index to make sure it appears in front of the y-axis markings.

Now for each individual .bar:

.bar {
   border-radius: 3px 3px 0 0;
   bottom: 0;
   cursor: pointer;
   height: 0;
   position: absolute;
   text-align: center;
   width: 24px;
}

.bar.fig0 {
   left: 0;
}

.bar.fig1 {
   left: 26px;
}

.bar.fig2 {
   left: 52px;
}

The main styles to note here are:

  • position: absolute and bottom: 0, which means that the bars will be attached to the bottom of our graph and grow up;
  • the bar for each species (.fig0.fig1 and .fig2), which will be positioned within .bar-group.

Now, why don’t we minimize the number of sharp edges on any given page by using the border-radius property to round the edges of the top-left and top-right corners of each bar? OK, so border-radius isn’t really necessary, but it adds a nice touch for browsers that support it. Thankfully, the latest versions of the most popular browsers do support it.

Because we’ve placed the values from each table cell in each bar, we can add a neat little pop-up that appears when you hover over a bar:

.bar span {
   #fefefe url(../images/info-bg.gif) 0 100% repeat-x;
   border-radius: 3px;
   left: -8px;
   display: none;
   margin: 0;
   position: relative;
   text-shadow: rgba(255, 255, 255, 0.8) 0 1px 0;
   width: 40px;
   z-index: 20;

   -webkit-box-shadow: rgba(0, 0, 0, 0.6) 0 1px 4px;
   box-shadow: rgba(0, 0, 0, 0.6) 0 1px 4px;
}

.bar:hover span {
   display: block;
   margin-top: -25px;
}

First, the pop-up is hidden from view via display: none. Then, when a .bar element is hovered over, we’ve setdisplay: block to bring it into view, and set a negative margin-top to make it appear above each bar.

The text-shadowrgba and box-shadow properties are currently supported by most modern browsers as is. Of these modern browsers, only Safari requires a vendor prefix (-webkit-) to make box-shadow work. Note that these properties are simply enhancements to our graph and aren’t required to understand it. Our baseline of Internet Explorer 8 simply ignores them.

Our final step in bringing everything together is to color code each bar:

.fig0 {
   background: #747474 url(../images/bar-01-bg.gif) 0 0 repeat-y;
}

.fig1 {
   background: #65c2e8 url(../images/bar-02-bg.gif) 0 0 repeat-y;
}

.fig2 {
   background: #eea151 url(../images/bar-03-bg.gif) 0 0 repeat-y;
}

In this example, I’ve simply added a background-color and a background-image that tiles vertically. This will update the styles for the bars and the little icons that represent them in the legend. Nice.

And, believe it or not, that is it!

The Finished Product

screenshot

That about wraps it up. I hope we’ve done enough to alert the public to the dangers of zombie over-population. More than that, however, I hope you’ve gained something useful from this tutorial and that you’ll continue to push the boundaries of what can be done in the browser — especially with proper Web standards and without the use of third-party plug-ins. If you’ve got ideas on how to extend or improve anything you’ve seen here, don’t hesitate to leave a comment below, or find me on Twitter @derek_mack.

Bonus: Unleashing The Power Of CSS3

This bonus is not as detailed as our main example. It serves mainly as a showcase of some features being considered in the CSS3 specification.

Because support for CSS3 properties is currently limited, so is their use. Although some of the features mentioned here are making their way into other Web browsers, Webkit-based ones such as Apple Safari and Google Chrome are leading the way.

We can actually create our graph using no images at all, and even animate the bars using CSS instead of jQuery.

Let’s start by removing the background images from our bars, replacing them with the -webkit-gradient property:

.fig0 {
   background: -webkit-gradient(linear, left top, right top, color-stop(0.0, #747474), color-stop(0.49, #676767), color-stop(0.5, #505050), color-stop(1.0, #414141));
}

.fig1 {
   background: -webkit-gradient(linear, left top, right top, color-stop(0.0, #65c2e8), color-stop(0.49, #55b3e1), color-stop(0.5, #3ba6dc), color-stop(1.0, #2794d4));
}

.fig2 {
   background: -webkit-gradient(linear, left top, right top, color-stop(0.0, #eea151), color-stop(0.49, #ea8f44), color-stop(0.5, #e67e28), color-stop(1.0, #e06818));
}

We can do the same with our little number pop-ups:

.bar span {
   background: -webkit-gradient(linear, left top, left bottom, color-stop(0.0, #fff), color-stop(1.0, #e5e5e5));
   …
}

For more information on Webkit gradients, check out the Surfin’ Safari blog.

Continuing with the pop-ups, let’s introduce -webkit-transition. CSS transitions are remarkably easy to use and understand. When the browser detects a change in an element’s property (height, width, color, opacity, etc.), it will transition to the new property.

Again, refer to Surfin’ Safari for more information on -webkit-transition and CSS3 animation.

Here’s an example:

.bar span {
   background: -webkit-gradient(linear, left top, left bottom, color-stop(0.0, #fff), color-stop(1.0, #e5e5e5));
   display: block;
   opacity: 0;

   -webkit-transition: all 0.2s ease-out;
}

.bar:hover span {
   opacity: 1;
}

When you hover over the bar, the margin and opacity of the pop-up will change. This triggers a transition event according to the properties we have set. Very cool.

Thanks to -webkit-transition, we can simplify our JavaScript functions a bit:

// Set individual height of bars
function displayGraph(bars, i) {
   // Changed the way we loop because of issues with $.each not resetting properly
   if (i  bars.length) {
      // Add transition properties and set height via CSS
      $(bars[i].bar).css({'height': bars[i].height, '-webkit-transition': 'all 0.8s ease-out'});
      // Wait the specified time, then run the displayGraph() function again for the next bar
      barTimer = setTimeout(function() {
         i++;
         displayGraph(bars, i);
      }, 100);
   }
}
// Reset graph settings and prepare for display
function resetGraph() {
   // Set bar height to 0 and clear all transitions
   $.each(bars, function(i) {
      $(bars[i].bar).stop().css({'height': 0, '-webkit-transition': 'none'});
   });

   // Clear timers
   clearTimeout(barTimer);
   clearTimeout(graphTimer);

   // Restart timer
   graphTimer = setTimeout(function() {
      displayGraph(bars, 0);
   }, 200);
}

Here are the main things we’ve changed:

  • Set the height of the bars via the jQuery css() function, and allowed CSS transitions to take care of the animation;
  • When resetting the graph, turned transitions off so that the height of the bars is instantly set to 0.

Check out the example if you have the latest version of Safari or Chrome installed.

Ultra-Mega Webkit Bonus: Now In 3-D!

For a sneak peek of what the future holds, check out a little experiment that I put together, with a 3-D effect and CSS transforms. Again, it requires the latest versions of Safari or Chrome:

As in our previous Webkit example, there are no images, and all animation is handled via CSSKiss my face!

I can’t tell you what to do with all this information. But I do caution you about the potential misuse of your new powers. In the words of our friend Captain Planet, “The power is yours!”

Use it wisely.

[/faq]