Sep 9, 2011

Recent Facebook update causes your like button to appear TWICE!

I was shocked when one of my clients sent me a screen shot showing TWO facebook like buttons on his website - one under the other. I went in circles for a couple minutes trying to see if maybe something was happening twice in JavaScript when I found this article:

Here's what my code looked like:

// append like button
fbLike = $('

Apparently, this recent FB update conflicts with the class name 'fb-like' ANYWHERE in your DOM. All I had to do was rename my div wrapper from fb-like to fblike and everything was fixed.

How odd is that!

May 26, 2011

Fancy UL/LI bullets

I've always been frustrated at the rather useless selection of bullet options for LI lists in CSS. So I decided to do something about it.

I developed a simple set of transparent PNGs and a set of CSS classes to boot. Here's the result: (the below image was generated using only ul/li code)

Download the bullet images here.

Then try this CSS: (be sure to update the image references accordingly)

ul.check li, li.check { list-style-image:url('li.check.png'); }
ul.x li, li.x { list-style-image:url('li.x.png'); } li, { list-style-image:url(''); } li, { list-style-image:url(''); } li, { list-style-image:url(''); } li, { list-style-image:url(''); } li, { list-style-image:url(''); }
ul.minus li, li.minus { list-style-image:url('li.minus.png'); }

May 6, 2011

The Secret to Beautifully Inset/Embossed Text

White drop shadows.

This simple technique has totally transformed my designs in the last couple weeks. My buttons have more dimension. My text has more impact. I feel so Web 3.0.

Try it in your favorite image processor... or even better, IN CSS!

text-shadow:1px 1px 0px #fff;
filter:DropShadow(Color=#ffffff, OffX=1, OffY=1); /* FOR IE */

Apr 26, 2011

Connect Multiple Browsers using JavaScript

When searching for answers to development issues, I often land on Ben Nadel's Blog.

I landed there today and subsequently stumbled upon the most glorious JavaScript post I've ever seen in my entire life.

Ben demonstrates how to communicate with multiple client browsers using JAVASCRIPT! That's right. Imagine you and your friend have the same website pulled up. Then imagine you click on text field and started typing... and watching your friend's browser update INSTANTLY. No joke.

Ben shows us more here:

Thanks, Ben!

Apr 20, 2011

Dynamic Image Placeholders (or what I like to call 'Image Ipsum')

Ever struggle to find a generic image placeholder of a web design during development?

Two fantastic sites you need to checkout:

Both services allow you to paste a URI like "" into your HTML and get back a random JPEG. In the above URI, 980 is width, 220 is height and 'balls' is a search parameter.

One suggestion for - rename your website to ;)

Apr 14, 2011

Caching JSON Requests is a retail website I've been developing for a couple of months.  The latest version was designed specifically with speed in mind.  We want customers to be able to find exactly what they are looking for quickly and intuitively.

The site is being served from a shared environment.  While its very cost effective, the database performance is less than ideal.  We've done our best to keep db processing to a minimum.  But with every request for a product list comes yet another hit against the database... and they were sometimes very costly hits.

I'd eventually had it with the intermitent performance issues and said to myself - 'there's got to be a way to make these requests load faster for ALL visitors'.  I want peoples minds to be BLOWN away at how fast the web experience is.

Then it hit me... all I'm returning to the browser is a JSON string.  What if that JSON string was cached in 6hr intervals?  Then the product lists would load INSTANTLY... no waiting for db processing...  the server can respond as fast as possible with the appropriate JSON response.

My solution was build in ColdFusion (hangs head in embarrassment) but the same could be applied to any server language.

Here's how it works...

Here's my server directory structure:
   ajax/_cache  (this is a dir)

In each file that processes a request and returns JSON, I include _cache.cfm at the top of that file.  At the very bottom of each file, I also change my <cfoutput> or WriteOutput() to a custom function called CacheOutput(JSON_STRING_HERE).

Here's what _cache.cfm does. (remember - its being included before anything else)

  1. Scan the cache directory for files older than 6hrs and deletes them.
  2. Creates a unique identifier for the given request by lumping the current server file that's being processed with any GET or POST variables.
  3. It then compresses the potentially large unique identifier into an encrypted hash string. This new string becomes the file name for the cache content.
  4. Does a file already exist in the cache with that unique identifier?
    - if YES, serve that content up and ABORT page execution
    - if NO, let the current page on the server finish processing the request
  5. Lastly, it defines the function that will be used on the original page processing the request to output AND cache the result.

Here's the code:

<cfset __cacheMinutes = 60*6>

<!--- keep the cache files clean --->

<!--- get all files in the cache ----->
<cfdirectory directory="#ExpandPath('.\')#/_cache" name="qDir" sort="datelastmodified ASC" type="file" />

<!--- determine the expiry date --->
<cfset cacheExpiryDateTime = DateAdd('n',-__cacheMinutes,Now())>
<cfset timestr = '#DateFormat(cacheExpiryDateTime,"yyyy-mm-dd")# #TimeFormat(cacheExpiryDateTime,"HH:mm:ss")#'>

<!--- filter the files by the expiry date --->
<cfquery name="qDir_filtered" dbtype="query">
    FROM qDir
    WHERE datelastmodified < '#Evaluate("timestr")#' 

<!--- delete old files --->
<cfloop query="qDir_filtered">
 <cffile action="delete" file="#ExpandPath('.\')#/_cache/#name#" />

<!--- build a unique requestid --->

<!--- start the request id with the server page being executed --->
<cfset __requestID = '#CGI.PATH_INFO#'>

<!--- add url variables to the unique requestid --->
<cfset StructDelete(Url,'FieldNames')>
<cfloop collection="#Url#" item="i">
 <cfset __requestID = '#__requestID#_#i#-#Url[i]#'>

<!--- add form variables to the unique requestid --->
<cfset StructDelete(Form,'FieldNames')>
<cfloop collection="#Form#" item="i">
 <cfset __requestID = '#__requestID#_#i#-#Form[i]#'>

<!--- build a hashed filename ---->
<cfset __cacheID = Hash(__requestID)>
<cfset __cacheFile = '#ExpandPath('.\')#/_cache/#__cacheID#.bk'>

<!--- does this cache exist? ----->
<cfif FileExists(__cacheFile)>
 <cffile action="read" file="#__cacheFile#" variable="CachedContent" />

<!--- build the function that caches content ----->
<cffunction name="CacheOutput" output="yes">
 <cfargument name="OutputString">
    <!--- output the result as fast as possible --->
    <!--- write the output to the cache --->
    <cffile action="write" file="#__cacheFile#" nameconflict="overwrite" output="#OutputString#" />