Add a Fade Effect to Your Forms

by Justin 6. April 2008 00:08

Forms are boring! Or are they? Make any form more interesting by morphing between your form's questions using mootools, a JavaScript framework. Just click the first link below and click "Next Question >>" to see the effect in action. Then click the second link to see what the page would look like if the user had JavaScript disabled.

Demo with JavaScript Enabled

Demo with JavaScript Disabled (Simulated)

Not only can this be used with a form, but with anything that you want to break up into steps, such as a wizard that guides the user through a list of new features for a product.

The technique I use is known as unobtrusive JavaScript, which means the page does not rely on the JavaScript to work. Rather, enabling JavaScript will result in some "bells and whistles," but if it's turned off the page is still 100% functional. My goal was to keep this script as simple as possible, so feel free to edit it and use it on any of your projects—free of charge or limitation.

The script basically works like this:

  1. Get all the questions, or steps, in the form.
  2. Dynamically add some navigation controls to move forward and backward for each question.
  3. Hide all the questions or steps, except for the first step.


Whenever a link is clicked an event is called to handle which step is faded in as the current step is faded out.

Download mootools script
Download fade effect script 

Note: Everything in the "options" section of the class is there to allow you to easily customize the script. The "initalize" section is basically a constructor for the class itself.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Internet Explorer 8 Beta 1 is Here!

by Justin 6. March 2008 01:14

Download IE8 Beta 1!

Perhaps the most incredible news came Monday when it was announced that the default rendering mode for IE8 will NOT be the legacy engine, but the web standards engine. This is great news for those of us who want the web to move toward interoperability.

I'll be honest, I didn't believe Microsoft when they claimed that web standards was going to be the default rendering mode. (I mean, I just watched a video a few weeks ago about how Microsoft doesn't want to break backward compatiblity.) So I installed IE8 Beta 1 and went straight to the Acid 2 test—and to my surprise it worked.

Internet Explorer 8 Beta 1 passes the Acid 2.. by default!

Now the new meta tag I mentioned has not been disgarded. Rather than rendering with the old engine by default, and using the meta tag to force "standards mode," Microsoft has opted to render in "standards mode" by default and using the meta tag to force IE8 to use the old rendering engine. In other words, if your site breaks in IE8 you'll need to add the new meta tag to it, or upgrade your site to follow web standards.

Needless to say, I could not be happier with their decision. If IE8 launches to the public as expected, then web standards should finally be the standard within a few years of the release date.

Developer Tools

The new "Developer Tools" seem to be a fusion of Microsoft's Internet Explorer Developer Toolbar and Firebug. One of the first things I noticed after installing IE8 was that the Developer Toolbar no longer works because it's been replaced by Developer Tools. It's a shame because there are many useful features of the Developer Toolbar that are currently absent from the new Developer Tools. If you need the toolbar to do your job then I'd recommend not installing IE8 Beta 1 until the Developer Tools have matured.

Anyone who's familiar with Firebugs' CSS tracing capabilities will probably be disappointed with the Beta 1 offering from Developer Tools. Unlike Firebug and the Developer Toolbar, you can not manipulate CSS on the fly. However, I liked Microsoft's new JavaScript debugger better than Firebug's debugger. What's been missing until now is a good, free JavaScript debugger for IE that's quick to open and easy to use. If you're familiar with Visual Studio's debugger you'll have no trouble figuring out the new JavaScript debugger. I was using it within seconds, whereas Firebug had took some experimenting to get used to.

For more information on IE8 check out the new Internet Explorer 8 Readiness Toolkit.

Conclusion 

If you're a web developer IE8 is definately something to be excited about. However, I uninstalled IE8 and went back to using IE7, the Web Developer Toolbar, and MultipleIEs. The lack of CSS manipulation capabilities in IE8 was disrupting the speed of a project that I am working on. Hopefully, Beta 2 will have better CSS support in the Developer Tools.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

The Ternary Operator (My Guilty Pleasure!)

by Justin 28. February 2008 11:37

Most of the time I am "Mr. Best Practice," but I have one flaw—I think the conditional ternary operator is great! Yes, we've heard it before—they are "hard to read" or "they are error prone," but I disagree. If you understand how it works, and you don't over-complicate your syntax, it's not hard to read or error prone at all. In fact, I believe if used correctly it can increase readibility by decreasing code clutter.

What is it? The conditional ternary operator is simply a conditional operator that accepts three operands. It takes a condition, and then depending on that condition returns one of two operands. Here I demonstrate the basic syntax..

variable = condition ? true part : false part;

Here are a few examples that may be easier to grasp..

int i = true ? 0 : 1; // i = 0
int j = false ? 0: 1; // j = 1

 

The ternary statement is basically just a short-hand way of writing five lines of code..

int i; // i = 0
if (true)
    i = 0; 
else
    i = 1; 

 

Do you use VB.NET? Just use the IIf function instead. The following two lines are in different languages (C# and VB.NET, respectively) but they produce the same result.

int i = true ? 1 : 2; // i = 1
Dim i as Integer = IIf(true,  1, 2) ' i = 1

 

Now the problem with the ternary operator is that people sometimes get crazy with it. You can embed a ternary statement inside of another ternary statement, just like you can embed an if inside of another if..

int k = true ? false ? 2 : 5 : 1; // k = 5

 

.. which is the same as ..

int k = true ? (false ? 2 : 5) : 1;

 

.. which is the same as ..

int k;
if (true)
{
	if (false)
		i = 2;
	else
		i = 5; 
}
else
	i = 1;

 

It doesn't stop there, you can put anything inside a ternary that's considered in-line code. That is, anything that doesn't require a line break. Here's a little exercise for you. Work it out in your head, then compile it to see what the answer is. (Of course, you'd never want to do anything like this in a real project!)

bool a = true, b = false, c = true;
bool d = a || b ? c && b ? a || c : b  && c : a || c ? b || c : a && b;

 

As you can see, conditional statements that use the ternary operator can quickly get out of hand, but as long as you don't get crazy they should still be readable. If they're not readable, or readability is questionable, then you should use the classic if/else block instead.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags:

Is Your Server's E-Mail Showing Up in Spam Folders?

by Justin 12. February 2008 10:19

The first step is to make sure your server is not blacklisted, which is pretty easy to do. Just click that link and type in the IP of your server.

The next step is to make sure the domain name of the e-mail address you're sending from routes back to the IP of your SMTP server.

In other words, say your sending e-mail from admin@mysite.com. Open the command line and ping "mysite.com" like this..

C:\Documents and Settings\Justin>ping mysite.com 

Pinging mysite.com [64.136.24.162] with 32 bytes of data:

Reply from 64.136.24.162: bytes=32 time=72ms TTL=240
Reply from 64.136.24.162: bytes=32 time=95ms TTL=240
Reply from 64.136.24.162: bytes=32 time=84ms TTL=240
Reply from 64.136.24.162: bytes=32 time=76ms TTL=240

Ping statistics for 64.136.24.162:
    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
    Minimum = 72ms, Maximum = 95ms, Average = 81ms

We can see that the IP address that routes back to mysite.com is 64.136.24.162.

If your SMTP server is not on this exact same IP your e-mails might be showing up in the spam folders of Hotmail and Gmail. In this case, your SMTP needs to be on 64.136.24.162—the IP of mysite.com. If it's on 64.136.24.163 then don't be surprised if you're showing up as spam.

Why? Because spammers often spoof e-mail addresses to make it harder for them to track. In their e-mail headers spammers might claim their spam is from admin@idontspam.com, but it's really coming from the IP of lovestospam.com. These days, most mail servers will double check the domain name of the "mail from" against the real name of the server. If something looks fishy in the spam folder it goes.

So make sure the IP of your SMTP matches up with the domain name you claim to be sending from.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

The Microsoft IE Web Standards Soap Opera Continues

by Justin 11. February 2008 13:09

Oh Lord!

Remember those DOCTYPE tags that were supposed to tell the browser how to render the content of the page? Well, they've pretty much lost their meaning. Why?

  1. Developers were using DOCTYPE tags for years without understanding their purpose.
  2. Internet Explorer was ignoring the web-standards behind the DOCTYPE.
  3. Developers only tested their pages in IE, possibly before the popularity of Firefox, Opera, Safari, etc.

In a nutshell, it was the developer's ignorance combined with Microsoft's lack of interest in updating Internet Explorer after version 6.0. (Microsoft released IE 1.0 to IE 6.0 in six years, but IE 6.0 to IE 7.0 took more than five years!)

The result? The inability to move Internet Explorer to web-standards without breaking all those sites that suffer from the aforementioned problems. Microsoft is very afraid of "breaking the web," as they say. A lot of people were angry when their pages didn't render "correctly" in IE7, though the browser was trying to rendering their page according to the DOCTYPE they were using, which was basically ignored in the old versions of IE.

Now things are supposed to be getting better. IE8 now passes the Acid 2 test, which is a great step forward for the Microsoft team, but we have to explicitly tell IE to render in standards mode using a new meta tag. If we fail to use this tag IE will still use the old rendering engine from yesteryears.

The New Meta Tag

The meta tag tells the browser which browser version the page was originally designed for.

<meta http-equiv="X-UA-Compatible" content="IE=8" />

In this case, the meta tag says the page was designed for IE8.

However, what frustrates many of us is that IE's default rendering behavior is still going to be the old one. This means that people who don't necessarily know what they're doing are still going to be designing pages for the old rendering engine—indefinately. I find that the world's most commonly-used browser is "opt-in" for web-standards is a little troubling, but it's the only sure way for IE to stay backwards compatible.

It's a little progress at a cost, I guess.

All I know is that I'll still be supporting IE6 and IE7 until their marketshare falls into negligible numbers, which doesn't look to be anytime soon. The one-design-fits-all dream is slightly closer, but still so far away. In the mean time I'm going to start making space on my sites for a new meta tag.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Convert that DetailsView's LinkButton to a Button

by Justin 7. February 2008 09:52
DetailsView with LinkButtons DetailsView with Buttons

I love .NET's Data Controls. Version 2.0 ushered in several new ones, one of which was the DetailsView. It's great except for one thing, the default buttons for "update", "insert", and "cancel" are LinkButtons! I first noticed this when I was working with the ASP.NET BeerHouse Starter Kit.

I've observed ordinary users trying to use these buttons and it always confuses them. The fact is, clicking text to submit a form is pretty rare, especially if the form is large and requires a lot of information. Almost always a button or a graphic that looks like a button is used. I'm a fan of consistency and intuitive user interfaces, so naturally I wanted to change the LinkButtons to good old-fashioned Buttons.

The problem is related to the AutoGenerateXXXXXButton attributes of the <asp:DetailsView /> control, where XXXXX is "Edit," "Insert," or "Delete." Get rid of them if you're using them. If you don't you'll find that you have two command fields later.

Create an <asp:CommandField /> element under the <Fields> tag of the DetailsView. Set the ButtonType to "Button." That is the most important part. If you don't set it it will default to "Link" and display LinkButtons again. We'll also need to tell it which command buttons to display via the ShowXXXXXButton attribute of the CommandField.  That's pretty much it. Below is an example of the code that generated the second graphic here on the right. The first graphic was the default look-and-feel for the DetailsView.

<asp:DetailsView ID="DetailsView2" runat="server" AutoGenerateRows="False"
    DefaultMode="Edit" DataSourceID="XmlDataSource1" GridLines="none">
    <Fields>
        <asp:BoundField DataField="Vin" HeaderText="Vin" SortExpression="Vin" />
        <asp:BoundField DataField="Make" HeaderText="Make" SortExpression="Make" />
        <asp:BoundField DataField="Model" HeaderText="Model" SortExpression="Model" />
        <asp:BoundField DataField="Year" HeaderText="Year" SortExpression="Year" />
        <asp:BoundField DataField="Price" HeaderText="Price" SortExpression="Price" />
        <asp:CommandField ButtonType="Button" ShowCancelButton="true" ShowDeleteButton="true" 
            ShowEditButton="true" ShowInsertButton="true" />
    </Fields>
</asp:DetailsView>
<asp:XmlDataSource ID="XmlDataSource1" runat="server" DataFile="~/CarList.xml"></asp:XmlDataSource>

 

If you'd rather use an image instead of a Button set ButtonType to "Image." You'll also have to give the URL of the image via the XXXXXImageUrl attribute of the CommandField, where "XXXXX" is the command type. (i.e. Cancel, Update, Insert, etc.)

Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , , ,

Create a Scaled Thumbnail with GDI+ (i.e. Constrain Proportions)

by Justin 6. February 2008 11:35

GDI+ comes with a function to create a thumbnail, but you must specify the size of the image. I wanted a function that I could pass an image, and the maximum height and width of the thumbnail. The function would then scale the image down to fit within that size; however, it would also retain the same height and width ratio so the thumbnail would not be distorted. Myself and a co-worker of mine (i.e. David Johnston) came up with a simple algorithm, and I coded it out. Note that this function will work not only with BMPs, but also GIF, JPG, PNG, etc—which are all basically compressed forms of a bitmap, and can be read into .NET's System.Drawing.Bitmap class.

Note: Please make sure you import the System.Drawing namespace if you plan on using this function. 

Update: This function uses the GetThumbnailImage method if the image is small (150px dimensions or less), because it produces more visually pleasing results when creating thumbnails. On the other hand, if you try to create a larger image using the GetThumbnailImage method the results will be blocky and overly compressed. This function takes this into consideration so it will always give good results.

C#

/// <summary>
/// Returns a new bitmap resized to fix a maximum size 
/// while still maintaining the correct aspect ratio.
/// </summary>
/// <param name="oldImage">The image to create a thumbnail from.</param>
/// <param name="maxWidth">The maximum allowed thumbnail width in pixels.</param>
/// <param name="maxHeight">The maximum allowed thumbnail height in pixels.</param>
/// <returns>A properly scaled thumbnail image.</returns>
private static Bitmap GetResizedImage(ref Bitmap oldImage, int maxWidth, int maxHeight)
{
    Bitmap newImage = null;
 
    // if the image is too large (i.e. needs to be resized).
    if (oldImage.Width > maxWidth || oldImage.Height > maxHeight)
    {
        // we use ratios to properly constrain proportions of the image.
        decimal imgRatio = (decimal)oldImage.Width / (decimal)oldImage.Height;
        decimal maxRatio = (decimal)maxWidth / (decimal)maxHeight;
 
        // if the image can be scaled down to perfectly fit the max height and max width.
        if (imgRatio == maxRatio)
        {
            newImage = new Bitmap(oldImage, maxWidth, maxHeight);
        }
        else if (imgRatio < maxRatio)
        {
            // Adjust the width to match the maximum height.
            decimal newRatio = (decimal)maxHeight / (decimal)oldImage.Height;
            int newWidth = (int)decimal.Round(newRatio * oldImage.Width);
 
            // if new image is a thumbnail then use the method that produces better thumbnail results.
            if (maxWidth <= 150 && maxHeight <= 150)
                newImage = (Bitmap)oldImage.GetThumbnailImage(newWidth, maxHeight, null, new IntPtr());
            else
                newImage = new Bitmap(oldImage, newWidth, maxHeight);
        }
        else
        {
            // Adjust the height to match the maximum width.
            decimal newRatio = (decimal)maxWidth / (decimal)oldImage.Width;
            int newHeight = (int)decimal.Round(newRatio * oldImage.Height);
 
            // if new image is a thumbnail then use the method that produces better thumbnail results.
            if (maxWidth <= 150 && maxHeight <= 150)
                newImage = (Bitmap)oldImage.GetThumbnailImage(maxWidth, newHeight, null, new IntPtr());
            else
                newImage = new Bitmap(oldImage, maxWidth, newHeight);
        }
    }
    else
    {
        // return a copy because it's smaller than our max size.
        newImage = new Bitmap(oldImage);
    }
 
    return newImage;
}

VB.NET

''' <summary>
''' Returns a new bitmap resized to fix a maximum size 
''' while still maintaining the correct aspect ratio.
''' </summary>
''' <param name="oldImage">The image to create a thumbnail from.</param>
''' <param name="maxWidth">The maximum allowed thumbnail width in pixels.</param>
''' <param name="maxHeight">The maximum allowed thumbnail height in pixels.</param>
''' <returns>A properly scaled thumbnail image.</returns>
Private Shared Function GetResizedImage(ByRef oldImage As Bitmap, ByVal maxWidth As Integer, ByVal maxHeight As Integer) As Bitmap
    Dim newImage As Bitmap = Nothing
 
    ' if the image is too large (i.e. needs to be resized).
    If oldImage.Width > maxWidth Or oldImage.Height > maxHeight Then
 
        ' we use ratios to maintain the proper aspect ratio of the image.
        Dim imgRatio As Decimal = CDec(oldImage.Width) / CDec(oldImage.Height)
        Dim maxRatio As Decimal = CDec(maxWidth) / CDec(maxHeight)
 
        ' if the image can be scaled down to perfectly fit the max height and max width.
        If imgRatio = maxRatio Then
            newImage = CType(oldImage.GetThumbnailImage(maxWidth, maxHeight, Nothing, New IntPtr()), Bitmap)
        ElseIf imgRatio < maxRatio Then
            ' Adjust the width to match the maximum height.
            Dim newRatio As Decimal = CDec(maxHeight) / CDec(oldImage.Height)
            Dim newWidth As Integer = CType(Decimal.Round(newRatio * oldImage.Width), Integer)
 
            ' if new image is a thumbnail then use the method that produces better thumbnail results.
            If maxWidth <= 150 AndAlso maxHeight <= 150 Then
                newImage = CType(oldImage.GetThumbnailImage(NewWidth, maxHeight, Nothing, New IntPtr()), Bitmap)
            Else
                newImage = New Bitmap(oldImage, newWidth, maxHeight)
            End If
        Else
            ' Adjust the height to match the maximum width.
            Dim newRatio As Decimal = CDec(maxWidth) / CDec(oldImage.Width)
            Dim newHeight As Integer = CInt(Decimal.Round(newRatio * oldImage.Height))
 
            ' if new image is a thumbnail then use the method that produces better thumbnail results.
            If maxWidth <= 150 AndAlso maxHeight <= 150 Then
                newImage = CType(oldImage.GetThumbnailImage(maxWidth, newHeight, Nothing, New IntPtr()), Bitmap)
            Else
                newImage = New Bitmap(oldImage, maxWidth, newHeight)
            End If
        End If
    Else
        ' return a copy because it's smaller than our max size.
        newImage = New Bitmap(oldImage)
    End If
    Return newImage
End Function

Currently rated 4.5 by 4 people

  • Currently 4.5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: , ,

Vertically Aligning Input Fields Using CSS (No Tables!)

by Justin 5. February 2008 20:35

A common design requirement is for the input fields of a form to be vertically aligned, each to the right of some descriptive text. The look is very clean and easily understood by users because of its widespread use.

Those who want to use this form layout along with a purely CSS-based design should try the following technique. Simply float a label element to the left and set its width. I can verify that this works in Firefox, Internet Explorer 6 and 7, Opera, and Safari. For reference I've included aligning input fields using a table so you may compare the differences.

Aligning Input Fields Using CSS

<!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">
    <head>
        <title>Aligning Form Fields Using CSS</title>
        <style type="text/css">
            .form-field
            {
                margin-bottom:5px;
            }
            .form-field label
            {
                float:left;
                width:120px;
            }
        </style>
    </head>
    <body>
        <form action="">
            <div class="form-field">
                <label for="name">Name:</label>
                <input id="name" type="text" name="name" />
            </div>
            <div class="form-field">
                <label for="email">E-mail:</label>
                <input id="email" type="text" name="email" />
            </div>
            <div class="form-field">
                <label for="subject">Subject:</label>
                <input id="subject" type="text" name="subject" />
            </div>
            <p>
                <label for="body">Body:</label><br />
                <textarea id="body" name="body" cols="30" rows="6">
                </textarea><br />
                <input type="submit" />
            </p>
        </form>
    </body>
</html>

 

Aligning Input Fields Using a Table

<!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">
    <head>
        <title>Aligning Form Fields Using a Table</title>
        <style type="text/css">
            .input-align
            {
                text-align:right;
            }
        </style>
    </head>
    <body>
        <form action="">
            <table style="width:260px">
                <tr>
                    <td>
                        <label for="name">Name:</label>
                    </td>
                    <td class="input-align">
                        <input id="name" type="text" name="name" />
                    </td>
                </tr>
                <tr>
                    <td>
                        <label for="email">E-mail:</label>
                    </td>
                    <td class="input-align">
                        <input id="email" type="text" name="email" />
                    </td>
                </tr>
                <tr>
                    <td>
                        <label for="subject">Subject:</label>
                    </td>
                    <td class="input-align">
                        <input id="subject" type="text" name="subject" />
                    </td>
                </tr>
            </table>
            <p>
                <label for="body">Body:</label><br />
                <textarea id="body" name="body" cols="30" rows="6"> 
                </textarea><br />
                <input type="submit" />
            </p>
        </form>
    </body>
</html>

Currently rated 3.0 by 3 people

  • Currently 3/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

AJAX is great, but not always..

by Justin 4. February 2008 20:10

I've noticed that AJAX has the same affect on people as OOP. Once someone finally grasps the concept(s) they become so excited that they find a way to overuse the technology in a way that demonstrates its weaknesses.

A good example of poor AJAX use is flickr's camera finder feature. You choose a camera and it shows examples of pictures that were taken with the camera.

Video Demonstration: ajax_navigation_issue.wmv (1.47 mb)

  1. Change the "Now showing" drop down box from "interesting pictures" to "night photos."
  2. You'll notice what's obviously an AJAX update.
  3. Click one of the "night" photos.
  4. Now click the back button.
  5. Notice that you're shown the "interesting pictures" again, but the drop down list says "night photos"!

To view the night pictures again you must change the "Now showing" drop down list to something else, then change it back to "night photos" again. Then you'll actually see the "night photos" again. How annoying. I ended up working around this by opening each picture in a new Firefox tab, but how many web users know how to do this?

Someone else that I used to work with done something very similar after he first learned AJAX techniques. It was a gallery feature that had thousands of pictures and used pagination. I would skip through 30 pages looking at thumbnails until I found a picture I liked. Clicking on an image would take us to another page, showing the picture in its full size and giving some basic information about it.

However, when I clicked the back button I was no longer on page 30, but on page 1 again! Really, really annoying and very similar to the problem mentioned above. The ironic part was that the AJAX was updating around 80% of the HTML on the gallery page! I told him, politely, that a good old fashioned post-back would be the best solution here. He resisted because of the amount of work he already put into AJAX calls, and he even talked about fixing it with some incredibly complicated AJAX-based solution, but eventually he agreed with me and removed AJAX from the gallery altogether.

My point isn't that I don't like AJAX, but that AJAX is NOT the be-all-end-all of web development. It is NOT a solution to every problem, or even most of them. AJAX has the potential to lessen the strain on servers by reducing the amount of data needed to be transferred, and also to perform some pretty slick effects that were difficult several years ago, but you need to know when to use it. Before you ever take on an AJAX-based solution you need to make sure it will improve the user's experience, not make it worse. If you get nothing else from this entry, just remember to always be careful when mixing AJAX with navigation.

Be the first to rate this post

  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags: ,

Powered by BlogEngine.NET 1.4.5.0
Theme by Mads Kristensen

About the author

Justin HoltonHi, my  name is Justin. I cut my teeth learning HTML back when Netscape Navigator was still the most popular web browser. Later that inspired me to major in Computer Science at college. Today I'm a professional web developer with experience in everything from social networking application design to Search Engine Optimization (SEO). I believe the Internet is the most important achievement of man since the printing press, and I'm grateful that I was born in time to see it go from obscurity to a ubiquity.

Page List