jueves, 28 de noviembre de 2013

Internet Explorer Bomb

Today I deserve some chocolate.

If you are working with SASS you have probably a time bomb in your code base and you don't know.
I was totally happy on Monday when the customer told us that the application did't work in Internet Explorer.
We are working with Rails and Sass.

The problem seemed to be that the stylesheets weren't found.
Well, I have a Mac and I don't have Internet Explorer. So I used VirtualBox with the virtual machines that provides www.modern.ie to try to debug the issue.

I could see two things:
- The application worked in IE version >= 10
- In IE version < 10, the stylesheets were accessed (no error 404) but the site was showing as if there weren't any styles.

The designer gave us a hint: Perhaps we could use http://blesscss.com/.

What is it?

The problem appears here:
Microsoft's IE support documentation explains that in Internet Explorer 6-9:
  1. All style tags after the first 31 style tags are not applied.
  2. All style rules after the first 4,095 rules are not applied.
  3. On pages that uses the @import rule to continously import external style sheets that import other style sheets, style sheets that are more than three levels deep are ignored.

There are three possible solutions:

      1. Use Bless.
  • It splits a big css in various css. The first css make an "@import url" to the other css file.
  • You need Node to use Bless.
  • It isn't integrated in the assets pipeline of Rails.
      2. Use gem css_splitter
  • It is integrated in the assets pipeline of Rails.
      3. Split files manually.

For every solution, you need to know which css has more than 4095 rules. My question was, how do I know that?

First I obtained a list of css files with a command like that:

  find . -name "*css" > css.txt

Then I loaded the css_splitter gem in my project, and in "rails c" I defined the following method:

def count                                                  
  IO.foreach("css.txt") do |line|                          
    puts line.strip                                        
    puts CssSplitter::Splitter.count_selectors(line.strip!)
  end                                                      
end                                                        

Run the method and.....

The winners are!!!!
  • ./public/assets/application-c5f01626ddcd1f522b8a7f16cecf3355.css with 11,575 selectors
  • ./vendor/assets/stylesheets/libs/<util>/<util>.css with 9,759 selectors
The files in the project are structured like this:

app
  assets
    stylesheets
      application.css.scss
        @import "screen";
vendor
  assets
    stylesheets
      screen.css
        @import "libs/<util>/<util>";
      libs
        <util>
          <util>.css

The assets pipeline of Rails precompiles application.css.scss with all the @import and create a file with all the selectors:
./public/assets/application-c5f01626ddcd1f522b8a7f16cecf3355.css

But, which css should I split? The one in public/assets or <util>.css?

I tried to use the gem css_splitter but I wasn't able to make it work. So I used other approach.

I installed node and npm on my machine. I executed blessc -f <util>.css. This command overwrites the original file. At the end I had three files:


vendor
  assets
    stylesheets
      libs
        <util>
          <util>.css
            @import url('libs/<util>/<util>-blessed2.css');
            @import url('libs/<util>/<util>-blessed1.css');
          <util>-blessed1.css
          <util>-blessed2.css

Now the number of selectors were:
./public/assets/application-d618a047f651d19af56d0e3e9132fba3.css ------> 3,387
./vendor/assets/stylesheets/libs/<util>/<util>-blessed1.css -------------------> 4,095
./vendor/assets/stylesheets/libs/<util>/<util>-blessed2.css -------------------> 4,093
./vendor/assets/stylesheets/libs/<util>/<util>.css -------------------------------> 1,571

Ok, but where is the magic? The key is that inside <util>.css we are using @import url. We aren't composing a big file, the file is calling others files using the URL.

But, we have still a problem. Rails creates dynamic names for assets. It concatenates a digest to the asset name, for example, ./public/assets/application-d618a047f651d19af56d0e3e9132fba3.css. So we can not call simply to libs/<util>/<util>-blessed2.css.

No problem, we have solution for almost everything.

We are using sass-rails, so we can use the helper asset_path inside the css file. The steps are:
  • Rename ./vendor/assets/stylesheets/libs/<util>/<util>.css to ./vendor/assets/stylesheets/libs/<util>/<util>.css.erb
  • Inside ./vendor/assets/stylesheets/libs/<util>/<util>.css.erb, we wrote:
       @import url(<%= asset_path 'libs/<util>/<util>-blessed2.css' %>);
       @import url(<%= asset_path 'libs/<util>/<util>-blessed1.css' %>);
  • In the file config/application.rb I added the new files in the property config.assets.precompile: 
config.assets.precompile += %w{ libs/<util>/<util>.css libs/<util>/<util>-blessed2.css libs/<util>/<util>-blessed1.css }
Remember, I only used the gem css_splitter for the selector count, don't leave it in your Gemfile!

I hope that this post helps you.

I am enjoying this wonderful chocolate with salt, I deserve it!
















2 comentarios:

  1. Nice bug and nice solution.

    Other solution : write in your customer contracts that you only support the latest 2 major versions of each browser. As IE11 is the latest version, you wouldn't have to support IE9 and less :).

    ResponderEliminar