Archive for November, 2008

min-width and max-width with Javascript (IE Hack)

Wednesday, November 5th, 2008

So, recently some of the pages I wrote require me to apply certain CSS feature not supported by IE6. The max-width and min-width. They are what they say, provide a minimum and maximum width requirement for a box element. They work like a charm in Firefox, Webkit, and Chrome, just not IE.

Well, readers who are familiar with cross-browser (in)compatibility problems probably would have encountered similar situations when features work differently in different browser. Short hacking and Google search usually helps a lot. Not in this case. Most pages (well, I only went through the top 20-40 pages in Google search, with several combination of keywords) mention the pretty standard CSS expression hacks, like this one. It’s not good enough. CSS expressions are slow. Intuitively, it should run on every resize. But that’s not the case. Steve Souders research into CSS expressions are still fresh in my mind. Expressions executes on every page resize and mouse move events1! Not good!! Definitely not good!

The next obvious move is to use Javascript and register your own handler. Unfortunately, either the CSS expression hacks are too popular or nobody publishes Javascript code that does this, I could find no results in Google. So I didn’t waste any more time and roll out my own hacks. The hacks is darn simple (okay, not darn simple, but not too difficult either). The idea is to enclose the div where you want to set your max-/min-width with a wrapper div that should have a width of 100%. This wrapper div will take the form of the enclosed div should you not place max-/min-width. Subsequently, register a listener to window’s resize that check whether this wrapper div’s width exceeds the max-width or decreases below the min-width and then set the width of the enclosed div appropriately. The crucial part of the code is the following (I simplify it to register directly on window.onresize, which is obviously a bad code, but illustrates the concept well).

// This one for max-width
window.onresize = function() { =
      wrapperDiv.clientWidth > maxWidth + 1 ?
          maxWidth + 'px' : 'auto';

// And this one for min-width
window.onresize = function() { =
      wrapperDiv.clientWidth < minWidth + 1 ?
          minWidth + 'px' : 'auto';

Replace enclosedDiv and wrapperDiv by the appropriate elements as mentioned in earlier paragraph. Btw, You only need to do this in IE. In other browsers, just set the CSS properties appropriately.

Notice the maxWidth + 1 and minWidth + 1? Without the + 1, IE will lock because of race condition. (This page also mentioned this problem, so it seems like a common problem, and not because of my incompetence.)

Right now I’m writing a library that is specialized for this problem. Look forward for it soon. The libraries will handle multiple resize handlers graciously (well, technically, you only need 1 resize handler that resize all the divs in the page in the correct order; and you define the order). The library should also handle creation of the wrapper for IE automatically. In fact, 90% of the library is done, but I’m not confident of releasing it because I haven’t actually tested it at all! I’m going to replace my hacks with the library, if it works fine after we test it, it’ll show up in this blog.

Javascript is fun (yes, cross-browser compatibility is painful, but isn’t it the thing that makes Javascript colorful?)!

  1. []

Obama for ‘08!

Monday, November 3rd, 2008

It’s the day ya know… Tomorrow is the day for change. And Obama is change. I’m not into politics (or economy), but I’ve read enough newspaper articles supporting and admonishing both side of the election war. I must say, my mind has logically picked Obama.

And, obviously, I’m really not that convincing. So I’m just going to point out one reading I read today that was funny and true at the same time: here. It has one of those moments when politics got fun:

Finally, it’s important to me that the President of the United States is smarter than me. And if the president dies, on account of being old, I expect the Vice President to be smarter than me too. I want a President who understands plumbers, but I don’t want a plumber as Secretary of State.

—Stephen Elliott, The Huffington Post

Optimize for the most common use case

Sunday, November 2nd, 2008

This is the best advice I’ve ever stick myself with when writing codes. Before delving deep into this, let’s take a look of one of my Scheme peeve (I was reminded of this when reading eric’s post in LispCast): the unavailability of length=.

Let’s write a simple Scheme code:

(define (two-len-list list)
  (= 2 (length list)))

What’s so wrong about this code? Well, a hint, a Scheme list is basically a linked-list. Got it? Yes! length will recurse through the entire list. The order of growth is O(n), where n is the length of the list for the above example. As suggested by eric in LispCast (his focus in the article above is that LISP should automatically optimized the above code to look like the code below, but that’s another matter), this should be easily optimized to:

(define (two-len-list list)
  (length= 2 list))

Apparently, that neat procedure is defined in LISP. In Scheme? AFAICT (Scheme is just a small hobby, I might have missed the procedure that does this), it’s not defined. Well, no matter, we can always write it ourselves.

(define (length= n lis)
  (cond ((= n 0) (empty? lis))
        ((empty? lis) false)
        (else (length= (- n 1) (cdr lis)))))

Neat! And it will only recurse n times at most. Used in two-len-list, it will make the procedure O(1). (Another thing that a lot of new Schemers do, including me during my first day learning about list, is to write this: (= 0 (length lis)), where it could be written as (empty? lis)).

Now, we come at the best part: optimize for the most common use case. Let’s use the same example, shall we? What if you know that in your code, the length of the list matters! Let’s say that in 70% of the call, the length of the list is required. Then make sure you keep the length of the list at all time, instead of recomputing it every time you need it. It used to be hard to do this when list was mutable, but with the advent of R6RS, list is now immutable, this becomes relatively straightforward. You can even abstract it into a new list-based data-structure.

(define (make-sclist lis)
  (list 'sclist (length lis) lis))
(define (is-sclist? sclist)
  (eq? 'sclist (car sclist)))
(define (sclist-length sclist)
  (if (not (is-sclist? sclist))
      (error "Not a self-counting list")
      (cadr sclist)))
(define (sclist-car sclist)
  (if (not (is-sclist? sclist))
      (error "Not a self-counting list")
      (car (get-plainlist sclist))))
(define (sclist-cdr sclist)
  (if (not (is-sclist? sclist))
      (error "Not a self-counting list")
      (list 'sclist
            (- (sclist-length sclist) 1)
            (cdr (get-plainlist sclist)))))
(define (get-plainlist sclist)
  (if (not (is-sclist? sclist))
      (error "Not a self-counting list")
      (caddr sclist)))
(define (sclist-cons new-member sclist)
  (if (not (is-sclist? sclist))
      (error "Not a self-counting list")
      (list 'sclist
            (+ 1 (sclist-length sclist))
            (cons new-member
                  (get-plainlist sclist)))))

When writing the above code, I realize how much I have coded in object-oriented paradigms. I kept wanting to write the above code in a message-passing style and encapsulated the length and the list in a let (similar to a private environment). At the end, I went for tag-based approach to increase the readability. You’ll also see the increase level of abstraction in the code.

Optimize for the most common use case is a very powerful advice. It allows you to focus your thoughts and willpower at that algorithms that will get accessed the most number of time, thus whatever small improvements you make there, it will be multiplied by the number of calls!

This does not only apply to programming. Take a look at database too. If you’re writing a blog, what’s the most common use case? Getting the last n posts, getting a posts between this date and that date, getting posts tagged with some tags, and searching. So optimize for them! Index the posts by date and tags. Try pre-scoring each posts to keywords (possibly, a keyword can be any word that is being mentioned in the post) and store them to speed up searching, at the expensive of higher storage.

On writing a webapps, make sure the most commonly accessed pages are the fastest. What is the point of optimizing those 10 pages that got accessed 3% of the time while another 5 pages were accessed 97% of the time (there is a point, but only after you optimize the 5 pages)? (Sometime it’s not easy to determine which pages will get accessed the most, but once you launched the webapps, you’d better be tracking the usage and allocate resources to those highly accessed pages.)

There is one place where it’s a little contradictory to follow this advice. Say in a webapps, you have the above scenario where 5 pages are being accessed 97% of the time. You would want links to these 5 pages in obvious places since users are very likely to want to go to these pages. At the same time, you may also want to publicize this really cool feature on the 6th page. So you also want to place a link to this page in obvious place. Well, all right, it’s not that contradictory, but in this case, you should optimize your screen real estate for both. If you opt for sidebar approach, it’s not too hard, a top navigation may be slightly more mischievous to deal with. Good luck!

Reduce # of HTTP requests

Saturday, November 1st, 2008

I think I’ve become more absent-minded recently. Several days ago I received an Amazon package at home. I left it lying around and unopened until this morning. I remembered that I ordered a book that I’ve coveted for several months and that the book is in that package. Lol.

So I spent this morning reading Rule 1 fromĀ High Performance Web Sites (that’s the book that I received in the package). It’s interesting really, the design of this website tries to follow that rule pretty closely. That is, it tries to reduce the number of HTTP requests being sent to the server. HTTP requests add a burden to the server and to your web browsers. It gets slower; much slower. Of course Firefox users would be very familiar with tabs auto-restoring during crashes/restarts. At the point of auto-restoring, it is not uncommon for me to load 50 tabs at the same time. If each site has 20 HTTP requests in average (Blimey! That many? You’ll be surprised, some website have over a hundred requests; heck, even my faculty website has over 40 HTTP requests.), the auto-restore can easily amassed 1000 requests. Firefox, by default, limits concurrent HTTP requests to about 30 (check out your about:config, search for network.http.max-connections). Older Firefox default to 2 concurrent connection per server. Ouch!

I’m hoping that this blog will not become a beast in term of HTTP requests. The initial design (from default Wordpress 2.6.3) contains over 10 requests. I’ve redesigned the page substantially and reduces the amount of requests by quite a bit. Even after adding line-numbering (a vertical image strip) and Google Analytics (a whopping 2 requests, 1 for the javascript file, another for the actual tracking, which uses a heavily parameterized GET request for a 1×1 gif image), this site has a manageable 6 requests. I plan to add a Javascript library for some of the additional stuffs I plan to do. But I’ll try to do that while reducing other requests. My first project is to replace that image for line numbers with a Javascript. So the Javascript library will take the place of the image download.

My next game will involve playing around with the cache settings, probably even installing the notoriously problematic plugin, WP Super Cache.

Readers who are planning to create a website of their own may also want to consider sticking with HTML standards instead of XHTML standards. That will save you tonnes of closing tags. The w3cschool HTML tag reference pages will indicate whether the closing tag is optional for HTML. I have no intention to switch to HTML for this blog though. As long as I don’t get digged or slashdotted, I don’t expect amazingly high amount of traffic… Heck, even 100 readers a day will be pretty amazing! Saving bandwidth is not my concern right now.

Anyway, for more of interesting stuffs and analysis on how to make your frontend better, be it website or webapps, try reading Steve Souders’ website.