27
Apr

JavaScript Dropdown Menu

This lightweight JavaScript drop down menu script (~1.6kb) allows you to easily add smooth transitioning dropdowns to your website. This can be used for navigation, dropdown lists, info panels, etc. The script has been tested working in IE6, IE7, IE8, Firefox, Opera and Safari. The markup for the menu including the mouse events looks like the following.

<dl class="dropdown">
<dt id="one-ddheader" onmouseover="ddMenu('one',1)" onmouseout="ddMenu('one',-1)">Dropdown One</dt>
<dd id="one-ddcontent" onmouseover="cancelHide('one')" onmouseout="ddMenu('one',-1)">
<ul>
<li><a href="#" class="underline">Navigation Item 1</a></li>
<li><a href="#" class="underline">Navigation Item 2</a></li>
<li><a href="#" class="underline">Navigation Item 3</a></li>
<li><a href="#" class="underline">Navigation Item 4</a></li>
<li><a href="#">Navigation Item 5</a></li>
</ul>
</dl>

You can have as many dropdowns on one page as you like. Just be sure and have a unique naming scheme for each menu (i.e. one-ddheader, two-ddheader, etc). The “look and feel” is fully customizable though the CSS and you can replace the unordered list with any content you like if you are not looking for a menu.

Click here for the demo.

Click here to download the source.

Click here for a vertical flyout version of this menu.

By Request: Slideup version demo.

Please report any bugs to michael@leigeber.com.

Update 4/28/2008 - I have updated the HTML and CSS to use a definition list and an unordered list in lieu of the previous div-based layout. Thanks to Paul and Deigo for the suggestion.
Update 4/28/2008 - Fixed flicker issue in FF2 on Mac and “-1″ error in IE when quickly moving from the menu to the browser. Thanks to Philip and Chris for reporting these.
Update 5/16/2008 - Optimized the code reducing size by ~15%.


173 responses so far
6 Diggs Spread This
25
Apr

To begin I would like to thank all of you that have shown interest in this blog and posted comments. The site launched one week ago and has already generated over 13,000 unique visitors and 40,000 page views. As I am writing this article my previous post is on the front page of del.icio.us. I look forward to expanding and improving the site and ask that if you have any comments or suggestions please email me at michael@leigeber.com.

Following up on my post last week about the eAccelerator PHP optimizer and cache manager I thought I would post a general introduction to caching. This article focuses on caching in an Apache/PHP environment but the principles remain the same for any platform/language you work with.

Browser Cache

Every browser has it’s own cache but the size and behavior of each varies. Fortunately we are not at the sole mercy of the browser to determine how to handle our data. Through the use of HTTP headers you can dictate to the browser when to request updated content and when to serve files from the local cache. I highly suggest downloading the Firefox plugins Firebug and YSlow to help you analyze your HTTP headers. Below you can see a screenshot of the headers tab available on any HTTP request through Firebug.

Firebug Headers

The difficulty of caching is determining how you want the browser to cache and then pulling it all together on the server-side. Different file types, for instance, might be generally more static than others and therefore safer to cache for long periods of time. On the other hand, some content may updated on a regular basis and not need to be cached for long, if at all. Since the browser is closest to the user and your server is potentially never involved in the serving of content the highest performance boost can come from effective browser caching.

Below is an example of setting a header property in PHP. This would tell the browser to cache the files for one year from the date of this post.

<?php
header('Expires: Fri, 25 Apr 2009 00:00:00 GMT');
?>

Here is an example of setting headers with Apache. This would set an expiration of one year from today on all images files.

<FilesMatch "\.(jpg|jpeg|png|gif)$">
Header set Expires "Fri, 25 Apr 2009 00:00:00 GMT"
</FilesMatch>

There are a number of header parameters that can be sent as part of your response to HTTP requests. Here is a breakdown of the ones you should be familiar with for caching purposes…

Last-modified: Fri, 25 Apr 2008 00:00:00 GMT
This header tells the browser when the file being requested was last altered. The browser “asks” your server if it has a file that has a more recent “Last-modified” timestamp than the version that is currently has stored. If a newer file exists on your server then the browser requests the updated file, else the existing file is served. Although the communication does have a little overhead, it is much more efficient than simply serving the same unmodified content over and over.

Etag: “28ff-44aee6630f900″
Etags are basically unique identifiers attached to your files that the browser can use to compare cached files against. This works much like the “Last-modified” tag and there has been quite a bit of debate as to whether one is better than the other or whether to include both. I personally suggest including both as there may be rare situations where the Etag would detect changes that are not effected in the timestamp.

Expires: Fri, 25 Apr 2009 00:00:00 GMT
The expires header is ideal when you can plan on how long your content is safely cacheable. Why is it superior to the previous two cache controls? Using expires does not require a trip to the server to verify the freshness of your content. The browser simply serves the files from the local cache for the fastest user experience and zero server overhead.

Cache-Control: max-age=86400
The max-age tag, much like the expires header, eliminates the need to check for updated content when the cached file is within the age limit specified. The value assigned to the max-age is the number of milliseconds the file will be considered fresh. During that time, the locally stored files will be served. It is important to note that HTTP/1.1 allows caching of anything unless overridden by the “Cache-Control” header.


For static content I highly suggest serving files from a cookie-free subdomain (i.e. static.domain.com) and establishing a “never expires” policy. Many larger site have already taken advantage of this tactic but smaller sites can also benefit from the method. When you need to make a change to a file simply change the reference to the file to an incremented version (javascrtipt_1.js -> javascript_2.js) of itself and then the newer version will be downloaded and cached. There are even ways to automate the versioning process.

For dynamic files it is best to use the Cache-Control: no-cache header and for more static files the Last-modified header is appropriate. Another method to ensure content is refreshed is to append some unique querystring value to the URI.

Example of setting a “never expires” header for all static files…
<FilesMatch "\.(jpg|jpeg|png|gif|swf|css|js|ico|pdf)$">
Header add "Expires" "Mon, 01 Jan 2018 00:00:00 GMT"
Header add "Cache-Control" "max-age=31536000"
</FilesMatch>

or with PHP…
<?php
header('Expires: Mon, 01 Jan 2018 00:00:00 GMT');
header('Cache-Control: max-age=31536000');
?>

Server Caching

Server-side caching can have a huge impact on performance. Since the highest level of caching your PHP files should be set to is Last-modified, Apache will be serving these files most frequently. There are a number of third party caching/PHP optimizers that make caching a breeze such as XCache, eAccelerator, memcached and others. You can even store your compiled pages in memory with these optimizers for an even faster client/server transaction.

You are not limited to using one of these third parties for caching. You can manually cache files in PHP through the use of code like the following…

<?php
$current = $_SERVER["SCRIPT_NAME"];
$parts = Explode(’/', $current);
$current = $parts[count($parts) - 1];
$store = ‘cache/’;
$page = $_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI'];
$cache = $store.md5($page).’.cache’;
if(file_exists($cache) && (filemtime($current) < filemtime($cache)) ) {
@readfile($cache);
exit();
}
ob_start();
// YOUR PHP SCRIPT //
$new = fopen($cache, ‘w’);
fwrite($new, ob_get_contents());
fclose($new);
ob_end_flush();
?>

Intermediary Caching

By intermediary caching I am referring to anything between the browser and the server that caches your content. Perhaps you are using a third party CDN (content delivery network) such as Akamai or edgecast to speed up HTTP request delivery. These types of services are tailored to high volume websites with widespread user-bases and are generally cost-prohibitive to small-medium sized websites.

There are also other caches you may not not even know exist. Many large corporations, educational institutions and even countries cache content coming into their network. Oftentimes the proxies function much like browsers in their respect for HTTP headers however they do not always abide by your rules so be sure and identify private content by defining unique querystring parameters lest sensitive information be spread to multiple recipients.


Hopefully, if you are not caching now, you will be motivated to implement a caching policy soon. I plan to follow up soon with a more in-depth Apache/PHP caching post.


12 responses so far
3 Diggs Spread This
22
Apr

JavaScript dialog boxes

I have put together a lightweight (~4.5kb) JavaScript dialog box library. The script currently offers four dialog styles: alerts, warnings, prompts and success. There is nothing to add to your page except references to the JavaScript and CSS if you choose not to incorporate them in your existing files. The Divs are dynamically added to the DOM when the function is called. The function currently relies on a content wrapper to calculate the page height however you could use the body height but the background overlay would only cover the currently visible content. The variable for the wrapper ID as well as the speed and timer settings are available at the top of the JavaScript file. Dialog boxes are generated as follows…

showDialog('Error','You have encountered an error.','error',2);

The first property is the title of the box, the second is the message, the third is the box style (alert, warning, prompt or success) and the fourth is an optional autohide timeout. Set the auto hide to the number of seconds you want to show the dialog before it fades out. The message can take HTML, just be sure and escape when necessary. The “look and feel” can easily be changed through the CSS and additional styles can easily be added by adding 2 lines of CSS.

The script is tested working in IE 6/7, Firefox 2/3, Opera and Safari. It functions in IE8 but not 100% correctly due to the lack of a the alpha(opacity=xx) property. Hopefully it will be added before it leaves beta.

Update 4/23/2008 - Added autohide feature, thanks for the suggestion Chris.
Update 4/23/2008 - Added dynamic vertical positioning, 1/3 way down the current viewable window.
Update 4/25/2008 - Resolved double click issue when closing.
Update 5/21/2008 - Resolved issue when clicking to close the dialog box before the fading animation completes.

View the demo.

Download the source.


139 responses so far
3 Diggs Spread This
21
Apr

Based on the feedback from my article last Friday I thought it would be useful to create a CSS cheat-sheet. For those of you that are not familiar with shorthand you can print this out and keep it handy. Try and use these principles whenever you code and before too long shorthand will become second nature.

css-cheat-sheet.pdf


40 responses so far
9 Diggs Spread This
20
Apr

JavaScript is an extremely powerful and flexible scripting language. Unfortunately flexible, for many people, means fallible. I am going to highlight 5 best practices that you can use in any JavaScript project. These are very broad, I will follow up soon with more specific best practices.

First and foremost, keep your code simple, clean and well documented. This is by no means unique to JavaScript but many people seem to think it is the exception to the rule. Your code should naturally comment itself but it is also important to at least introduce every function. I recommend two versions, a fully documented and formatted version and then a compressed version that you use in production. There are a number of free online utilities that can strip out comments and pack your script. There is no need to push out the extra size required for the documentation and formatting.

Second, keep your JavaScript in an external file. The only exception to this rule is if you have some very lightweight script specific to a single page or are setting up variables that cannot be done in the external JS. An external file results in greater scalability, maintainability and degradability. The correct way to reference an external JS file is as follows:

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

Third, separate your JavaScript from the presentation layer. We have all heard of unobtrusive JavaScript but we still see inline script all the time. Instead of cluttering your font-end code with dozens of event handlers add them dynamically. There are exceptions to this rule so please use common sense and separate the layers when it makes sense. An example of adding an onclick event handler from JavaScript:

var div = document.getElementById('div');
div.onclick = new Function("processClick(this)");

Fourth, properly define and scope variables. Many of the scripts I download hoping to use in a project I immediately throw out. The reason being that the programmer did not take the time to properly define and scope variables. Always reference the first instance of a variable by using var. Otherwise the only way someone can know if that is the first reference to that variable is by starting at the top and reading all the way down. It is also important to scope variables correctly. Don’t scope a variable on the global level unless you need it there. I also recommend differentiation of global and local variables though some kind of visual identifier such is all caps on global variables or some easily identifiable character.

Fifth, don’t assume JavaScript support in the first place. Depending on your audience you may choose to disregard this suggestion but for mainstream websites I highly suggest coding with the minority in mind (an estimated 5-10% of web users do not have JavaScript enabled) and degrade your scripts gracefully. JavaScript should be considered as an added feature and not a dependency. An examples of this would be links, the most fundamental element of a web page.

<a href="javascript:processClick()">link</a>
<a href="#" onclick="javascript:processClick()">link</a>

If the user clicks the either of the links above with JavaScript disabled nothing will happen. However, with the code below they could still navigate.

<a href="link.html" onclick="processClick(); return false;">link</a>


23 responses so far
7 Diggs Spread This
Subscribe to RSS Feed
Powered by FeedBurner
Recent Links
Tag Cloud