Mark RushakoffMark Rushakoff
Unwanted whitespace between elements
edit Posted by Mark Rushakoff on Saturday January 21, 2012 at 09:06AM

We recently came across a situation in our markup where we wanted whitespace in the markup for readability, but we didn't want that whitespace represented between the elements.

We found a fix that suggested using font-size: 0 in CSS to eliminate the whitespace. That worked fine in Chrome, but we found that in Firefox, the containing element no longer scrolled with the mouse wheel or arrow keys! Apparently Firefox's scroll speed is proportional to font-size.

Serving up different sized dynamic images based on device resolutions.

Jason and I were working on an app where we needed to render dynamic images of several different sizes for different mobile devices, depending on device resolution.

Our scenario was analogous to the following:

Let's say you are an engineer for Twitter and you need to display a user's avatar on their show page, but you need to serve a small, medium, or large image based on the resolution of the device accessing the page. This is a potential candidate for CSS media queries, except that the image url needs to be obtained from our User object (in Ruby). Let's cross that bridge when we get there and spike on an initial proof-of-concept.

users#show view
<h2><%= @user.name %></h2>
<div class="avatar"></div>
# ...
stylesheet
/* Default - screens over 767px wide */
.avatar {
  /* ... */
  width: 220px;
  height: 220px;
  background: url('/assets/dfp/large.jpg');
}

/* Mobile Landscape */
@media only screen and (min-width: 480px) and (max-width: 767px) {
  .avatar {
    /* ... */
    width: 140px;
    height: 197px;
    background: url('/assets/dfp/medium.jpg');
  }
}

/* Mobile portrait */
@media only screen and (max-width: 479px) {
  .avatar {
    /* ... */
    width: 60px;
    height: 85px;
    background: url('/assets/dfp/small.jpg');
  }
}

Pretty straightforward stuff, especially if you've followed the Responsive Web Design (CSS media queries) trend. The interesting thing to note here is that when putting the background image in the stylesheet, the asset does not get requested until you resize your browser to the size that uses that image. Go ahead and load this page in a web browser, open up the network panel, resize the display, and watch the asset size change and different requests getting fired off. That's exactly what we want!

Okay, great. But, although it would be glorious, not everyone is going to have Dog Fanny Pack for their avatar. We need to call some ruby method on some ruby object to get the image (e.g. user.avatar.path). Not really something you can do from within the stylesheet. Alternatively, you wouldn't want to move the background image reference to the view because then it gets loaded as soon as the page loads, regardless of the screen resolution.

To make this puppy (pun intended) dynamic, we create a little proxy. See below:

Routes
ResponsiveUserAvatar::Application.routes.draw do
  # ...
  match "users/avatars/:size.jpg" => "users#avatar"
end
Users controller
def avatar
  user = get_user
  size = params[:size]
  if user.present?
    send_file "#{Rails.root}/#{user.avatar.path(size)}"
  else
    render :status => :not_found, :text => "not found"
  end
end

We create an action, called avatar, on our users controller (you also create a separate avatars controller) that takes a size parameter. The avatar action first looks up the user (You don't get to see how 'till later), then it grabs the size from the url and renders the user's avatar for that size.

Quick aside

You may notice this action isn't really on the member (i.e. no user id), even though it should be. We'll get to that in a second. You may also be thinking of alternatives to using send_file. Jason and I experimented with redirects, but ran into caching issues. I am open to suggestions on these topics, so feel free to post feedback in the comments or submit a pull request to the demo app.

Back to business

Now all we have to do is call our proxy image/action in the stylesheet, see below:

users.css
/* Default - screens over 767px wide */
.avatar {
  /* ... */
  background: url('/users/avatars/large.jpg');
}

/* Mobile Landscape */
@media only screen and (min-width: 480px) and (max-width: 767px) {
  .avatar {
    /* ... */
    background: url('/users/avatars/medium.jpg');
  }
}

/* Mobile portrait */
@media only screen and (max-width: 479px) {
  .avatar {
    /* ... */
    background: url('/users/avatars/small.jpg');
  }
}

Coming clean...

So, I left out one minor detail... Okay major detail... It's time to show the implementation of the "get_user" method.

users_controller
class UsersController < ApplicationController
  # ...
  private

  def get_user
    if request.env["HTTP_REFERER"].present?
      user_id = request.env["HTTP_REFERER"].match(/\/(\d+)\/?/)[1]
      user = User.find_by_id(user_id)
    end
  end
end

In our case we always had the user id in the page that used the avatar. If we tried to implement this on the listing page... Well it wouldn't be pretty.

While this isn't a silver bullet solution, it's a cool concept and can be applied to many different applications. I call it the "responsive-css-background-fake-image-proxy" pattern. Okay, maybe not, but to recap, the main components are:

  1. Use CSS media queries to target different device resolutions.
  2. Reference the background-image proxy url in the stylesheet, which only loads the image size needed.
  3. Use a fake image proxy to get your dynamic image.

Demo app and demo app code

Ask for Help

"We keep getting webrat thread exceptions running our integration specs with the rails integration runner: Thread tried to join itself. The error message varies with different versions of ruby 1.8.6 vs 1.8.7."

Anyone had this problem or know why?

"How do I skin an iphone mobile site to be the correct width so it's not 980px wide?"

<meta name="viewport" content="width = device width" />

*"We're trying to deploy some nginx configuration changes to EngineYard Cloud, what's the right way to do that?"

We've tried building custom chef recipes to solve this problem, but they run after nginx has already restarted, so are a poor solution to this problem. The better solution might be to check in configuration files into the application and symlink them into the nginx configuration directory using a before_symlink.rb hook in the /deploy directory.

*"We've got a has_many association where some of the child records are originally saved in an invalid state. When we later load the parent and ask it if it's valid, it returns true even with validates_associated. How can we get the desired validation behavior?"

Turns out that unloaded associations are not validated. Solution: load the association before calling .valid? on the parent. In general, you should also not create invalid objects, instead using a state variable to put them into a "draft" or "incomplete" state where they are still valid but not complete. Then remove that state and you'll see the errors required to finish that object.

Interesting Things

  • When RubyMine 2.0.1 won't run your focused specs, try attaching rspec 1.2.9 to it rather than 1.3.x. It fixed this issue for one of our teams.
  • Rubymine 2.0.2 came out today: can finally run focused contexts?! Also including bundler support! What's new
  • We tried our Unicorn on EngineYard cloud: so far so good. It's still "experimental" but seems to work.

Ask for Help

"How do I redefine a CSS class in javascript"

You can create a new <style> element and append it to the head. This should probably be avoided if you can help it.

If you simply want to toggle between 2 states, consider putting both sets of rules in the CSS and toggling a class on the body or other container.

*"Should I use BOSH for XMPP on the iPhone?"

Probably not. If you have a long-running low latency XMPP connection, you'll probably want to use a socket from the CFNetwork package. That's the most we know about iPhone development right now.

Interesting Things

  • New tracker updates with better burndown charting!

Abhijit HiremagalurAbhijit Hiremagalur
Standup 01/16/2009: onReady() for AJAX, Web Sprites & Detecting UTF-8
edit Posted by Abhijit Hiremagalur on Monday January 19, 2009 at 10:09PM

Interesting Things

  • Web based sprite generator - here

This also makes the generated sprite really small which is great if you care about page load times. A Ruby+ImageMagick sprite generator might also be a good thing to build.

  • Cool way of detecting if a file is UTF-8 enconded using Ruby+IConv - here

Ask for Help

"Is there an onReady() for AJAX events?"

Jim KingdonJim Kingdon
New York Standup 10/3/2008
edit Posted by Jim Kingdon on Friday October 03, 2008 at 01:29PM
  • Our site has a number of buttons which are: (a) really links which look like buttons instead of links, (b) ajaxy actions, which typically cause an edit box to appear, or (c) form submissions. This item is especially about (a) and perhaps (b). Right now we have what is a glorified styled a tag, as described here. We've been having trouble getting that to work right (in particular, the height seems off by one pixel in mysterious circumstances). Probably the easiest solution is the rails button_to helper, which makes a form with one button in it (and method=get). This loses some desirable behaviors of the a tag (such as being able to control click or menu-click to open in a new tab), but certainly solves the rendering hassles.

Ryan DyRyan Dy
Standup 03/6/2008
edit Posted by Ryan Dy on Thursday March 06, 2008 at 05:17PM

There is a firebug that works with Firefox 3, it can be found at GetFirebug.com. The current beta version 1.1 appears to fix a problem that Firebug was having with inspecting anchor elements.

Also, there is a google code project ie7-js. It is a Javascript library that attempts to make IE7 behave more like a standards-compliant browser. It includes a limited PNG transparency fix. It also gives you Pseudo-element like :before and :after.

Combining this with yui and IE7 gets a lot closer to behaving as you might expect!