Archive for the ‘Programming’ Category

How to Create a PHP/MySQL Powered Forum from Scratch

March 18th, 2010

In this tutorial, we’re going to build a PHP/MySQL powered forum from scratch. This tutorial is perfect for getting used to basic PHP and database usage. Let’s dive right in!

Step 1: Creating Database Tables

It’s always a good idea to start with creating a good data model when building an application. Let’s describe our application in one sentence: We are going to make a forum which has users who create topics in various categories. Other users can post replies. As you can see, I highlighted a couple of nouns which represent our table names.

Users

  • Categories
  • Topics
  • Posts

These three objects are related to each other, so we’ll process that in our table design. Take a look at the scheme below.

Looks pretty neat, huh? Every square is a database table. All the columns are listed in it and the lines between them represent the relationships. I’ll explain them further, so it’s okay if it doesn’t make a lot of sense to you right now.

I’ll discuss each table by explaining the SQL, which I created using the scheme above. For your own scripts you can create a similar scheme and SQL too. Some editors like MySQL Workbench (the one I used) can generate .sql files too, but I would recommend learning SQL because it’s more fun to do it yourself. A SQL introduction can be found at W3Schools.

Users Table
CREATE TABLE users (
user_id 	INT(8) NOT NULL AUTO_INCREMENT,
user_name	VARCHAR(30) NOT NULL,
user_pass  	VARCHAR(255) NOT NULL,
user_email	VARCHAR(255) NOT NULL,
user_date	DATETIME NOT NULL,
user_level	INT(8) NOT NULL,
UNIQUE INDEX user_name_unique (user_name),
PRIMARY KEY (user_id)
) TYPE=INNODB;
	

The CREATE TABLE statement is used to indicate we want to create a new table, of course. The statement is followed by the name of the table and all the columns are listed between the brackets. The names of all the fields are self-explanatory, so we’ll only discuss the data types below.

user_id

“A primary key is used to uniquely identify each row in a table.”

The type of this field is INT, which means this field holds an integer. The field cannot be empty (NOT NULL) and increments which each record inserted. At the bottom of the table you can see the user_id field is declared as a primary key. A primary key is used to uniquely identify each row in a table. No two distinct rows in a table can have the same value (or combination of values) in all columns. That might be a bit unclear, so here’s a little example.

There is a user called John Doe. If another users registers with the same name, there’s a problem, because: which user is which? You can’t tell and the database can’t tell either. By using a primary key this problem is solved, because both topics are unique.

All the other tables have got primary keys too and they work the same way.

user_name

This is a text field, called a VARCHAR field in MySQL. The number between brackets is the maximum length. A user can choose a username up to 30 characters long. This field cannot be NULL. At the bottom of the table you can see this field is declared UNIQUE, which means the same username cannot be registered twice. The UNIQUE INDEX part tells the database we want to add a unique key. Then we define the name of the unique key, user_name_unique in this case. Between brackets is the field the unique key applies to, which is user_name.

user_pass

This field is equal to the user_name field, except the maximum length. Since the user password, no matter what length, is hashed with sha1(), the password will always be 40 characters long.

user_email

This field is equal to the user_pass field.

user_date

This is a field in which we’ll store the date the user registered. It’s type is DATETIME and the field cannot be NULL.

user_level

This field contains the level of the user, for example: ‘0′ for a regular user and ‘1′ for an admin. More about this later.

Categories Table

CREATE TABLE categories (
cat_id 		 	INT(8) NOT NULL AUTO_INCREMENT,
cat_name	 	VARCHAR(255) NOT NULL,
cat_description 	VARCHAR(255) NOT NULL,
UNIQUE INDEX cat_name_unique (cat_name),
PRIMARY KEY (cat_id)
) TYPE=INNODB;

These data types basically work the same way as the ones in the users table. This table also has a primary key and the name of the category must be an unique one.

Topics Table

CREATE TABLE topics (
topic_id		INT(8) NOT NULL AUTO_INCREMENT,
topic_subject  		VARCHAR(255) NOT NULL,
topic_date		DATETIME NOT NULL,
topic_cat		INT(8) NOT NULL,
topic_by		INT(8) NOT NULL,
PRIMARY KEY (topic_id)
) TYPE=INNODB;

This table is almost the same as the other tables, except for the topic_by field. That field refers to the user who created the topic. The topic_cat refers to the category the topic belongs to. We cannot force these relationships by just declaring the field. We have to let the database know this field must contain an existing user_id from the users table, or a valid cat_id from the categories table. We’ll add some relationships after I’ve discussed the posts table.

Posts Table

CREATE TABLE posts (
post_id 		INT(8) NOT NULL AUTO_INCREMENT,
post_content		TEXT NOT NULL,
post_date 		DATETIME NOT NULL,
post_topic		INT(8) NOT NULL,
post_by		INT(8) NOT NULL,
PRIMARY KEY (post_id)
) TYPE=INNODB;

This is the same as the rest of the tables; there’s also a field which refers to a user_id here: the post_by field. The post_topic field refers to the topic the post belongs to.

“A foreign key is a referential constraint between two tables. The foreign key identifies a column or a set of columns in one (referencing) table that refers to a column or set of columns in another (referenced) table.”

Now that we’ve executed these queries, we have a pretty decent data model, but the relations are still missing. Let’s start with the definition of a relationship. We’re going to use something called a foreign key. A foreign key is a referential constraint between two tables. The foreign key identifies a column or a set of columns in one (referencing) table that refers to a column or set of columns in another (referenced) table. Some conditions:

  • The column in the referencing table the foreign key refers to must be a primary key
  • The values that are referred to must exist in the referenced table

By adding foreign keys the information is linked together which is very important for database normalization. Now you know what a foreign key is and why we’re using them. It’s time to add them to the tables we’ve already made by using the ALTER statement, which can be used to change an already existing table.

We’ll link the topics to the categories first:

ALTER TABLE topics ADD FOREIGN KEY(topic_cat) REFERENCES categories(cat_id) ON DELETE CASCADE ON UPDATE CASCADE;

The last part of the query already says what happens. When a category gets deleted from the database, all the topics will be deleted too. If the cat_id of a category changes, every topic will be updated too. That’s what the ON UPDATE CASCADE part is for. Of course, you can reverse this to protect your data, so that you can’t delete a category as long as it still has topics linked to it. If you would want to do that, you could replace the ‘ON DELETE CASCADE’ part with ‘ON DELETE RESTRICT’. There is also SET NULL and NO ACTION, which speak for themselves.

Every topic is linked to a category now. Let’s link the topics to the user who creates one.

ALTER TABLE topics ADD FOREIGN KEY(topic_by) REFERENCES users(user_id) ON DELETE RESTRICT ON UPDATE CASCADE;

This foreign key is the same as the previous one, but there is one difference: the user can’t be deleted as long as there are still topics with the user id of the user. We don’t use CASCADE here because there might be valuable information in our topics. We wouldn’t want that information to get deleted if someone decides to delete their account. To still give users the opportunity to delete their account, you could build some feature that anonymizes all their topics and then delete their account. Unfortunately, that is beyond the scope of this tutorial.


Link the posts to the topics:

ALTER TABLE posts ADD FOREIGN KEY(post_topic) REFERENCES topics(topic_id) ON DELETE CASCADE ON UPDATE CASCADE;

And finally, link each post to the user who made it:

ALTER TABLE posts ADD FOREIGN KEY(post_by) REFERENCES users(user_id) ON DELETE RESTRICT ON UPDATE CASCADE;

That’s the database part! It was quite a lot of work, but the result, a great data model, is definitely worth it.


Step 2: Introduction to the Header/Footer System

Each page of our forum needs a few basic things, like a doctype and some markup. That’s why we’ll include a header.php file at the top of each page, and a footer.php at the bottom. The header.php contains a doctype, a link to the stylesheet and some important information about the forum, such as the title tag and metatags.

header.php
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="nl" lang="nl">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<meta name="description" content="A short description." />
	<meta name="keywords" content="put, keywords, here" />
	<title>PHP-MySQL forum</title>
	<link rel="stylesheet" href="style.css" type="text/css">
</head>
<body>
<h1>My forum</h1>
	<div id="wrapper">
	<div id="menu">
		<a class="item" href="/forum/index.php">Home</a> -
		<a class="item" href="/forum/create_topic.php">Create a topic</a> -
		<a class="item" href="/forum/create_cat.php">Create a category</a>

		<div id="userbar">
		<div id="userbar">Hello Example. Not you? Log out.</div>
	</div>
		<div id="content">
	

The wrapper div will be used to make it easier to style the entire page. The menu div obviously contains a menu with links to pages we still have to create, but it helps to see where we’re going a little bit. The userbar div is going to be used for a small top bar which contains some information like the username and a link to the logout page. The content page holds the actual content of the page, obviously.

The attentive reader might have already noticed we’re missing some things. There is no </body> or </html> tag. They’re in the footer.php page, as you can see below.

</div><!-- content -->
</div><!-- wrapper -->
<div id="footer">Created for Nettuts+</div>
</body>
</html>
	

When we include a header and a footer on each page the rest of the page get embedded between the header and the footer. This method has got some advantages. First and foremost, everything will be styled correctly. A short example:

<?php
$error = false;
if($error = false)
{
 	//the beautifully styled content, everything looks good
 	echo '<div id="content">some text</div>';
}
else
{
 	//bad looking, unstyled error :-(
}
?>
	

As you can see, a page without errors will result in a nice page with the content. But if there’s an error, everything looks really ugly; so that’s why it’s better to make sure not only real content is styled correctly, but also the errors we might get.

Another advantage is the possibility of making quick changes. You can see for yourself by editing the text in footer.php when you’ve finished this tutorial; you’ll notice that the footer changes on every page immediately. Finally, we add a stylesheet which provides us with some basic markup – nothing too fancy.

body {
	background-color: #4E4E4E;
	text-align: center;			/* make sure IE centers the page too */
}

#wrapper {
	width: 900px;
	margin: 0 auto; 			/* center the page */
}

#content {
	background-color: #fff;
	border: 1px solid #000;
	float: left;
	font-family: Arial;
	padding: 20px 30px;
	text-align: left;
	width: 100%;				/* fill up the entire div */
}

#menu {
	float: left;
	border: 1px solid #000;
	border-bottom: none;		/* avoid a double border */
	clear: both;				/* clear:both makes sure the content div doesn't float next to this one but stays under it */
	width:100%;
	height:20px;
	padding: 0 30px;
	background-color: #FFF;
	text-align: left;
	font-size: 85%;
}

#menu a:hover {
	background-color: #009FC1;
}

#userbar {
	background-color: #fff;
	float: right;
	width: 250px;
}

#footer {
	clear: both;
}

/* begin table styles */
table {
	border-collapse: collapse;
	width: 100%;
}

table a {
	color: #000;
}

table a:hover {
	color:#373737;
	text-decoration: none;
}

th {
	background-color: #B40E1F;
	color: #F0F0F0;
}

td {
	padding: 5px;
}

/* Begin font styles */
h1, #footer {
	font-family: Arial;
	color: #F1F3F1;
}

h3 {margin: 0; padding: 0;}

/* Menu styles */
.item {
	background-color: #00728B;
	border: 1px solid #032472;
	color: #FFF;
	font-family: Arial;
	padding: 3px;
	text-decoration: none;
}

.leftpart {
	width: 70%;
}

.rightpart {
	width: 30%;
}

.small {
	font-size: 75%;
	color: #373737;
}
#footer {
	font-size: 65%;
	padding: 3px 0 0 0;
}

.topic-post {
	height: 100px;
	overflow: auto;
}

.post-content {
	padding: 30px;
}

textarea {
	width: 500px;
	height: 200px;
}
	

Step 3: Getting Ready for Action

Before we can read anything from our database, we need a connection. That’s what connect.php is for. We’ll include it in every file we are going to create.

<?php
//connect.php
$server	= 'localhost';
$username	= 'usernamehere';
$password	= 'passwordhere';
$database	= 'databasenamehere';

if(!mysql_connect($server, $username,  $password))
{
 	exit('Error: could not establish database connection');
}
if(!mysql_select_db($database)
{
 	exit('Error: could not select the database');
}
?>
	

Simply replace the default values of the variables at the top of the page with your own date, save the file and you’re good to go!


Step 4: Displaying the Forum Overview

Since we’re just started with some basic techniques, we’re going to make a simplified version of the forum overview for now.

<?php
//create_cat.php
include 'connect.php';
include 'header.php';

echo '<tr>';
	echo '<td class="leftpart">';
		echo '<h3><a href="category.php?id=">Category name</a></h3> Category description goes here';
	echo '</td>';
	echo '<td class="rightpart">';
			echo '<a href="topic.php?id=">Topic subject</a> at 10-10';
	echo '</td>';
echo '</tr>';
include 'footer.php';
?>
	

There you have it: a nice and clean overview. We’ll be updating this page throughout the tutorial so that it becomes more like the end result, step by step!


Step 5: Signing up a User

Let’s start by making a simple HTML form so that a new user can register.

A PHP page is needed to process the form. We’re going to use a $_SERVER variable. The $_SERVER variable is an array with values that are automatically set with each request. One of the values of the $_SERVER array is ‘REQUEST_METHOD’. When a page is requested with GET, this variable will hold the value ‘GET’. When a page is requested via POST, it will hold the value ‘POST’. We can use this value to check if a form has been posted. See the signup.php page below.

<?php
//signup.php
include 'connect.php';
include 'header.php';

echo '<h3>Sign up</h3>';

if($_SERVER['REQUEST_METHOD'] != 'POST')
{
    /*the form hasn't been posted yet, display it
	  note that the action="" will cause the form to post to the same page it is on */
    echo '<form method="post" action="">
 	 	Username: <input type="text" name="user_name" />
 		Password: <input type="password" name="user_pass">
		Password again: <input type="password" name="user_pass_check">
		E-mail: <input type="email" name="user_email">
 		<input type="submit" value="Add category" />
 	 </form>';
}
else
{
    /* so, the form has been posted, we'll process the data in three steps:
		1.	Check the data
		2.	Let the user refill the wrong fields (if necessary)
		3.	Save the data
	*/
	$errors = array(); /* declare the array for later use */

	if(isset($_POST['user_name']))
	{
		//the user name exists
		if(!ctype_alnum($_POST['user_name']))
		{
			$errors[] = 'The username can only contain letters and digits.';
		}
		if(strlen($_POST['user_name']) > 30)
		{
			$errors[] = 'The username cannot be longer than 30 characters.';
		}
	}
	else
	{
		$errors[] = 'The username field must not be empty.';
	}

	if(isset($_POST['user_pass']))
	{
		if($_POST['user_pass'] != $_POST['user_pass_check'])
		{
			$errors[] = 'The two passwords did not match.';
		}
	}
	else
	{
		$errors[] = 'The password field cannot be empty.';
	}

	if(!empty($errors)) /*check for an empty array, if there are errors, they're in this array (note the ! operator)*/
	{
		echo 'Uh-oh.. a couple of fields are not filled in correctly..';
		echo '<ul>';
		foreach($errors as $key => $value) /* walk through the array so all the errors get displayed */
		{
			echo '<li>' . $value . '</li>'; /* this generates a nice error list */
		}
		echo '</ul>';
	}
	else
	{
		//the form has been posted without, so save it
		//notice the use of mysql_real_escape_string, keep everything safe!
		//also notice the sha1 function which hashes the password
		$sql = "INSERT INTO
					users(user_name, user_pass, user_email ,user_date, user_level)
				VALUES('" . mysql_real_escape_string($_POST['user_name']) . "',
					   '" . sha1($_POST['user_pass']) . "',
					   '" . mysql_real_escape_string($_POST['user_email']) . "',
						NOW(),
						0)";

		$result = mysql_query($sql);
		if(!$result)
		{
			//something went wrong, display the error
			echo 'Something went wrong while registering. Please try again later.';
			//echo mysql_error(); //debugging purposes, uncomment when needed
		}
		else
		{
			echo 'Successfully registered. You can now <a href="signin.php">sign in</a> and start posting! :-) ';
		}
	}
}

include 'footer.php';
?>
	

A lot of explanation is in the comments I made in the file, so be sure to check them out. The processing of the data takes place in three parts:

  • Validating the data
  • If the data is not valid, show the form again
  • If the data is valid, save the record in the database

The PHP part is quite self-explanatory. The SQL-query however probably needs a little more explanation.

INSERT INTO
       users(user_name, user_pass, user_email ,user_date, user_level)
VALUES('" . mysql_real_escape_string($_POST['user_name']) . "',
       '" . sha1($_POST['user_pass']) . "',
       '" . mysql_real_escape_string($_POST['user_email']) . "',
       NOW(),
       0);
	

On line 1 we have the INSERT INTO statement which speaks for itself. The table name is specified on the second line. The words between the brackets represent the columns in which we want to insert the data. The VALUES statement tells the database we’re done declaring column names and it’s time to specify the values. There is something new here: mysql_real_escape_string. The function escapes special characters in an unescaped string , so that it is safe to place it in a query. This function MUST always be used, with very few exceptions. There are too many scripts that don’t use it and can be hacked real easy. Don’t take the risk, use mysql_real_escape_string().

“Never insert a plain password as-is. You MUST always encrypt it.”

Also, you can see that the function sha1() is used to encrypt the user’s password. This is also a very important thing to remember. Never insert a plain password as-is. You MUST always encrypt it. Imagine a hacker who somehow manages to get access to your database. If he sees all the plain-text passwords he could log into any (admin) account he wants. If the password columns contain sha1 strings he has to crack them first which is almost impossible.

Note: it’s also possible to use md5(), I always use sha1() because benchmarks have proved it’s a tiny bit faster, not much though. You can replace sha1 with md5 if you like.

If the signup process was successful, you should see something like this:

Try refreshing your phpMyAdmin screen, a new record should be visible in the users table.


Step 6: Adding Authentication and User Levels

An important aspect of a forum is the difference between regular users and admins/moderators. Since this is a small forum and adding features like adding new moderators and stuff would take way too much time, we’ll focus on the login process and create some admin features like creating new categories and closing a thread.

Now that you’ve completed the previous step, we’re going to make your freshly created account an admin account. In phpMyAdmin, click on the users table, and then ‘Browse’. Your account will probably pop up right away. Click the edit icon and change the value of the user_level field from 0 to 1. That’s it for now. You won’t notice any difference in our application immediately, but when we’ve added the admin features a normal account and your account will have different capabilities.

The sign-in process works the following way:

  • A visitor enters user data and submits the form
  • If the username and password are correct, we can start a session
  • If the username and password are incorrect, we show the form again with a message

The signin.php file is below. Don’t think I’m not explaining what I’m doing, but check out the comments in the file. It’s much easier to understand that way.

<?php
//signin.php
include 'connect.php';
include 'header.php';

echo '<h3>Sign in</h3>';

//first, check if the user is already signed in. If that is the case, there is no need to display this page
if(isset($_SESSION['signed_in']) && $_SESSION['signed_in'] == true)
{
	echo 'You are already signed in, you can <a href="signout.php">sign out</a> if you want.';
}
else
{
	if($_SERVER['REQUEST_METHOD'] != 'POST')
	{
		/*the form hasn't been posted yet, display it
		  note that the action="" will cause the form to post to the same page it is on */
		echo '<form method="post" action="">
			Username: <input type="text" name="user_name" />
			Password: <input type="password" name="user_pass">
			<input type="submit" value="Sign in" />
		 </form>';
	}
	else
	{
		/* so, the form has been posted, we'll process the data in three steps:
			1.	Check the data
			2.	Let the user refill the wrong fields (if necessary)
			3.	Varify if the data is correct and return the correct response
		*/
		$errors = array(); /* declare the array for later use */

		if(!isset($_POST['user_name']))
		{
			$errors[] = 'The username field must not be empty.';
		}

		if(!isset($_POST['user_pass']))
		{
			$errors[] = 'The password field must not be empty.';
		}

		if(!empty($errors)) /*check for an empty array, if there are errors, they're in this array (note the ! operator)*/
		{
			echo 'Uh-oh.. a couple of fields are not filled in correctly..';
			echo '<ul>';
			foreach($errors as $key => $value) /* walk through the array so all the errors get displayed */
			{
				echo '<li>' . $value . '</li>'; /* this generates a nice error list */
			}
			echo '</ul>';
		}
		else
		{
			//the form has been posted without errors, so save it
			//notice the use of mysql_real_escape_string, keep everything safe!
			//also notice the sha1 function which hashes the password
			$sql = "SELECT
						user_id,
						user_name,
						user_level
					FROM
						users
					WHERE
						user_name = '" . mysql_real_escape_string($_POST['user_name']) . "'
					AND
						user_pass = '" . sha1($_POST['user_pass']) . "'";

			$result = mysql_query($sql);
			if(!$result)
			{
				//something went wrong, display the error
				echo 'Something went wrong while signing in. Please try again later.';
				//echo mysql_error(); //debugging purposes, uncomment when needed
			}
			else
			{
				//the query was successfully executed, there are 2 possibilities
				//1. the query returned data, the user can be signed in
				//2. the query returned an empty result set, the credentials were wrong
				if(mysql_num_rows($result) == 0)
				{
					echo 'You have supplied a wrong user/password combination. Please try again.';
				}
				else
				{
					//set the $_SESSION['signed_in'] variable to TRUE
					$_SESSION['signed_in'] = true;

					//we also put the user_id and user_name values in the $_SESSION, so we can use it at various pages
					while($row = mysql_fetch_assoc($result))
					{
						$_SESSION['user_id'] 	= $row['user_id'];
						$_SESSION['user_name'] 	= $row['user_name'];
						$_SESSION['user_level'] = $row['user_level'];
					}

					echo 'Welcome, ' . $_SESSION['user_name'] . '. <a href="index.php">Proceed to the forum overview</a>.';
				}
			}
		}
	}
}

include 'footer.php';
?>
	

This is the query that’s in the signin.php file:

SELECT
	user_id,
	user_name,
	user_level
FROM
	users
WHERE
	user_name = '" . mysql_real_escape_string($_POST['user_name']) . "'
AND
	user_pass = '" . sha1($_POST['user_pass'])
	

It’s obvious we need a check to tell if the supplied credentials belong to an existing user. A lot of scripts retrieve the password from the database and compare it using PHP. If we do this directly via SQL the password will be stored in the database once during registration and never leave it again. This is safer, because all the real action happens in the database layer and not in our application.

If the user is signed in successfully, we’re doing a few things:

<?php
//set the $_SESSION['signed_in'] variable to TRUE
$_SESSION['signed_in'] = true;
//we also put the user_id and user_name values in the $_SESSION, so we can use it at various pages
while($row = mysql_fetch_assoc($result))
{
 	$_SESSION['user_id'] = $row['user_id'];
 	$_SESSION['user_name'] = $row['user_name'];
}
?>
	

First, we set the ’signed_in’ $_SESSION var to true, so we can use it on other pages to make sure the user is signed in. We also put the username and user id in the $_SESSION variable for usage on a different page. Finally, we display a link to the forum overview so the user can get started right away.

Of course signing in requires another function, signing out! The sign-out process is actually a lot easier than the sign-in process. Because all the information about the user is stored in $_SESSION variables, all we have to do is unset them and display a message.

Now that we’ve set the $_SESSION variables, we can determine if someone is signed in. Let’s make a last simple change to header.php:

Replace:

<div id="userbar">Hello Example. Not you? Log out.</div>
	

With:

<?php
<div id="userbar">
 	if($_SESSION['signed_in'])
 	{
 	 	echo 'Hello' . $_SESSION['user_name'] . '. Not you? <a href="signout.php">Sign out</a>';
 	}
 	else
 	{
 		echo '<a href="signin.php">Sign in</a> or <a href="sign up">create an account</a>.';
 	}
</div>
	

If a user is signed in, he will see his or her name displayed on the front page with a link to the signout page. Our authentication is done! By now our forum should look like this:


Step 7: Creating a Category

We want to create categories so let’s start with making a form.

<form method="post" action="">
 	Category name: <input type="text" name="cat_name" />
 	Category description: <textarea name="cat_description" /></textarea>
	<input type="submit" value="Add category" />
 </form>
	

This step looks a lot like Step 4 (Signing up a user’), so I’m not going to do an in-depth explanation here. If you followed all the steps you should be able to understand this somewhat quickly.

<?php
//create_cat.php
include 'connect.php';

if($_SERVER['REQUEST_METHOD'] != 'POST')
{
    //the form hasn't been posted yet, display it
    echo '<form method='post' action=''>
 	 	Category name: <input type='text' name='cat_name' />
 		Category description: <textarea name='cat_description' /></textarea>
 		<input type='submit' value='Add category' />
 	 </form>';
}
else
{
    //the form has been posted, so save it
    $sql = ìINSERT INTO categories(cat_name, cat_description)
 	   VALUES('' . mysql_real_escape_string($_POST['cat_name']) . ì',
 		     '' . mysql_real_escape_string($_POST['cat_description']) . ì')';
    $result = mysql_query($sql);
    if(!$result)
    {
        //something went wrong, display the error
        echo 'Error' . mysql_error();
    }
    else
    {
        echo 'New category successfully added.';
    }
}
?>
	

As you can see, we’ve started the script with the $_SERVER check, after checking if the user has admin rights, which is required for creating a category. The form gets displayed if it hasn’t been submitted already. If it has, the values are saved. Once again, a SQL query is prepared and then executed.


Step 8: Adding Categories to index.php

We’ve created some categories, so now we’re able to display them on the front page. Let’s add the following query to the content area of index.php.

SELECT
 	categories.cat_id,
	categories.cat_name,
 	categories.cat_description,
FROM
 	categories
	

This query selects all categories and their names and descriptions from the categories table. We only need a bit of PHP to display the results. If we add that part just like we did in the previous steps, the code will look like this.

<?php
//create_cat.php
include 'connect.php';
include 'header.php';

$sql = "SELECT
			cat_id,
			cat_name,
			cat_description,
		FROM
			categories";

$result = mysql_query($sql);

if(!$result)
{
	echo 'The categories could not be displayed, please try again later.';
}
else
{
	if(mysql_num_rows($result) == 0)
	{
		echo 'No categories defined yet.';
	}
	else
	{
		//prepare the table
		echo '<table border="1">
			  <tr>
				<th>Category</th>
				<th>Last topic</th>
			  </tr>';	

		while($row = mysql_fetch_assoc($result))
		{
			echo '<tr>';
				echo '<td class="leftpart">';
					echo '<h3><a href="category.php?id">' . $row['cat_name'] . '</a></h3>' . $row['cat_description'];
				echo '</td>';
				echo '<td class="rightpart">';
							echo '<a href="topic.php?id=">Topic subject</a> at 10-10';
				echo '</td>';
			echo '</tr>';
		}
	}
}

include 'footer.php';
?>
	

Notice how we’re using the cat_id to create links to category.php. All the links to this page will look like this: category.php?cat_id=x, where x can be any numeric value. This may be new to you. We can check the url with PHP for $_GET values. For example, we have this link:

category.php?cat_id=23
	

The statement echo $_GET[ëcat_id'];’ will display ‘23′. In the next few steps we’ll use this value to retrieve the topics when viewing a single category, but topics can’t be viewed if we haven’t created them yet. So let’s create some topics!


Step 9: Creating a Topic

In this step, we’re combining the techniques we learned in the previous steps. We’re checking if a user is signed in, we’ll use an input query to create the topic and create some basic HTML forms.

The structure of create_topic.php can hardly be explained in a list or something, so I rewrote it in pseudo-code.

<?php
if(user is signed in)
{
	//the user is not signed in
}
else
{
	//the user is signed in
	if(form has not been posted)
	{
		//show form
	}
	else
	{
		//process form
	}
}
?>
	

Here’s the real code of this part of our forum, check the explanations below the code to see what it’s doing.

<?php
//create_cat.php
include 'connect.php';
include 'header.php';

echo '<h2>Create a topic</h2>';
if($_SESSION['signed_in'] == false)
{
	//the user is not signed in
	echo 'Sorry, you have to be <a href="/forum/signin.php">signed in</a> to create a topic.';
}
else
{
	//the user is signed in
	if($_SERVER['REQUEST_METHOD'] != 'POST')
	{
		//the form hasn't been posted yet, display it
		//retrieve the categories from the database for use in the dropdown
		$sql = "SELECT
					cat_id,
					cat_name,
					cat_description
				FROM
					categories";

		$result = mysql_query($sql);

		if(!$result)
		{
			//the query failed, uh-oh :-(
			echo 'Error while selecting from database. Please try again later.';
		}
		else
		{
			if(mysql_num_rows($result) == 0)
			{
				//there are no categories, so a topic can't be posted
				if($_SESSION['user_level'] == 1)
				{
					echo 'You have not created categories yet.';
				}
				else
				{
					echo 'Before you can post a topic, you must wait for an admin to create some categories.';
				}
			}
			else
			{

				echo '<form method="post" action="">
					Subject: <input type="text" name="topic_subject" />
					Category:'; 

				echo '<select name="topic_cat">';
					while($row = mysql_fetch_assoc($result))
					{
						echo '<option value="' . $row['cat_id'] . '">' . $row['cat_name'] . '</option>';
					}
				echo '</select>';	

				echo 'Message: <textarea name="post_content" /></textarea>
					<input type="submit" value="Create topic" />
				 </form>';
			}
		}
	}
	else
	{
		//start the transaction
		$query  = "BEGIN WORK;";
		$result = mysql_query($query);

		if(!$result)
		{
			//Damn! the query failed, quit
			echo 'An error occured while creating your topic. Please try again later.';
		}
		else
		{

			//the form has been posted, so save it
			//insert the topic into the topics table first, then we'll save the post into the posts table
			$sql = "INSERT INTO
						topics(topic_subject,
							   topic_date,
							   topic_cat,
							   topic_by)
				   VALUES('" . mysql_real_escape_string($_POST['topic_subject']) . "',
							   NOW(),
							   " . mysql_real_escape_string($_POST['topic_cat']) . ",
							   " . $_SESSION['user_id'] . "
							   )";

			$result = mysql_query($sql);
			if(!$result)
			{
				//something went wrong, display the error
				echo 'An error occured while inserting your data. Please try again later.' . mysql_error();
				$sql = "ROLLBACK;";
				$result = mysql_query($sql);
			}
			else
			{
				//the first query worked, now start the second, posts query
				//retrieve the id of the freshly created topic for usage in the posts query
				$topicid = mysql_insert_id();

				$sql = "INSERT INTO
							posts(post_content,
								  post_date,
								  post_topic,
								  post_by)
						VALUES
							('" . mysql_real_escape_string($_POST['post_content']) . "',
								  NOW(),
								  " . $topicid . ",
								  " . $_SESSION['user_id'] . "
							)";
				$result = mysql_query($sql);

				if(!$result)
				{
					//something went wrong, display the error
					echo 'An error occured while inserting your post. Please try again later.' . mysql_error();
					$sql = "ROLLBACK;";
					$result = mysql_query($sql);
				}
				else
				{
					$sql = "COMMIT;";
					$result = mysql_query($sql);

					//after a lot of work, the query succeeded!
					echo 'You have successfully created <a href="topic.php?id='. $topicid . '">your new topic</a>.';
				}
			}
		}
	}
}

include 'footer.php';
?>
	

I’ll discuss this page in two parts, showing the form and processing the form.

Showing the form
We’re starting with a simple HTML form. There is actually something special here, because we use a dropdown. This dropdown is filled with data from the database, using this query:

SELECT
 	cat_id,
 	cat_name,
 	cat_description
FROM
 	categories
	

That’s the only potentially confusing part here; it’s quite a piece of code, as you can see when looking at the create_topic.php file at the bottom of this step.

Processing the form

The process of saving the topic consists of two parts: saving the topic in the topics table and saving the first post in the posts table. This requires something quite advanced that goes a bit beyond the scope of this tutorial. It’s called a transaction, which basically means that we start by executing the start command and then rollback when there are database errors and commit when everything went well. More about transactions.

<?php
//start the transaction
$query  = "BEGIN WORK;";
$result = mysql_query($query);
//stop the transaction
$sql = "ROLLBACK;";
$result = mysql_query($sql);
//commit the transaction
$sql = "COMMIT;";
$result = mysql_query($sql);
?>
	

The first query being used to save the data is the topic creation query, which looks like this:

INSERT INTO
	topics(topic_subject,
               topic_date,
               topic_cat,
               topic_by)
VALUES('" . mysql_real_escape_string($_POST['topic_subject']) . "',
       NOW(),
       " . mysql_real_escape_string($_POST['topic_cat']) . ",
       " . $_SESSION['user_id'] . ")
	

At first the fields are defined, then the values to be inserted. We’ve seen the first one before, it’s just a string which is made safe by using mysql_real_escape_string(). The second value, NOW(), is a SQL function for the current time. The third value, however, is a value we haven’t seen before. It refers to a (valid) id of a category. The last value refers to an (existing) user_id which is, in this case, the value of $_SESSION[ëuser_id']. This variable was declared during the sign in process.

If the query executed without errors we proceed to the second query. Remember we are still doing a transaction here. If we would’ve got errors we would have used the ROLLBACK command.

INSERT INTO
        posts(post_content,
        post_date,
        post_topic,
        post_by)
VALUES
        ('" . mysql_real_escape_string($_POST['post_content']) . "',
         NOW(),
         " . $topicid . ",
         " . $_SESSION['user_id'] . ")
	

The first thing we do in this code is use mysql_insert_id() to retrieve the latest generated id from the topic_id field in the topics table. As you may remember from the first steps of this tutorial, the id is generated in the database using auto_increment.

Then the post is inserted into the posts table. This query looks a lot like the topics query. The only difference is that this post refers to the topic and the topic referred to a category. From the start, we decided to create a good data model and here is the result: a nice hierarchical structure.


Step 10: Category View

We’re going to make an overview page for a single category. We’ve just created a category, it would be handy to be able to view all the topics in it. First, create a page called category.php.

A short list of the things we need:

Needed for displaying the category

  • cat_name
  • cat_description

Needed for displaying all the topics

  • topic_id
  • topic_subject
  • topic_date
  • topic_cat

Let’s create the two SQL queries that retrieve exactly this data from the database.

SELECT
    cat_id,
    cat_name,
    cat_description
FROM
    categories
WHERE
    cat_id = " . mysql_real_escape_string($_GET['id'])
	

The query above selects all the categories from the database.

SELECT
    topic_id,
    topic_subject,
    topic_date,
    topic_cat
FROM
    topics
WHERE
    topic_cat = " . mysql_real_escape_string($_GET['id'])
	

The query above is executed in the while loop in which we echo the categories. By doing it this way, we’ll see all the categories and the latest topic for each of them.
The complete code of category.php will be the following:

<?php
//create_cat.php
include 'connect.php';
include 'header.php';

//first select the category based on $_GET['cat_id']
$sql = "SELECT
			cat_id,
			cat_name,
			cat_description
		FROM
			categories
		WHERE
			cat_id = " . mysql_real_escape_string($_GET['id']);

$result = mysql_query($sql);

if(!$result)
{
	echo 'The category could not be displayed, please try again later.' . mysql_error();
}
else
{
	if(mysql_num_rows($result) == 0)
	{
		echo 'This category does not exist.';
	}
	else
	{
		//display category data
		while($row = mysql_fetch_assoc($result))
		{
			echo '<h2>Topics in ′' . $row['cat_name'] . '′ category</h2>';
		}

		//do a query for the topics
		$sql = "SELECT
					topic_id,
					topic_subject,
					topic_date,
					topic_cat
				FROM
					topics
				WHERE
					topic_cat = " . mysql_real_escape_string($_GET['id']);

		$result = mysql_query($sql);

		if(!$result)
		{
			echo 'The topics could not be displayed, please try again later.';
		}
		else
		{
			if(mysql_num_rows($result) == 0)
			{
				echo 'There are no topics in this category yet.';
			}
			else
			{
				//prepare the table
				echo '<table border="1">
					  <tr>
						<th>Topic</th>
						<th>Created at</th>
					  </tr>';	

				while($row = mysql_fetch_assoc($result))
				{
					echo '<tr>';
						echo '<td class="leftpart">';
							echo '<h3><a href="topic.php?id=' . $row['topic_id'] . '">' . $row['topic_subject'] . '</a><h3>';
						echo '</td>';
						echo '<td class="rightpart">';
							echo date('d-m-Y', strtotime($row['topic_date']));
						echo '</td>';
					echo '</tr>';
				}
			}
		}
	}
}

include 'footer.php';
?>
	

And here is the final result of our categories page:


Step 11: Topic View

The SQL queries in this step are complicated ones. The PHP-part is all stuff that you’ve seen before. Let’s take a look at the queries. The first one retrieves basic information about the topic:

SELECT
    topic_id,
    topic_subject
FROM
    topics
WHERE
    topics.topic_id = " . mysql_real_escape_string($_GET['id'])
	

This information is displayed in the head of the table we will use to display all the data. Next, we retrieve all the posts in this topic from the database. The following query gives us exactly what we need:

SELECT
    posts.post_topic,
    posts.post_content,
    posts.post_date,
    posts.post_by,
    users.user_id,
    users.user_name
FROM
    posts
LEFT JOIN
    users
ON
    posts.post_by = users.user_id
WHERE
    posts.post_topic = " . mysql_real_escape_string($_GET['id'])
	

This time, we want information from the users and the posts table – so we use the LEFT JOIN again. The condition is: the user id should be the same as the post_by field. This way we can show the username of the user who replied at each post.

The final topic view looks like this:


Step 12: Adding a Reply

Let’s create the last missing part of this forum, the possibility to add a reply. We’ll start by creating a form:

<form method="post" action="reply.php?id=5">
    <textarea name="reply-content"></textarea>
    <input type="submit" value="Submit reply" />
</form>
	

The complete reply.php code looks like this.

<?php
//create_cat.php
include 'connect.php';
include 'header.php';

if($_SERVER['REQUEST_METHOD'] != 'POST')
{
	//someone is calling the file directly, which we don't want
	echo 'This file cannot be called directly.';
}
else
{
	//check for sign in status
	if(!$_SESSION['signed_in'])
	{
		echo 'You must be signed in to post a reply.';
	}
	else
	{
		//a real user posted a real reply
		$sql = "INSERT INTO
					posts(post_content,
						  post_date,
						  post_topic,
						  post_by)
				VALUES ('" . $_POST['reply-content'] . "',
						NOW(),
						" . mysql_real_escape_string($_GET['id']) . ",
						" . $_SESSION['user_id'] . ")";

		$result = mysql_query($sql);

		if(!$result)
		{
			echo 'Your reply has not been saved, please try again later.';
		}
		else
		{
			echo 'Your reply has been saved, check out <a href="topic.php?id=' . htmlentities($_GET['id']) . '">the topic</a>.';
		}
	}
}

include 'footer.php';
?>
	

The comments in the code pretty much detail what’s happening. We’re checking for a real user and then inserting the post into the database.


Finishing Up

Now that you’ve finished this tutorial, you should have a much better understanding of what it takes to build a forum. I hope my explanations were clear enough! Thanks again for reading.


Posted in Programming | Comments (0)

Learning Server-Side JavaScript with Node.js

March 16th, 2010

Node.js is all the buzz at the moment, and makes creating high performance, real-time web applications easy. It allows JavaScript to be used end to end, both on the server and on the client. This tutorial will walk you through the installation of Node and your first “Hello World” program, to building a scalable streaming Twitter server.

What is Node.js?

JavaScript has traditionally only run in the web browser, but recently there has been considerable interest in bringing it to the server side as well, thanks to the CommonJS project. Other server-side JavaScript environments include Jaxer and Narwhal. However, Node.js is a bit different from these solutions, because it is event-based rather than thread based. Web servers like Apache that are used to serve PHP and other CGI scripts are thread based because they spawn a system thread for every incoming request. While this is fine for many applications, the thread based model does not scale well with many long-lived connections like you would need in order to serve real-time applications like Friendfeed or Google Wave.

“Every I/O operation in Node.js is asynchronous…”

Node.js, uses an event loop instead of threads, and is able to scale to millions of concurrent connections. It takes advantage of the fact that servers spend most of their time waiting for I/O operations, like reading a file from a hard drive, accessing an external web service or waiting for a file to finish being uploaded, because these operations are much slower than in memory operations. Every I/O operation in Node.js is asynchronous, meaning that the server can continue to process incoming requests while the I/O operation is taking place. JavaScript is extremely well suited to event-based programming because it has anonymous functions and closures which make defining inline callbacks a cinch, and JavaScript developers already know how to program in this way. This event-based model makes Node.js very fast, and makes scaling real-time applications very easy.


Step 1 Installation

Node.js runs on Unix based systems, such as Mac OS X, Linux, and FreeBSD. Unfortunately, Windows is not yet supported, so if you are a Windows user, you can install it on Ubuntu Linux using Virtualbox. To do so, follow this tutorial. You will need to use the terminal to install and run Node.js.

  1. Download the latest release of Node.js from nodejs.org (the latest version at the time of this writing is 0.1.31) and unzip it.
  2. Open the terminal, and run the following commands.
    cd /path/to/nodejs
    make
    sudo make install
    		

    A lot of messages will be outputted to the terminal as Node.js is compiled and installed.


Step 2 Hello World!

Every new technology starts with a “Hello World!” tutorial, so we will create a simple HTTP server that serves up that message. First, however, you have to understand the Node.js module system. In Node, functionality is encapsulated in modules which must be loaded in order to be used. There are many modules listed in the Node.js documentation. You load these modules by using the require function like so:

var sys = require("sys");

This loads the sys module, which contains functions for dealing with system level tasks like printing output to the terminal. To use a function in a module, you call it on the variable that you stored the module in, in our case sys.

sys.puts("Hello World!");

Running these two lines is as simple as running the node command with the filename of the javascript file as an argument.

node test.js

This will output “Hello World!” to the command line when run.

To create an HTTP server, you must require the http module.

var sys = require("sys"),
    http = require("http");

http.createServer(function(request, response) {
    response.sendHeader(200, {"Content-Type": "text/html"});
    response.write("Hello World!");
    response.close();
}).listen(8080);

sys.puts("Server running at http://localhost:8080/");

This script imports the sys and http modules, and creates an HTTP server. The anonymous function that is passed into http.createServer will be called whenever a request comes in to the server. Once the server is created, it is told to listen on port 8080. When a request to the server comes in, we first send HTTP headers with the content type and status code of 200 (successful). Then we send “Hello World!” and close the connection. You might notice that we have to explicitly close the connection. This will make it very easy to stream data to the client without closing the connection. If you run this script and go to http://localhost:8080/ in your browser, you will see “Hello World!”


Step 3 A Simple Static File Server

OK, so we have built an HTTP server, but it doesn’t send anything except for “Hello World,” no matter what URL you go to. Any HTTP server must be able to send static files such as HTML files, images and other files. The following code does just that:

var sys = require("sys"),
    http = require("http"),
    url = require("url"),
    path = require("path"),
    fs = require("fs");

http.createServer(function(request, response) {
    var uri = url.parse(request.url).pathname;
    var filename = path.join(process.cwd(), uri);
    path.exists(filename, function(exists) {
    	if(!exists) {
    		response.sendHeader(404, {"Content-Type": "text/plain"});
    		response.write("404 Not Found\n");
    		response.close();
    		return;
    	}

    	fs.readFile(filename, "binary", function(err, file) {
    		if(err) {
    			response.sendHeader(500, {"Content-Type": "text/plain"});
    			response.write(err + "\n");
    			response.close();
    			return;
    		}

    		response.sendHeader(200);
    		response.write(file, "binary");
    		response.close();
    	});
    });
}).listen(8080);

sys.puts("Server running at http://localhost:8080/");

We start by requiring all of the modules that we will need in our code. This includes the sys, http, url, path, and fs or filesystem modules. Next we create an HTTP server like we did before. This time, we will use the url module to parse the incoming URL of the request and find the pathname of the file being accessed. We find the actual filename on the server’s hard drive by using path.join, which joins process.cwd(), or the current working directory, with the path to the file being requested. Next, we check if the file exists, which is an asynchronous operation and thus requires a callback. If the file does not exist, a 404 Not Found message is sent to the user and the function returns. Otherwise, we read the file using the fs module using the “binary” encoding, and send the file to the user. If there is an error reading the file, we present the error message to the user, and close the connection. Because all of this is asynchronous, the server is able to serve other requests while reading the file from the disk no matter how large it is.

If you run this example, and navigate to http://localhost:8080/path/to/file, that file will be shown in your browser.


Step 4 A Real Time Tweet Streamer

Building on our static file server, we will build a server in Node.js that streams tweets to a client that is served through our static file server. To start, we will need one extra module in this example: the events module. Node has a concept called an EventEmitter, which is used all over to handle event listeners for asynchronous tasks. Much like in jQuery or another client side JavaScript framework where you bind event listeners to things like mouse clicks, and AJAX requests, Node allows you to bind event listeners to many things, some of which we have already used. These include every I/O operation, such as reading a file, writing a file, checking if a file exists, waiting for HTTP requests, etc. The EventEmitter abstracts the logic of binding, unbinding, and triggering such event listeners. We will be using an EventEmitter to notify listeners when new tweets are loaded. The first few lines of our tweet streamer imports all of the required modules, and defines a function for handling static files, which was taken from our previous example.

var sys = require("sys"),
    http = require("http"),
    url = require("url"),
    path = require("path"),
    fs = require("fs"),
    events = require("events");

function load_static_file(uri, response) {
	var filename = path.join(process.cwd(), uri);
	path.exists(filename, function(exists) {
		if(!exists) {
			response.sendHeader(404, {"Content-Type": "text/plain"});
			response.write("404 Not Found\n");
			response.close();
			return;
		}

		fs.readFile(filename, "binary", function(err, file) {
			if(err) {
				response.sendHeader(500, {"Content-Type": "text/plain"});
				response.write(err + "\n");
				response.close();
				return;
			}

			response.sendHeader(200);
			response.write(file, "binary");
			response.close();
		});
	});
}

We have used the http module to create a server before, but it is also possible to create an HTTP client using the module. We will be creating an HTTP client to load tweets from Twitter’s public timeline, which is performed by the get_tweets function.

var twitter_client = http.createClient(80, "api.twitter.com");

var tweet_emitter = new events.EventEmitter();

function get_tweets() {
	var request = twitter_client.request("GET", "/1/statuses/public_timeline.json", {"host": "api.twitter.com"});

	request.addListener("response", function(response) {
		var body = "";
		response.addListener("data", function(data) {
			body += data;
		});

		response.addListener("end", function() {
			var tweets = JSON.parse(body);
			if(tweets.length > 0) {
				tweet_emitter.emit("tweets", tweets);
			}
		});
	});

	request.close();
}

setInterval(get_tweets, 5000);

First, we create an HTTP client on port 80 to api.twitter.com, and create a new EventEmitter. The get_tweets function creates an HTTP “GET” request to Twitter’s public timeline, and adds an event listener that will be triggered when Twitter’s servers respond. Because Node.js is asynchronous, the data in the body of the response comes in chunks, which are picked up by the response’s “data” listener. This listener simply appends the chunk to the body variable. Once all of the chunks have come in, the “end” listener is triggered, and we parse the incoming JSON data. If more than one tweet is returned, we emit the “tweets” event on our tweet_emitter, and pass in the array of new tweets. This will trigger all of the event listeners listening for the “tweets” event, and send the new tweets to each client. We retreive the new tweets every five seconds, by using setInterval.

Finally, we need to create the HTTP server to handle requests.

http.createServer(function(request, response) {
    var uri = url.parse(request.url).pathname;
    if(uri === "/stream") {

    	var listener = tweet_emitter.addListener("tweets", function(tweets) {
    		response.sendHeader(200, { "Content-Type" : "text/plain" });
    		response.write(JSON.stringify(tweets));
    		response.close();

    		clearTimeout(timeout);
    	});

    	var timeout = setTimeout(function() {
    		response.sendHeader(200, { "Content-Type" : "text/plain" });
    		response.write(JSON.stringify([]));
    		response.close();

    		tweet_emitter.removeListener(listener);
    	}, 10000);

    }
    else {
    	load_static_file(uri, response);
    }
}).listen(8080);

sys.puts("Server running at http://localhost:8080/");

Just as we did with our static file server, we create an HTTP server that listens on port 8080. We parse the requested URL, and if the URL is equal to "/stream", we will handle it, otherwise we pass the request off to our static file server. Streaming consists of creating a listener to listen for new tweets on our tweet_emitter, which will be triggered by our get_tweets function. We also create a timer to kill requests tht last over 10 seconds by sending them an empty array. When new tweets come in, we send the tweets as JSON data, and clear the timer. You will see how this works better after seeing the client side code, which is below. Save it as test.html in the same directory as the server side JavaScript.

<!DOCTYPE html>
<html>
	<head>
		<title>Tweet Streamer</title>
		<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
	</head>
	<body>
		<ul id="tweets"></ul>
		<script type="text/javascript">
		var tweet_list = $("#tweets");

		function load_tweets() {
			$.getJSON("/stream", function(tweets) {
				$.each(tweets, function() {
					$("<li>").html(this.text).prependTo(tweet_list);
				});
				load_tweets();
			});
		}

		setTimeout(load_tweets, 1000);
		</script>
	</body>
</html>

Here, we have a simple HTML page that imports the jQuery library and defines an unordered list to put the tweets in. Our client side JavaScript caches the tweet list, and runs the load_tweets function after one second. This gives the browser enough time to finish loading the page before we start the AJAX request to the server. The load_tweets function is very simple: It uses jQuery’s getJSON function to load /stream. When a response comes in, we loop through all of the tweets and prepend them to the tweet list. Then, we call load_tweets again. This effectively creates a loop that loads new tweets, which times out after ten seconds because of the timeout on the server. Whenever there are new tweets, they are pushed to the client which maintains a continuous connection to the server. This technique is called long-polling.

If you run the server using node and go to http://localhost:8080/test.html, you will see the Twitter public timeline stream into your browser.


Next Steps

Node.js is a very exciting technology that makes it easy to create high performance real time applications. I hope you can see its benefit, and can use it in some of your own applications. Because of Node’s great module system, it is easy to use prewritten code in your application, and there are many third party modules available for just about everything – including database connection layers, templating engines, mail clients, and even entire frameworks connecting all of these things together. You can see a complete list of modules on the Node.js wiki, and more Node tutorials can be found on How To Node. I would also recommend that you watch a video from JSConf, in which Ryan Dahl, the creator of Node, describes the design philosophy behind Node. That is available here.

I hope you have enjoyed this tutorial. If you have any comments, you can leave one here or send me a message on Twitter. Happy noding!


Posted in Programming | Comments (0)

How to Build a Lava-Lamp Style Navigation Menu

March 14th, 2010

A couple weeks ago, I created a screencast that demonstrated how to build a three-level navigation menu. In a response email, one of our readers requested a tutorial on how to build a lava-lamp style menu. Luckily, it’s quite a simple task, especially when using a JavaScript library. We’ll build one from scratch today.

Screenshot

Prefer a Screencast?

Step 1 Create the Mark-up

Before we can create this neat functionality, we need a base from which to work from. In your favorite code editor, create an unordered list for your navigation, and import both jQuery and jQuery UI, via Google.

<!DOCTYPE html>

<html lang="en">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>SpasticNav  Plugin</title>
	<link rel="stylesheet" href="css/style.css" type="text/css" media="screen" />
</head>
<body>

<div id="container">

	<ul id="nav">
		<li id="selected"><a href="#">Home</a></li>
		<li><a href="#">About</a></li>
		<li><a href="#">Blog</a></li>
		<li><a href="#">More About My Portfolio</a></li>
		<li><a href="#">Contact</a></li>
	</ul>

</div>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4/jquery.min.js" type="text/javascript"></script>	

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.2/jquery-ui.min.js"></script>

</body>
</html>

Note how we gave an id of “selected” to the home page. This is fairly standard in most websites; it allows use to target the current page, and style that particular list item accordingly.

Next, we must decide how to best implement the lava-lamp functionality. To allow for reusability, we’ll package this little script into a plugin, and call it like:

$('#nav').spasticNav();

Since we’ve decided to build a plugin, let’s go ahead and create a new file for that script, and reference it in our mark-up. We’ll call it jquery.spasticNav.js.

<script type="text/javascript" src="js/jquery.spasticNav.js"></script>	

<script type="text/javascript">
$('#nav').spasticNav();
</script>
</body>

Step 2 Beginning the Plugin

To reduce the number of global variables that we must create, as well as remove any possibilities of the $ symbol clashing with other JavaScript libraries, let’s wrap our plugin in a self-executing anonymous function.

(function($) {

})(jQuery);

Now, jQuery will be passed into our plugin, and will be represented via the $ symbol.

Next, it’s generally a best practice to give the users of the plugin as much flexibility as possible. As such, we’ll give them the option of passing in an object-literal when they call the plugin to override a handful of settings. As I see it, they should be able to:

  • Set the amount of overlap for our little blob. This refers to how much the blob will exceed the height of the navigation menu.
  • Set the speed
  • Set a reset, which causes the blob to move back to the current page item (assuming that the user never clicks on a link)
  • Set the color of the blob. This can be accomplished with CSS, but it’s a nice convenience, nonetheless.
  • Set the easing option.

Now, we’ll name our plugin, and make it equal to a function. $.fn is simply an alias for jquery.prototype.

$.fn.spasticNav = function(options) {

};

Knowing that we’ll be allowing these overrides, we must make sure that we accept an “options” parameter.

Step 3 Configuration Options

Now that we’ve named our plugin, the next step is to create the configuration options.

options = $.extend({
	overlap : 20,
	speed : 500,
	reset : 1500,
	color : '#0b2b61',
	easing : 'easeOutExpo'
}, options);

Above, we’re taking the options variable, setting some default properties and values, and then extending it with whatever (if anything) the user passes in when they call the plugin. That way, the options they pass will override our default settings. For example, if, when I call this plugin, I pass:

$('#nav').spasticNav({
   speed : 2000,
   easing : 'easeOutElastic'
});

Those two properties will override the default settings, while the remainder of the options will remain the same.

Step 4 Implementing the Functionality

Now, we’re ready to cycle through each element that was passed to this plugin, and implement the lava-lamp functionality. Remember, we can’t assume that the user is going to pass a single element to this plugin. They could, if they wanted, reference a class, which refers to multiple items that should receive this functionality. As such, we’ll call this.each to iterate over each item in the wrapped set.

return this.each(function() {

});

Within this function, we’ll create some variables. Not all of them will immediately have values, but since the JavaScript engine will hoist all variable names to the top of the function anyways (behind the scenes), it’s generally a best practice to declare them at the top, and then initialize them later.

var nav = $(this),
	currentPageItem = $('#selected', nav),
	blob,
	reset;
  • nav : “Caches” this, wrapped in the jQuery object.
  • currentPageItem : Contains the list item with an id of selected. We pass a second parameter to set the context to search from. That way, we don’t have to traverse the entire dom to find this element.
  • blob : For lack of a better word, this variable will reference the highlighter, that will follow our mouse when we hover over the menu.
  • reset : This will store a reference to the setTimeout function that will create later. It’s needed in order to call clearTimeout. More on this soon…

Now that we’ve declared/initialized our variables, let’s create the actual blob, so to speak.

$('<li id="blob"></li>').css({
	width : currentPageItem.outerWidth(),
	height : currentPageItem.outerHeight() + options.overlap,
	left : currentPageItem.position().left,
	top : currentPageItem.position().top - options.overlap / 2,
	backgroundColor : options.color
}).appendTo(this);

The reason why we’re calling the CSS method, rather than simply adding a class, is because these values will vary depending on the current page’s list item. As such, we must use JavaScript to retrieve they values.

  • width: Get the width of currentPageItem, including any borders and padding.
  • height: Get the height of currentPageItem, including any borders and padding. Also, add the amount of overlap, to make the blob extend outside of the menu.
  • left: Sets the left property of the blob equal to the left position of the currentPageItem. (We must set a positioning context in our CSS for this value to take effect.)
  • top: Sets the top value as well, and vertically centers the blob.
  • backgroundColor: Sets the background color.

Finally, we append this new list item to this, or #nav.

Next, we need to store a reference to #blob. That way, we don’t have to search the DOM everytime we wish to access it. We declared the blob variable at the top of the function. Now, let’s initialize it.

blob = $('#blob', nav);

Step 5 The Hover Event

We must now “listen” for when the user hovers over one of the list items (excluding the blob of course) in our navigation menu. When they do, we’ll set the width and left properties of the blob equal to that of the currently hovered list item.

$('li:not(#blob)', nav).hover(function() {
	// mouse over
	clearTimeout(reset);
	blob.animate(
		{
			left : $(this).position().left,
			width : $(this).width()
		},
		{
			duration : options.speed,
			easing : options.easing,
			queue : false
		}
	);
}, function() {
	// mouse out
	reset = setTimeout(function() {
		blob.animate({
			width : currentPageItem.outerWidth(),
			left : currentPageItem.position().left
		}, options.speed)
	}, options.reset);

});

To summarize the script above…

  • Get all list items – not the #blob – within the navigation menu, and when they’re hovered over, run a function.
  • Animate the blob, and set its left and width values equal to that of the hovered list item.
  • Pass an object literal as the second parameter of animate, and set the duration and easing equal to what we set in our configuration options. Set queue to false to prevent animation build-up.
  • When they mouse out, call setTimeOut, which will push the blob back to the current page item. If we didn’t do this, and the user didn’t click on a navigation link, the menu would show that they were on
    a different page entirely. This will, after a second or so, animate the blob back to currentPageItem.

And that’s all there is to it! This is a super simple plugin. The next step is to style our navigation menu.

Step 6 Styling the Menu

Without any styling, our menu should look similar to this:

Unstyled mark-up

Let’s first style the “nav” ul. Open your style.css file, and add:

#nav {
	position: relative;
	background: #292929;
	float: left;
}
Styling the navigation menu

Next, we’ll style each list item.

#nav li {
	float: left;
	list-style: none;
	border-right: 1px solid #4a4a4a;
	border-left: 1px solid black;
}

This simply floats each list item to the left, and adds a border to each side.

Styling the list items

Moving along, we next must style the anchor tags within our navigation menu.

#nav li a {
	color: #e3e3e3;
	position: relative;
	z-index: 2;
	float: left;
	font-size: 30px;
	font-family: helvetica, arial, sans-serif;
	text-decoration: none;
	padding: 30px 45px;
}

We’re setting a color, floating them to the left, setting some font values, and a healthy amount of padding. Take note of the z-index property. This is a necessity, and will be explained shortly. However, remember that, in order to adjust the z-index, we must set a positioning context, which we’ve done.

Styling the anchor tags

Because we’re not implementing a full reset stylesheet, let’s ensure that we zero out any default margins and padding on our ul and li, just to save any potential headaches.

ul, li {
	margin: 0; padding: 0;
}

The last step is to style the blob itself!

#blob {
	border-right: 1px solid #0059ec;
	border-left: 1px solid #0059ec;
	position: absolute;
	top: 0;
	z-index : 1;
	background: #0b2b61;
	background: -moz-linear-gradient(top, #0b2b61, #1153c0);
	background: -webkit-gradient(linear, left top, left bottom, from(#0b2b61), to(#1153c0));
	-moz-border-radius: 4px;
	-webkit-border-radius: 4px;
	-moz-box-shadow: 2px 3px 10px #011331;
	-webkit-box-shadow: 2px 3px 10px #011331;
}

Once again, we set some pretty colors for our borders, and add some background colors (including CSS3 gradients/borders/shadows for Firefox and Safari/Chrome). Once again, we see that z-index property. Without this, the blob will display above all of the text in the navigation menu. To counter this, we must be sure that its z-index property is LOWER than the list item’s! We also must set the position to absolute in order to adjust its top and left values with our plugin.

Screenshot

Conclusion

That’s all there is to it! With minimal effort, we’ve created a really neat looking navigation menu from scratch. Let me know if you have any questions! Thanks for reading and watching.


Posted in Programming | Comments (0)

CodeIgniter from Scratch: File Operations

March 12th, 2010

In today’s episode, we are going to be working with several helper functions, related to files, directories, and downloads. We are going to learn how to read, write, download files, and retrieve information about both files and directories. Also at the end we will build a small file browser that utilizes jQuery as well.

Catch Up

Day 11: File Operations

Get the Flash Player to see this player.

Final Example


Posted in Programming | Comments (0)

Save Time and Money by Systematizing your Photoshop Workflow

March 10th, 2010

Repetitive tasks can quickly become tedious. As a designer, you probably often find yourself designing the same elements over and over from scratch. STOP!

Wasting time is so old-fashioned. It also means you’re wasting money! So, let’s review some ways that you can automate and systematize your Photoshop workflow. And be sure to download the project base for all of your new designs!

File Tree & Re-usable Project Base

The first step in systematizing your workflow is to organize your files. Identify all common elements you use within your designs.

You may come to a list such as the following:

  • Home Page template
  • About Page
  • Contact Page
  • Services Page
  • Fonts
  • Assets

The Project Base

Consider how your final deliverables are used. If they are shipped to a coding business, be as organized and thorough as possible, including notes and fonts where applicable.

You can ensure that you have perfectly organized themes by creating a package that allows you to “fill in the blanks”, so to speak. Simply copy this folder, assign it to the project by renaming it to the project and get working. This is a very simple and efficient way to work, and means that websites you built four years ago can easily be edited, because everything is packed into its very own place.

Download the Project Base

In the zip file, you’ll find a simple re-useable template starting point. This file contains elements such as dividers, buttons and a pre-gridded template to speed up your design workflow.

See how simple starting a new project is now? It may take a couple of minutes to set-up, but the time saved later is more time for you to be doing what you love best – designing!

Now that we have a solid base to build upon, we can starting filling out our ‘boiler-plater’ with specific files and concepts.

Web Template

The sample template will allow you to create a page of your most used elements, ready for quick inclusion in your latest project.

If you’re a web designer in particular, you’ll find that a lot of your projects, even those different in every possible way – use and re-use common elements.

Begin by building a few re-usable templates. Don’t worry about which elements you wish to include just yet, because you can always go back and look at your previous projects and analyze more popular features – then re-create them here.

Personally, I use just one template. I have a home page (this contains elements such as news bulletins, blog post stylings, menus, logos and call to actions). I can modify this template to suit blog pages, buy it now forms and more.

The page also has a 960.gs pre-applied. This has been included for download at the end of the article, in the template boilerplate.

Once you’re set-up with this concept, you can build designs very quickly. However, you should definitely consider developing your own, and tailor it to your clients and team. You could go further by adding icons, search elements and online store related items – anything to suit your client base.

Grouping & Organizing

The above image demonstrates the tangible difference between an organized layer palette, and a messy one!

When you’re in a real creative flow, it can be difficult to stop and start naming and grouping layers. Photoshop doesn’t really help in this area by default either!

So, take the time as you go to name and group layers. This will help you later when coding the design, or if you ship if off for coding elsewhere.

Name each layer, and try to use a descriptive and short word – such as ‘menu’ for the navigation. Group specific areas, such as the header or a contact form.

This has a two-fold benefit.

  1. You can toggle whole areas (or the whole design) on or off in order to work on the background or other elements.
  2. You can move sections of a design without the need for moving each element. This can save hours per project!

So, take the time to stop every five minutes or so, and name and group each element. You’ll be glad you did!

Make good use of the select tool! You can toggle the select mode between layers and groups. This facilitates mass updating and re-aligning designs, so don’t forget it!

Actions

If you don’t use actions in your work-flow, then you should begin using them immediately.

Photoshop actions are entirely configurable lists of actions Photoshop should carry out automatically. You can create your own, or choose from the bounty available online. If you work on Photographs for example, and have 400 pictures to edit and resize, this would take a long time. Instead, you can create an action that adjusts the contrast, alters the size and then saves the file in .jpg format, blasting through 400 files in seconds! You must use actions!

If you do not have the knowledge, or experience to create your own actions, GraphicRiver has a large selection of actions available for purchase from $1!

The actions on sale range from simple photo effects to this action which can add various pre-formatted text sections such as contact details or blog posts in seconds.

Start using Actions and you will notice a significant increase in your productivity and reduce the amount of times you need to repeat monotonous tasks over and over.

Creating and Using an Action

Making your own action is easy. As an example, we’ll create a simple action to re-size several images at once, then save them.

First off, you need to hit the new action button, on the actions palette.

You shouldn’t need to change any settings as standard, bar changing the name for better organization. Photoshop is now recording your actions. So, all you need to do is re-size the image. Go to image size, and alter the dimensions. The action will change any image to these dimensions. Then hit save.

Finally, press stop on the action palette.

Now, when you press play on the action you’ve created, Photoshop will follow the list of defined actions on your image. Although very simple, an action such as this one could save you hours of monotonous resizing photos and images. This is only a very simple look at actions. They can do so much more, so take your time to get to know them and really use them. The beauty of them is in how you can record basically any action in Photoshop – selecting, changing styles, transforming, re-sizing and saving.

iPhone & UI Design

Do you work with user interfaces? If so, you really need to download a GUI element pack for Photoshop.

There are many available for most popular operating systems and mobile platforms.

For example, if you work with the iPhone, you can download the iPhone GUI pack that contains all elements available in the iPhone interface library, and then simply start designing your app. Simple, quick and much easier than attempting to create these from scratch.

Android GUI Kit

http://www.smashingmagazine.com/2009/08/18/android-gui-psd-vector-kit/

Teehan Lax Kits

The agency, Teehan Lax have several high quality, life-like UI kits. These are great for building an iphone app sales site, or designing the actual UI of an app. Kits are available for the iPhone, Palm Pre and Browsers.

Palm Pre

http://www.teehanlax.com/blog/2009/07/08/palm-pre-gui-psd/

iPhone

http://www.teehanlax.com/blog/2008/11/06/iphone-gui-psd-update-13/

Browser Elements

http://www.teehanlax.com/blog/2008/12/16/browser-form-elements-psd/

Conclusion

I hope we’ve given you some tips that you can use to improve your productivity and efficiency. Photoshop is a wonderful tool, but, if we’re not careful, we can find ourselves repeating the sames tasks over and over. So be sure to implement the above tips and start saving time, money and your sanity!

Download the Project Base

Do you have any Photoshop time-saving tips? Have any ideas on other tasks that could be automated to save time and repetition? Feel free to share them with the community via the comments.


Posted in Programming | Comments (0)

How to Create a Simple iTunes-like Slider

March 6th, 2010

When space is at a premium, making use of sliders is the optimal way to present information. Today, we’ll take a look at how to create a slider similar to the one used in the iTunes store.

iTunes Version

Developers often seek the functionality provided by sliders in order to fit lots of information in the space provided. But creating such a slider is not as difficult as you might think. With a little planning and some experimenting, you can create one rather quickly.

I believe a demo is worth a thousand words. Hit the demo and try it out yourselves.

Interested? Let’s get started right away!

Design Goals

Before we start coding, here are a few goals for this widget.

  • Minimize the space taken up by images by making the slideshow’s dimensions the same size of a single image and then fading between them.
  • Provide a vertical carousel of images on the side showing upcoming images.
  • Provide a method to manually move the carousel and the slideshow forward. In this instance, we make use of a simple anchor element.
  • On the carousel, the top most image is the next in line and will be displayed when the next button is clicked.
  • Minimize DOM manipulation as much as possible. That’s not to say we aren’t going to touch the DOM, it’s just that we aren’t going to meddle with the DOM too much.

Plan of Action

There are actually a handful of techniques to make a widget like this. For our purposes today, I’m going to stick with a technique which adheres to a saying:

When in doubt, use brute force.

Step 1: Setup the CSS for the gallery container so that all the main images collapse into taking the space of a single image. I’ll explain this point later below.

Step 2: Setup the CSS for the thumbnail container so that only three images are visible at once.

Step 3: Cycle through the images and assign a class to each thumbnail and image with a numeric index to identify each independently. For example, each image gets a class of thumb-xx where xx is a number.

Step 4: When the next button is clicked, move the carousel one thumbnail up and then display the thumbnail’s corresponding image.

These are the basic steps involved in creating such an effect. I’ll explain each step in detail as we go along.

Step 1: Core Markup

The HTML markup for the demo page looks like so:

<!DOCTYPE html>
<html lang="en-GB">
	<head>
		<title>iTunes slider</title>
		<link rel="stylesheet" href="style.css" type="text/css" />
	</head>

	<body>
    	<div id="container">
        	<h1>Create a simple iTunes-esque slider with jQuery</h1>
		<div>by Siddharth for the lovely folks at Net Tuts</div>
		<p>A simple slider/slideshow which mostly emulates the one on iTunes barring a few changes. Click the down button to cycle the images.</p> 

		<div id="gallery">
    		   <img src="img/1.jpg" />
    		   <img src="img/2.jpg" />
    		   <img src="img/3.jpg" />
    		   <img src="img/4.jpg" />
            	   <img src="img/5.jpg" />
            	   <img src="img/6.jpg" />
	        </div>

                <div id="thumbs">
    	   	   <img src="img/11.jpg" />
    		   <img src="img/22.jpg" />
    		   <img src="img/33.jpg" />
    		   <img src="img/44.jpg" />
            	   <img src="img/55.jpg" />
            	   <img src="img/66.jpg" />
	        </div>

        	<a href="#" id="next"></a>
        </div>

	<script type="text/javascript" src="js/jquery.js"></script>
	<script type="text/javascript" src="js/mocha.js"></script>

	</body>
</html>

Disregarding the boiler plate code, we have two container elements full of images: one for the main gallery images and one for the thumbnails. I’ve given an ID to both of them so they can be easily accessed from the JavaScript. We also include an anchor element which acts as the next button.

We include the jQuery library and our own script file at the end.

At the end of this stage, our demo page looks like just a list of images.

Tutorial Image

Step 2: CSS Styling

*{
	margin: 0;
	padding: 0;
	border: 0 none;
	outline: 0;
}

body{
	font-family: "Lucida Grande", "Verdana", sans-serif;
	font-size: 12px;
}

p{
	margin: 20px 0 40px 0;
}

h1{
	font-size: 30px;
	font-family: "Myriad Pro", "Lucida Grande", "Verdana", sans-serif;
	padding: 0;
	margin: 0;
}

h2{
	font-size: 20px;
}

#container{
	width: 900px;
	margin-left: auto;
	margin-right: auto;
	padding: 50px 0 0 0;
	position: relative;
}

img{
	display: block;
}

#gallery, #thumbs{
	float: left;
}

#gallery{
	width: 800px;
	height: 300px;
	overflow: hidden;
}

#gallery img{
	position: absolute;
}

#thumbs{
	width: 100px;
	height: 300px;
	overflow: hidden;
}

#next{
	display: block;
	width: 47px;
	height: 43px;
	background: url(img/arrow.png);
	position: relative;
	top: 257px;
	left: 855px;
}

#next:hover{
	background: url(img/arrowmo.png);
}

.clear{
	clear: both;
}

The CSS is pretty self explanatory but there are a couple of points I want you to take note of:

First up, notice that I’ve applied position: absolute to #gallery img. This makes sure that the images are stacked on top of each other instead of one below the other. This way we can later manipulate their opacity to decide which image to show.

Secondly, notice that the thumbs element has its height set to 300px. This is because the thumbnails in the demo are 100px tall each and I want the carousel to show 3 images at once. Essentially, for your own implementation, multiply the height of a thumbnail by the number of thumbnails you want to show at once to find the required height of the element.

Also, take note of the fact that we’ve set its overflow property to hidden to make sure no more than 3 thumbnails are shown at once.

After we’ve styled our slider, it looks like the image below. Notice that almost everything is in place. The last image is stacked at the top and is thus visible.

Tutorial Image

Step 3: JavaScript Implementation

Now that we have a solid framework and some basic styling in place, we can begin coding the required functionality. Note that we make extensive use of jQuery. Feel free to link to Google’s CDN if necessary.

Procuring the Elements and Prepping them

We first need to acquire the images and their corresponding thumbnails so that we can process them.

	var images = $("#gallery img");
	var thumbs = $("#thumbs img");
        var index = 0;

The above snippet will take care of obtaining the list of images and thumbnails, and storing them for later use. We also create a variable called index to denote which element to start from. For now, I’m setting it to start from the first element. Note that index is zero based.

	for (i=0; i<thumbs.length; i++)
	{
		$(thumbs[i]).addClass("thumb-"+i);
		$(images[i]).addClass("image-"+i);
	}

Next, we just iterate through both the lists and and add a class of thumb-xx or image-xx to each element where xx is a number. This lets us look for each individual thumbnail or image independently.

Hooking up the Handler

We now need to create an event handler and attach it to the next button so that we can do something when the button is clicked.

$("#next").click(sift);

The one liner above will take care of that. Essentially, we ask it to call the sift function everytime next is clicked.

function sift()
	{
		if (index<(thumbs.length-1)) {index+=1 ; }
		else {index=0}
		show (index);
	}

This is a very simple event handler actually. We just check to see what element is currently selected. If it is the last, we reset the index so the carousel goes back to the first element, thus creating a pseudo infinite carousel. Otherwise, we increment index by 1.

Next, we call the function show, passing in the index variable as a parameter. We’ll create the function in a bit.

Step 4: Implementing the Core Logic

function show(num)
	{
		images.fadeOut(400);
		$(".image-"+num).stop().fadeIn(400);
		var scrollPos = (num+1)*imgHeight;
		$("#thumbs").stop().animate({scrollTop: scrollPos}, 400);
	}

The show function implements the core functionality of this widget. Let me explain each part.

First, we fade out every image the gallery element contains. Next, we fade in just the required image making use of its class. Since each image can be accessed through its class and we have access to the positional index of the image, we just use the following selector: “.image-”+num

Next, we need to scroll the thumbnail element so that the required image is at the top of the carousel. There are two ways to go on about doing this.

The first method makes use of jQuery’s position property. This lets us find the element’s position relative to its parent element. Unfortunately, I’ve been running into quite a few problems with it and Chrome which means we’ll have to use our second method.

The next method is actually just as simple. Since we can easily obtain the height of a thumbnail and since each thumbnail is required to be of the same height, we can easily just find the product of the nth element’s position in the list and the height of a thumbnail to obtain its offset from the top.

var imgHeight = thumbs.attr("height");

The above line lets us obtain a thumbnail’s height. Remember that a collection of elements can be queried just like a normal element.

var scrollPos = (num+1)*imgHeight;

We now calculate the offset of the thumbnail we need. Since we need the thumbnail of the next element in the list and not of that image itself, we increment it by 1 before multiplying it by the height.

With all this info, we can now scroll the element to the height we need.

$("#thumbs").stop().animate({scrollTop: scrollPos}, 400);

We use jQuery’s animate property to alter the scrollTop property to the value we calculated above. If you are new to jQuery’s animation functions, refer to my earlier article. Essentially, we scroll the element x pixels from the top to create a carousel effect.

Step 5: A Few Tweaks

Polishing the Pseudo Infinite Effect

We are essentially done but a few quick bits of code will make it a little bit more polished.

thumbs.slice(0,3).clone().appendTo("#thumbs");

This line essentially takes the first three thumbnails, copies them over to the end of the list. The slice method selects the first three elements, the clone methods clones these DOM elements and finally the appendTo methods adds them to the passed element.

We can’t just use the appendTo method since it plucks the selected elements from their current position before placing it as required. We need the clone method to copy them first.

We do this to make sure when we approach the final few thumbnails, the illusion of an infinite carousel remains. Else, the user just sees empty blocks which isn’t really what we need.

Making it Auto Rotate

Making the widget auto rotate is actually very simple. Since we have a proper event handler in place, we just have to call the handler every n microseconds. The following line will take care of that:

setInterval(sift, 8000);

In the above code, I’ve asked to call the sift function every eight seconds. Remember, the duration is passed in as microseconds so n thousand equals n seconds.

Initializing the Widget

Currently, the page loads with the widget uninitialized. We’ll need to rectify this. All we need to do is to call the show function passing in the starting position as a parameter.

After you’ve attached the event handler, add this:

show(index);

The Final Code

And we are done! The final code looks like so:

$(document).ready(function()
{
	var index = 0;
	var images = $("#gallery img");
	var thumbs = $("#thumbs img");
	var imgHeight = thumbs.attr("height");
	thumbs.slice(0,3).clone().appendTo("#thumbs");
	for (i=0; i<thumbs.length; i++)
	{
		$(thumbs[i]).addClass("thumb-"+i);
		$(images[i]).addClass("image-"+i);
	}

	$("#next").click(sift);
	show(index);
	setInterval(sift, 8000);

	function sift()
	{
		if (index<(thumbs.length-1)){index+=1 ; }
		else {index=0}
		show (index);
	}

	function show(num)
	{
		images.fadeOut(400);
		$(".image-"+num).stop().fadeIn(400);
		var scrollPos = (num+1)*imgHeight;
		$("#thumbs").stop().animate({scrollTop: scrollPos}, 400);
	}
});

Conclusion

And there you have it: we’ve created a simple but useful slider. Hopefully you’ve found this tutorial interesting and useful. Feel free to reuse this code elsewhere in your projects, and chime in within the comments if you are running into difficulties.

Questions? Nice things to say? Criticisms? Hit the comments section and leave me a comment. Happy coding!


Posted in Programming | Comments (0)

Coding a Complex Design into CSS and HTML: New Plus Tutorial

March 4th, 2010

Today, we’re going to be taking the PSD website design, from Mahmoud’s Psdtuts+ Tutorial ,and coding it into valid, semantic HTML and CSS. Along the way, we’ll go over some essential CSS techniques, such as image replacement, sliding doors, and CSS sprites. This is a monster of a tutorial, as the design is a bit complex. Nevertheless, a relative beginner should be able to follow along, so put on a pot of coffee and let’s get started!

Help give back to Nettuts+ by becoming a Premium Member!

Final Product

Become a Premium Member

NETTUTS+ Screencasts and Bonus Tutorials

For those unfamiliar, the family of TUTS sites runs a premium membership service. For $9 per month, you gain access to exclusive premium tutorials, screencasts, and freebies from Nettuts+, Psdtuts+, Aetuts+, Audiotuts+, and Vectortuts+! For the price of a pizza, you’ll learn from some of the best minds in the business. Join today!

  • Subscribe to the Nettuts+ RSS Feed for more daily web development tuts and articles.


Posted in Programming | Comments (0)

Quick Tip: Learning jQuery 1.4’s $.proxy

March 2nd, 2010

One of my favorite new features in jQuery 1.4 is the new $.proxy method. It allows us force a particular context when calling a method. In JavaScript, there can be times when it’s difficult to hold on to the this keyword. For example, when it’s bound to some event handler, this now refers to the target of the handler, rather than your desired object.

If this sounds a bit confusing, don’t worry; today’s four-minute video quick tip should clear things up.


// Create an object.
var obj = {
        // this = obj
	somevar : 'some value',

	doSomething : function() {
		alert(this.somevar);
	}
};

// When bound to an event handler, this will
// refer to the target of the handler, or the div - not obj.
$('div').click(obj.doSomething); // undefined. 

// With $.proxy, we pass two parameters.
// 1. The method to call.
// 2. The context.
// In this case, we're forcing obj to once again be equal to this.
$('div').click( $.proxy(obj.doSomething, obj) ); // some value

Be sure to refer to the jQuery API for more information on $.proxy!


Posted in Programming | Comments (0)

CodeIgniter from Scratch: The Calendar Library

February 28th, 2010

In this tenth episode of the CodeIgniter From Scratch screencast series, we will be exploring the Calendar library. We are also going to utilize the database class and jQuery AJAX. I will show you how to build a simple and CSS-styled calendar page, which will have the ability to store and display content for each day.

Final Example

Catch Up

Day 10: The Calendar Library

Other Viewing Options


Posted in Programming | Comments (0)

Design a Prettier Web Form with CSS 3

February 26th, 2010

Thanks to advanced CSS properties, such as gradients and shadows, it’s now quite easy to turn a dull web form into something beautiful – with minimal effort. I’ll show you how in today’s tutorial!


Prefer a Video Tutorial? Join Plus!

If you’re more of a visual learner, you can watch a video version of this article instead. Simply help give back to Nettuts+ by signing up for a Plus membership, which grants you access to the Plus content for ALL of our sites – all for $9 per month.

Already a Member?

Watch the video version of this tutorial.

Our Final Product

Subtle background gradients give depth to the fields while shadows lift them from the page. Even more impressive is that this is done without any images at all.

By following this tutorial you will not only end up with a lightweight and beautiful form, you’ll also learn and understand new CSS3 techniques, such as box-shadow, gradients, opaque colors, and rounded corners.

CSS3?

CSS3 is the next generation of CSS that is currently under development, but that doesn’t stop browsers from already implementing most of the prominent features.

Full browser support:

Opera have support for CSS3 (except background gradients) in their next version (10.50 Beta).

Internet Explorer will have full CSS3 support with version 9.

The things you can do with CSS3 (shadows, gradients, round corners, animations, etc) all serve a purpose of creating beautiful effects without having to integrate images or scripts, resulting in quicker loading times.

Step 1: The HTML

Before we begin styling we need something to style, so here is the form.

<form class="form">

	<p class="name">
		<input type="text" name="name" id="name" />
		<label for="name">Name</label>
	</p>

	<p class="email">
		<input type="text" name="email" id="email" />
		<label for="email">E-mail</label>
	</p>

	<p class="web">
		<input type="text" name="web" id="web" />
		<label for="web">Website</label>
	</p>

	<p class="text">
		<textarea name="text"></textarea>
	</p>

	<p class="submit">
		<input type="submit" value="Send" />
	</p>

</form>

Each field is inside a paragraph with its own class, and the three first fields have a label explaining their use.

How does it look without any styling?

Functional, but dull. Let’s start pimping out this form.

Step 2: Basic Styling

Before we dive into the CSS3 techniques we need to create a basic layout for browsers that don’t yet support CSS3.

input, textarea {
	padding: 9px;
	border: solid 1px #E5E5E5;
	outline: 0;
	font: normal 13px/100% Verdana, Tahoma, sans-serif;
	width: 200px;
	background: #FFFFFF;
	}

textarea {
	width: 400px;
	max-width: 400px;
	height: 150px;
	line-height: 150%;
	}

input:hover, textarea:hover,
input:focus, textarea:focus {
	border-color: #C9C9C9;
	}

.form label {
	margin-left: 10px;
	color: #999999;
	}

.submit input {
	width: auto;
	padding: 9px 15px;
	background: #617798;
	border: 0;
	font-size: 14px;
	color: #FFFFFF;
	}

How does our effort look so far?

Not too bad. Now, let’s begin our enhancements with the more advanced CSS3.

Step 3: Box-shadow

Box-shadow does exactly what it sounds like: creates a shadow around a box.

The syntax for box-shadow is fairly simple:

box-shadow: <color> <horizontal offset> <vertical offset> <blur>;

Horizontal offset is the placement of the shadow from left to right. If you set it to “2px” the shadow will be 2 pixels to the right. Vertical offset is the same but up/down.

Blur is simply the amount of blur the shadow will have, where 0 is minimum.

This is how our box-shadow will look like:

input, textarea {
	box-shadow: rgba(0,0,0, 0.1) 0px 0px 8px;
	-moz-box-shadow: rgba(0,0,0, 0.1) 0px 0px 8px;
	-webkit-box-shadow: rgba(0,0,0, 0.1) 0px 0px 8px;
	}

Here we have three lines that look similar.

  • box-shadow is pure CSS3 and so far only used in Opera.
  • -webkit-box-shadow is for browsers using the Webkit engine, like Chrome and Safari.
  • -moz-box-shadow is for browsers using Mozilla’s Gecko engine, like Firefox, Camino, Flock, and SeaMonkey.

Until CSS3 becomes the standard, you have to use all three methods. Internet Explorer has their own weird way of doing things, and although it’s capable of making a shadow it will not look the way we want it. 3

You might notice that there was no normal RGB color used, this is because we’re using two CSS3 techniques on the same line: box-shadow and rgba.

RGBA (Red Green Blue Alpha) is, simply put, color with opacity.

The syntax for rgba is this:

rgba(<red>,<green>,<blue>,<opacity>);

It’s perfectly fine to use a light grey for the shadow’s color, but if you are using any other background than white it will look strange. An opaque black on the other hand will work well no matter what background.

So our box-shadow is black with 10% (0.1) opacity, no horizontal and vertical offset, and with a blur of 8 pixels. It will look like this:

The keyword here is subtlety. If we apply too much shadow, it will look ugly; if we apply too little, it won’t have an effect. Basically, we don’t want anyone to notice the shadow, but still have it lift the fields from the page.

Step 4: Background Gradient

While the box-shadow syntax is easy to grasp, gradients are trickier. With CSS3 gradients, you can create some amazing shapes — from dart boards to rainbows — so as you can imagine it has a more complex syntax. Thankfully, we don’t need to code a rainbow today; we just need a straight linear gradient.

Syntax for Webkit:

-webkit-gradient( linear, <start>, <end>, from(<color>), to(<color>) )

Syntax for Gecko:

-moz-linear-gradient(<start> <angle>, <color>, <color>)

As you can see, the methods are quite different, so this will require some explaining.

Webkit gradients require a start point (X and Y), an end point (X and Y), a from-color, and a to-color. The angle is determined by where start and end are, and the gradient will be colored with the “from(color)” fading to “to(color)”.

Gecko gradients, on the other hand, require only a start point (Y), and at least two colors. If you want a gradient going from top to bottom (90deg) you don’t need to assign an angle.

So to get a simple linear gradient from top to bottom – black to white – we would do like this:

background: -webkit-gradient(linear, left top, left bottom, from(#000000), to(#FFFFFF));
background: -moz-linear-gradient(top, #000000, #FFFFFF);

And it would appear like this:

(I will continue to use the black color for demonstration; at the end, I’ll switch to the real color we will be using for the form.)

Now that we have the basics out of the way, we can start making the form look how we want. The first thing we want to do is limit the height of the gradient so that it looks the same for both input fields and textarea; otherwise the gradient would fill the entire height, like this:

This is how we limit the background gradient to 25px in Webkit and Firefox:

input, textarea {
	background: -webkit-gradient(linear, left top, left 25, from(#000000), to(#FFFFFF));
	background: -moz-linear-gradient(top, #000000, #FFFFFF 25px);
	}

For Webkit, instead of setting the end point to “left bottom,” we set it to “left 25″, indicating it will end 25 pixels from the top.

For Gecko, we do the same thing by simply adding a “25px” value to the end color.

And the result is:

The second thing we want to do is create a thin white line at the top of the gradient, to give the subtle visual impression that the field is raised. How important can a single pixel be? Take a look at this article: Adding Depth with Pixel Perfect Line Work.

To create this, we’ll need three points in the gradient. In the previous example, our gradient had two points: top and bottom (black→white). Here, we’ll add an additional point in between them (white→black→white).

To illustrate:

How do we do this?

input, textarea {
	background: -webkit-gradient(linear, left top, left 25, from(#FFFFFF), color-stop(4%, #000000), to(#FFFFFF));
	background: -moz-linear-gradient(top, #FFFFFF, #000000 1px, #FFFFFF 25px);
	}

In Webkit we use the color-stop function, but unfortunately it doesn’t support values in pixels, only percentage. But thanks to paying attention to math in school we figure that 4% of 25px is 1px.

For Gecko, we simply add a third color between the first two and give it a “1px” value, indicating that it should end 1 pixel from the top.

The thin white line:

Now, let’s change the black color (#000000) to a more fitting light grey (#EEEEEE):

Just some small detail work remains.

First, we’ll create a darker shadow for the fields when the user hovers or selects it:

input:hover, textarea:hover,
input:focus, textarea:focus {
	-webkit-box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 8px;
	}

It’s just an increase from 10% to 15%, but what we are after is, once again, subtlety.

The last thing we do is create some rounded corners for the button3 to further make it stand out from the other elements:

.submit input {
	-webkit-border-radius: 5px;
	-moz-border-radius: 5px;
	}

The value is the radius the corners will be rounded by. The standard border-radius is intentionally left out since Opera seems to have some problem with it.

Result:

Step 5: The Other Browsers

Now we just need to take care of the browsers that don’t support CSS3 yet (IE), or only partly does (Opera).

We want the different versions (CSS3 and the normal) to look as similar as possible, and the simplest thing is to go back to the old way: images.

Simply take a screenshot of the beautiful CSS3 form and save a small portion of the gradient as an image.

Next, use it in the input and textarea as a background. As long as the CSS3 gradients comes after the background image, browsers that support CSS3 will ignore the image.

input, textarea {
	background: #FFFFFF url('bg_form.png') left top repeat-x;
	}

And now we are done! Enjoy your form and I hope you have learned something.

Final Preview

Chrome (4.0), Firefox (3.6), Safari (4.0):

Opera (10.50b):

Internet Explorer (8):

Full CSS

input, textarea {
	padding: 9px;
	border: solid 1px #E5E5E5;
	outline: 0;
	font: normal 13px/100% Verdana, Tahoma, sans-serif;
	width: 200px;
	background: #FFFFFF url('bg_form.png') left top repeat-x;
	background: -webkit-gradient(linear, left top, left 25, from(#FFFFFF), color-stop(4%, #EEEEEE), to(#FFFFFF));
	background: -moz-linear-gradient(top, #FFFFFF, #EEEEEE 1px, #FFFFFF 25px);
	box-shadow: rgba(0,0,0, 0.1) 0px 0px 8px;
	-moz-box-shadow: rgba(0,0,0, 0.1) 0px 0px 8px;
	-webkit-box-shadow: rgba(0,0,0, 0.1) 0px 0px 8px;
	}

textarea {
	width: 400px;
	max-width: 400px;
	height: 150px;
	line-height: 150%;
	}

input:hover, textarea:hover,
input:focus, textarea:focus {
	border-color: #C9C9C9;
	-webkit-box-shadow: rgba(0, 0, 0, 0.15) 0px 0px 8px;
	}

.form label {
	margin-left: 10px;
	color: #999999;
	}

.submit input {
	width: auto;
	padding: 9px 15px;
	background: #617798;
	border: 0;
	font-size: 14px;
	color: #FFFFFF;
	-moz-border-radius: 5px;
	-webkit-border-radius: 5px;
	}

Conclusion

That’s all there is to it! With minimal effort, and the power of CSS 3, we’ve turned a bland and ordinary form into something beautiful. Thanks so much for reading, and feel free to ask any questions that you might have below.

Write a Plus Tutorial

Did you know that you can earn up to $600 for writing a PLUS tutorial and/or screencast for us? We’re looking for in depth and well-written tutorials on HTML, CSS, PHP, and JavaScript. If you’re of the ability, please contact Jeffrey at nettuts@tutsplus.com.

Please note that actual compensation will be dependent upon the quality of the final tutorial and screencast.

Write a PLUS tutorial


Posted in Programming | Comments (0)