djsipe.com | Web Development

We’ve all seen them, links that look like they go somewhere but when you roll your mouse over them you see something like “javascript:popupWindow();” in the status bar instead of a URL. Putting JavaScript into the href of links is just a horrible idea as it severely degrades the accessibility of your site (and ticks off people like me who want to see where they’re about to be taken before they click). But rather than just complain about it, I thought I’d put together some code to help us all avoid this situation all together.

The ideal solution is one that will work for the greatest number of users. Given that, we need a solution that will use unobtrusive JavaScript to enhance a standard HTML page with customizable popup windows—rather than rely solely on JavaScript. Since straight-up HTML supports opening links in new windows by adding a target attribute to the link, we start off with a link that looks like this:

<a href="/popup.html" target="popupWindow" class="popup">Open new window</a>

Using this as a starting point, the next step is to create a simple popup window function that wraps the standard DOM method: window.open().  I use this function to do a few things:

  1. Enforce default parameters rather than depend on the browser’s defaults
  2. Provide defaults that favor greater accessibility, ie: scrollbars and resize
  3. Allow a JavaScript object literal to be used instead of the standard configuration string

Here’s the function:

// Create a namespace for our utilities
var UTIL = UTIL || {};
UTIL.popup = UTIL.popup || {};

/**
 * Open popup window
 *
 * Opens a popup window using as little as a URL. An optional params object can
 * be passed.
 *
 * @param {String} href
 * @param {Object} params
 * @return {WindowObjectReference}
 */
UTIL.popup.open = function (href, params)
{
    // Defaults (don't leave it to the browser)
    var defaultParams = {
        "width":       "800",   // Window width
        "height":      "600",   // Window height
        "top":         "0",     // Y offset (in pixels) from top of screen
        "left":        "0",     // X offset (in pixels) from left side of screen
        "directories": "no",    // Show directories/Links bar?
        "location":    "no",    // Show location/address bar?
        "resizeable":  "yes",   // Make the window resizable?
        "menubar":     "no",    // Show the menu bar?
        "toolbar":     "no",    // Show the tool (Back button etc.) bar?
        "scrollbars":  "yes",   // Show scrollbars?
        "status":      "no"     // Show the status bar?
    };

    var windowName = params["windowName"] || "new_window";

    var i, useParams = "";

    // Override defaults with custom values while we construct the params string
    for (i in defaultParams)
    {
        useParams += (useParams === "") ? "" : ",";
        useParams += i + "=";
        useParams += params[i] || defaultParams[i];
    }

    return window.open(href, windowName, useParams);
};

Admittedly, this part isn’t rocket science and could probably be done in a more elegant way, but it’s needed to really open things up for the next part.  Now we get to the fun stuff.  Using jQuery, we search the document for all links that have a CSS class of “popup”.  For each one we find, we add an onClick handler that disables the browser’s default onClick behavior for links and then opens up a popup window using the links href attribute.  Here’s the code:

$(function(){ // Run this code when the document's done loading    

    // Apply this code to each link with class="popup"
    $("a.popup").each(function (i){

        // Add an onClick behavior to this link
        $(this).click(function(event) {

            // Prevent the browser's default onClick handler
            event.preventDefault();

            // Grab parameters using jQuery's data() method
            var params = $(this).data("popup") || {};            

            // Use the target attribute as the window name
            if ($(this).attr("target"))
            {
                params.windowName = $(this).attr("target");
            }

            // Pop up the window
            var windowObject = UTIL.popup.open(this.href, params);

            // Save the window object for other code to use
            $(this).data("windowObject", windowObject);
        });
    });
});

One of the great features of jQuery that we utilize here is the data method.  This allows us to attach data to DOM elements without corrupting the HTML with non-standard attributes or tags.  Using jQuery’s ability to locate DOM elements using CSS selectors, we can bind the custom configuration object (used in our new popup function) to the links themselves.  Then, when a link is clicked, it can read it’s popup configuration and pass it to the popup window function.  In this way, we can keep our HTML standards compliant and completely separate from our JavaScript code.

Putting it all together, we can create an HTML page like this:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
      <title>Popup Window Test</title>
      <script type="text/javascript" src="jquery-1.2.6.pack.js"></script>
      <script type="text/javascript" src="utils.js"></script>
   </head>
   <body>
      <ul>
         <li>
            This link uses defaults:
            <a href="http://google.com" target="google" class="popup">Google.com</a>
         </li>
         <li>
            This link uses custom parameters:
            <a id="custom-popup" href="http://yahoo.com" target="yahoo" class="popup">Yahoo.com</a>
         </li>
      </ul>
      <script type="text/javascript">

         // Add custom pop-up properties to the second link
         $("a#custom-popup").data("popup", {width:400,height:400, top:200, left:200});

      </script>
   </body>
</html>

If you would like to download this example, you can get it here (jQuery not included for legal reasons).

Ever get that annoying “Click to activate and use this control” message in IE when you embed a Flash movie? I did and couldn’t find a solution that worked as well as I’d like. A quick browse of my Google results brought be here, but the solution there was kinda sloppy in my opinion. But with a few tweaks, I got things working…and with the added street cred’ of not introducing a single global variable.

Here’s the code:

(function()
{
    var clear = function(a)
    {
        for(var i=0; i < a.length; i++)
        {
            a[i].outerHTML=a[i].outerHTML;
        }
    };

    clear(document.getElementsByTagName("object"));
    clear(document.getElementsByTagName("embed"));
})();

The original code I found only applied the fix to object tags. But since use of the object tag is becoming deprecated, we also need to touch the embed tags as well. The code above does both and is wrapped in an anonymous function to keep all is private bits private.

Finally, since this is an IE-only fix, we should make use of IE’s conditional comments so other browsers don’t have to be bothered by the script. Assuming we placed (a minified version of) the code into a file named controlFix.js, we then add the following code at the end of the source code:

<!--[if IE]>
<script type="text/javascript" src="controlFix.js"></script>
<![endif]-->

If you’d like to use this on your site, you can download the minified JavaScript here.

On of the best tools around for helping you speed up your page load times is Yahoo!’s YSlow Firefox extension. I’ve been using it for a while and found it has some interesting insights on page load performance. Its recommendations come from Yahoo!’s “Exceptional Performance Team” 14 recommendations for speeding up page load times. If you’d rather hear it right from the horses mouth, have at, otherwise you can watch the conveniently embedded video below.

Briefly, the 14 recommendations are:

  1. Make fewer HTTP requests
  2. Use a CDN (like Akamai)
  3. Add an “Expires” header
  4. Use GZIP compression
  5. Put CSS at the top
  6. Put JavaScript (JS) at the bottom
  7. Avoid CSS expressions
  8. Make JS and CSS external
  9. Reduce DNS lookups
  10. Minify JS
  11. Avoid redirects
  12. Remove duplicate scripts
  13. Configure ETags
  14. Make AJAX cacheable

Since the video can probably explain each of these points better than I can, I’ll let them speak for themselves for the most part. Lately though, I have been thinking a lot about two of them, namely #1 and #10: Make fewer HTTP requests and Minify JavaScript. Bit by bit, I’ve been trying to get all our JavaScript files at work “minified” but, it’s an uphill battle with so many files being edited semi-frequently by people other than myself. Since minified JavaScript is almost impossible to read without getting a headache, you’d have to keep the un-minified versions on tap somewhere and leave step by step instructions on how to minify them and then get them on the site. Having people then copy and paste the minified versions into a single aggregate file is simply asking for trouble.

Then I found a key part of the solution: a port of Crockford’s JSMin to PHP. This PHP version, allows you to do something very powerful that the original JavaScript version did not. Now you can use aggregate and minify all your JavaScript into a single HTTP request by calling a PHP script in your script tag which gathers and minifies all the JavaScript for the page.

So, say you wanted to aggregate and minify the JavaScript for your menu and rich text editor of choice. In your HTML, you might use a script tag like this:

<script type="text/javascript" src="/jsaggmin.php?features=menu/richtexteditor"></script>

On the server, jsaggmin.php would do look something like this:

require "jsmin.php";

foreach (explode("/", $_GET['features']) as $feature)
{

    switch ($feature)
    {
        case "menu":
            echo JSMin::minify(file_get_contents("menu.js"));
            break;

        case "richtexteditor":
            echo JSMin::minify(file_get_contents("rte.js"));
            break;
    }

}

Granted, this isn’t the complete solution, but it’s a leap in the right direction. What is needed next (in addition to a caching mechanism) is a way to run the equivalent of JSLint on the server before the files are minified. As a rule of thumb, if a JavaScript file can’t pass JSLint, it will blow up when minified. So the PHP script would need to verify the JavaScript, and echo it as is if it fails to validate. Otherwise, you’ve just minified and aggregated all the scripts on your page and can focus on the other 12 recommendations.

© 2008 Donald J Sipe | Powered by WordPress | RSS Feed