Using .each() and contexts

One really cool usage of .each is using it to verify existence of a block of html on the page. If you are including all your site's javascript on every page or have DOM blocks that are conditionally included, then using this technique can help to improve performance significantly and compartmentalize your code.

This is probably how you typically see jQuery code written:

$(function() {

  $("#my_block select").change(function() {
    ...
  });

  $("#my_block a:contains('edit')".click(function() {
    ...
  });
});

Both of those selectors are invoked on every page load regardless of whether #my_block even exists. Also, jQuery can't use getElementById to look up #my_block because the selectors are combined with other criteria.

Instead of repeating the selector in multiple calls, we can use .each to guard the entire section from running only if the block actually exists.

$(function() {
  $("#my_block").each(function() {
    $("#my_block select").change(function() {
      ...
    });

    $("#my_block a:contains('edit')").click(function() {
      ...
    });
  });
});

In this example, we are now able to use the id of the block by itself, which allows jQuery to use the browser's built in and very fast getElementById function to look up the block. It also prevents any of the other selectors from running if the block does not exist.

Writing it this way also gives us another benefit. We now have the raw DOM object of the block available to use as a context for other searches. In case you aren't familiar with contexts in jQuery, it allows you to pass in a raw DOM object (not a jQuery object!) to use as the root node for the search. Typically when you run a selector, it searches the entire document for matches. Providing a context will restrict the search to the node you pass in. Be careful though, because if the context doesn't exist or is null then it will search the entire document tree.

Contexts work well with .each because we are guaranteed that it will actually exist. If nothing matches the selector, then the anonymous function inside will not fire. Putting it all together would look something like this.

$(function() {
  $("#my_block").each(function() {
    var my_block = this;

    $("select", my_block).change(function() {
      ...
    });

    $("a:contains('edit')", my_block).click(function() {
      ...
    });
  });
});

Once more nice thing about this is that it leaves you poised to move the code into an application specific jQuery plugin. You can basically take the content of it out and call it on your block.

(function($) {
  $.fn.extend({
    myBlockBehavior: function() {
      return this.each(function() {
        var my_block = this;

        $("select", my_block).change(function() {
          ...
        });

        $("a:contains('edit')", my_block).click(function() {
          ...
        });
      });
    }
  });
})(jQuery);

$(function() {
  $("#my_block").myBlockBehavior();
});

And now you have some clean, fast, encapsulated code that you can easily reuse.

jQuery SpicySelect v1.2

I just released version 1.2 of SpicySelect. The changes include:

- Made the text in the select not highlightable. You can't select the text in a regular select box, so the spicy select should not be selectable either.
- Added mouseover support to the "current" class that gets applied to denote the active selection. When a user expands the select, the "current" class will be applied whenever an option is moused over or navigated to with the keyboard. Please use that for styling instead of ":hover".
- Added configurable label markup. You can now specify what markup will be placed at the top of the select container to hold the text for the selected option.

Project Home: http://github.com/paulelliott/jquery-spicyselect

Demo: http://codingfrontier.heroku.com/jquery-spicyselect/demo/demo.html

These features and fixes were all in response to user feedback. If you are using the plugin and encounter any problems, please shoot me an email and let me know!

Using Uploadify and CarrierWave

Using Uploadify and CarrierWave together is a beautiful combination. You can easily leverage CarrierWave's image caching system to store the inline uploads from Uploadify.

The focus of this post is on the CarrierWave side, so we will ignore the Uploadify settings. They are just the standard ones anyway. I am also going to skip all the stuff you need to do to get the authenticity tokens working. If you need to set that up, see this post.

To get started, as always, we will need an uploader. This is the basic one we will use for this example.
class PhotoUploader < CarrierWave::Uploader::Base

  include CarrierWave::MiniMagick
  storage :s3

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end

  process :resize_to_fit => [200, 200]

  version :thumb do
    process :resize_to_fill => [60, 60]
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

We then need to attach our uploader to an object. In our example we use a Photo model with the uploader mounted on "image", but you can attach it to any model that is part of your form.
#migration
create_table :photos do |t|
  t.string :image
  t.timestamps
end

#model
class Photo < ActiveRecord::Base
  mount_uploader :image, PhotoUploader
end

Now we need a simple controller action to receive the post from Uploadify.
class PhotosController < ApplicationController
  skip_before_filter :verify_authenticity_token

  def create
    photo = Photo.new(params[:photo])
    render :partial => 'photo', :locals => { :photo => photo }
  end
end

This is where the real magic happens. We are going to create the Photo object so CarrierWave will store the upload in its temporary folder, but we won't save it. That is because we don't want to actually persist the object to the database until the full form is posted. Instead, we will return the cache location of the saved file.

You can return it any way you want: text, json, partial, whatever. The important thing is to send back the "image_cache" location so that it can be plugged into an input and posted up with the real form via the "onComplete" hook in uploadify.

Then when you make the real form post it will attach the temporary file. You can also use this method for multiple file uploads.

The Final Countdown

Tonight I released another jQuery plugin call The Final Countdown. It is a very simple to use countdown timer and it is, indeed, the last countdown timer you will ever need. To use it, you just select the DOM element that will contain the timer and call createTimer(). You can then start and stop the timer by calling startTimer() and pauseTimer() on the same element. It has all the essential options including robust date output format support and callbacks for clock ticks and timer completion.

You can find all the details on the github page at http://github.com/paulelliott/jquery-the_final_countdown

CarrierWave on Heroku

As the new hotness in file attachments, CarrierWave (http://github.com/jnicklas/carrierwave) provides a really slick and easy way to attach files to your models.  It presents an issue if you run on Heroku, however, since CarrierWave stores temporary files in public/uploads/tmp by default.  It uses these temp files when the form is redisplayed so the uploads are not lost.  It is a cool feature, but a hassle if you are on Heroku since those folders are not writable.

Heroku does provide a place for you to keep temporary files though.  You can configure CarrierWave to use a custom cache dir by adding to your uploader:

def cache_dir
  "#{RAILS_ROOT}/tmp/uploads"
end

 

CarrierWave will then use one of the temp folders provided by Heroku and you will be able to use the temporary files in future requests.  We still have the problem of being able to display the images when the form is redisplayed though.  It is a pretty awesome feature that we don't want to miss out on and luckily we don't have to.

To be able to serve up the temporary images from the tmp folder, you need to make an additional action on one of your controllers that will take the cache location as a parameter and return the image.  

Put this in the resource's controller to respond with the image:

def image_cache
  headers['Cache-Control'] = 'public; max-age=600' # cache image for 10 minutes
  send_file "#{RAILS_ROOT}/tmp/uploads/#{params['cache_id']}/#{params['filename']}", :disposition => 'inline', :type => "image/png"
end

Put this in your routes file (where resource is the controller your new action is in):

map.resource_image_cache '/resources/image_cache', :controller => 'resources', :action => 'image_cache', :requirements => { :cache_id => /\d{8}-\d{4}-\d{5}-\d{4}/, :filename => /[a-zA-Z0-9_ ]+\.(jpg|jpeg|png|gif){1}/i }

And reference it like this in your view (where avatar is the field you mounted the uploader on):

= image_tag(resource_image_cache_path(:cache_id => resource.avatar_cache[/^[\d-]+/], :filename => resource.avatar_cache[/[a-zA-Z0-9 _\.]+$/])) if resource.avatar_cache

Special thanks to fellow Rocketeer Shay ( https://twitter.com/shayarnett ) for his input on this post.

jQuery SpicySelect 1.1.1

One more small update to the script this evening. The version 1.1 series includes the feature set providing keyboard support to the select. The minor revision deployed tonight added the ability to start typing the name of an option to select it. It performs a case insensitive search on the options in the list and matches to the first one that contains the string the user typed. Pressing backspace or delete will cause it to clear out the cache so you can type a different selection. I know that different browsers work slightly differently in this regard, but this is similar to how Safari works and seemed sensible to me.

Also to note is that the source code in the repo is always the latest and greatest revision, but I package release milestones for quick download from the Downloads tab on the github project page. You can get either the raw or minified version there.

http://github.com/paulelliott/jquery-spicyselect

jQuery SpicySelect v1.1

I just pushed some changes to spicyselect up to github.  This includes some really cool fixes and enhancements to the original script.

  • The mask is now in the tab order
  • Upon receiving focus, it will have a "focus" class added so you can style accordingly
  • While focused, pressing the down arrow will expand the options
  • With the options expanded, the up and down arrows will move the selected option up and down through the list
  • When selecting with the keyboard, a class of "current" will be added to the current selection for easy styling
  • With the options expanded, pressing enter or space will choose the selected option
Now the mask will act much more similarly to how an actual select box acts.  Next up is to wire up the rest of the keys to select options by typing a name.

I also updated the demo a bit.  It now has some basic styles and I provided a quick link to download the stylesheet as an example.

As always, ping me with any bugs, requests, or feedback!

Same Domain, New Platform

I have been hosting my own Wordpress blog for a while now and have gotten a little tired of having to manage it. It offers great flexibility, but also requires me to keep up with updates of the software and the server it runs on. It really isn't something I want to do, given that there are some really good hosted solutions out there.

As a whole I am getting away from wanting to host run my own VPS and getting into managed hosts instead. Good solutions exist for the things I need to do and letting someone else who is an expert on system administration deal with that gives me more time to do the things that are important to me, namely writing code.

So, here I am running on Posterous. So far it has been a good experience. The migration from Wordpress was very simple and making posts via email is actually pretty handy. It is also a Rails application, which of course holds a special place in my heart.

If you were subscribing to me via RSS, then be advised that the feed path has changed.

Concise Syntax for Partials

Typically to render a partial in rails you would type:

render :partial => 'my_partial', :locals => {:some => @thing, :else => @goes_here} 

I recently found that you can now simply type it like this:

render 'my_partial', { :some => @thing, :else => @goes_here }

It isn't in the documentation as far as I can tell, but works like a champ and is nice and concise.

Introducing jQuery SpicySelect

I recently worked on a project in which the client wanted to have custom styled select boxes. Long story short, I found an obscure jQuery plugin that claimed to do it but was not compatible with Rails at all and was missing some functionality, like the support of option groups, and did not perform well on large lists. Needless to say, I completely reengineered the plugin and re-released it on github under a different name, SpicySelect. To use it, all you need to do is call on a normal select written out by your app:



$("select").spicyselect();


The plugin will hide your select and write out a DOM structure of ol/li elements wrapped in a div. It will carry over any classes you had on your select or options and wire up the events so that the mask works similarly to the underlying select. Any time the mask's selected option is changed, the underlying select's option is also changed and the change event is fired. That allows you to write code keying off the select's change event like you normally would and the mask will not interfere.

I put a demo up, although it has not been styled yet. I am hoping to get that done over the course of the next week or so. I also published a quick link to the spec.

Pertinent URLs:

github Project Page

Demo

Spec

I hope you find the plugin useful. If you have any comments, issues, feature requests, bugs, etc, please don't hesitate to email me!