select和input type=file两个都是html标签,但是它们在不同的浏览器上显示是完全不同,对于那些对UI要求非常高的网站来说,这是不可接受的。由于这两个标 签的样式是由浏览器实现的,所以要想完全通过css来统一样式几乎是不可能的,所以我们这里需要借助javascript的帮助。

比如我们 想要把input type=file做成下面这个样子

看上去这个应该由两个标签组成,左边是一个text field,右边是一个上传的按钮,要想在所有的浏览器上都把input type=file做成这个样子好像没这个可能,可以想到的办法就是设置两个层,下面的层由一个text field和一个按钮组成,上面是一个透明的input type=file的层,高度和宽度正好覆盖下面的层就可以了。

我是借助 javascript来生成下面的那个层

$.each($('form input[type=file]'), function(i, elem) {
  $(elem).parent().append($("<div class='fakefile'><input type='text'><div class='browser_button'></div></div>"));
  $(elem).change(function() {
    $(this).parent().find('input[type=text]').val($(this).val());
  });
});

这里fakefile就是下面的那个层,<div class="browser_button"></div>是通过css_sprite得到的按钮图片(最近使用 css_sprite到了偏执的状态)。然后就是通过css来区分上下两个层

.file {
  width: 209px;
  height: 28px;
  position: relative; }
.file input[type=file] {
  z-index: 2;
  width: 278px;
  opacity: 0;
  filter: alpha(opacity: 0);
  -moz-opacity: 0;
  cursor: pointer; }
.file div.fakefile {
  z-index: 1;
  position: absolute;
  top: 0;
  left: 0; }
.file div.fakefile input {
  width: 207px;
  height: 28px;
  border: 1px solid #c1c1c1;
  -moz-border-radius: 3px;
  -webkit-border-radius: 3px; }
.file div.fakefile .browser_button {
  position: absolute;
  top: 0;
  left: 207px; }

input type=file的z-index为2,.fakefile的z-index为1,表示fakefile为下面的层,input type=file为上面的层。input type=file的opacity设置为0,表示input type=file为透明的,通过position: absolute可以将两个层完全重叠。这样做出来的input type=file就和之前的图片一模一样了。

对于select标签的样式修改也可以用相同的办法进行处理。

css sprite best practices

2010-04-03 00:34:12 +0800

css sprite最佳实践(中文版)

(Updated on 2010-4-4, thank Scott Ballantyne)

The advantage of using the css sprite is to reduce a large number of http requests, so it makes the web page loaded much faster. I often find it it painful for me to compose a lot of images into one css sprite image and measure the x and y positions for each image.

Last year, I wrote a css_sprite gem, but to use it you need to define all the images you want to do the css sprite in the configuration file, and it is not easy to use. Because of this, recently I rewrote the css_sprite gem, it is not necessary to use configuration file any more by default, the new css_sprite gem follows the idea of Convention Over Configuration. Now the css_sprite gem can do the css sprite automatically.

First, let's look at the convention of the directory structrue.

The blue parts on the above image are the css_sprite directories according to convention. That means the directory whose name is css_sprite or css_sprite suffixed (e.g. another_css_sprite) needs to do the css sprite.

The green parts are images that need to be tranformed into the css sprite. Once you add images to the css_sprite directory or remove images, the css sprite operation will be automatically executed.

The red parts are automatically generated files. For each css_sprite directory, there is a css sprite image generated, combined by all the images under the css_sprite directory, and there is also a css or sass file generated according to the css_sprite image.

What about the generated css file?

.twitter_icon, .facebook_icon, .login_button, .logout_button {
  background: url('/images/css_sprite.png?1270170265') no-repeat;
}
.twitter_icon { background-position: 0px 0px; width: 14px; height: 14px; }
.facebook_icon { background-position: 0px -19px; width: 14px; height: 14px; }
.login_button { background-position: 0px -38px; width: 103px; height: 36px; }
.logout_button { background-position: 0px -79px; width: 103px; height: 36px; }

That means, the generated css file follows the naming convention: one image under the css_sprite directory corresponds to one class in the generated css file, the name of class is just the same as the name of image. The advantage of this is that developers only need to know what images are under css_sprite directory, then they can use the corresponding class names to display these images on the html page.

One difficulty that you may encounter is adding styles to a class that is being used by css sprite. Below is a description of how you might handle such an issue.

1. some related classes have some common styles. e.g. to buttons, you may apply them on input or a elements, in common you need to hide the text on input and a elements, hide the border and so on. So you should generate the common styles for these related classes. For example

.login_button, .logout_button {
  text-indent: -9999px;
  display: block;
  cursor: pointer;
  font-size: 0; # for ie
  line-height: 15px; # for ie
  border: 0; }

These styles should be added to the automatically generated css file accroding to user's customization. What customizations I always use are icon, logo, button and bg (abbr. for background).

2. the style for some specified class, e.g. define margin or float for login_button

.login_button {
  margin: 0 10px;
  float: left; }

These styles should be written in the user-defined css file, not the automatically generated css file.

Follow these rules, what you need to do is to put a new image into the css_sprite directory, then use the corresponding class name to display the image on html page. Generating css sprite image and css files are done automatically. Of course when I remove an image from the css_sprite directory, it is also removed from css_sprite image and css.

These are css sprite best practice I follow. Now it's time to see how to implement these in a rails application.

1. Install my css_sprite gem/plugin

sudo gem install css_sprite

Or

script/plugin install git://github.com/flyerhzm/css_sprite.git

Notice, css_sprite gem depends on the rmagick gem, so please make sure RMagick is successfully installed on your system.

Then add css_sprite gem in the environment.rb or Gemfile

2. Next make a directory whose name is css_sprite or ends with css_sprite (e.g. another_css_sprite) under public/images directory

3. If you install the css_sprite as a gem, you should add css_sprite task in Rakefile

require 'css_sprite'

If you install it as a plugin, you can skip this step

4. Let's start the css sprite automation

rake css_sprite:start

5. Put the images which need to do the css sprite under the css_sprite directory, then you will see the automatically generated css sprite image and css files. Now you can use the corresponding class name to display image on html. And don't forget include the stylesheet

<%= stylesheet_link_tag 'css_sprite' %>

Do you feel the you are saved from the dull css sprite job? Here are some additional tasks.

If you want to stop the css sprite automation, run

rake css_sprite:stop

If you want to restart the css sprite automation, run

rake css_sprite:restart

If you only want to run the css_sprite manually instead of automation, run

rake css_sprite:build

These are the default processes without configuration. If you want to use sass or you want to define common styles for some related classes, you need to define config/css_sprite.yml file

suffix:
  button: |
    text-indent: -9999px;
    display: block;
    cursor: pointer;
    font-size: 0;
    line-height: 15px;
    border: 0;
    outline: 0;

The effect of the above configuration file is to generate some common styles for all the images whose filename is "button" suffixed.

engine: sass
suffix:
  button: |
    text-indent: -9999px
    display: block
    cursor: pointer
    font-size: 0
    line-height: 15px
    border: 0
    outline: 0

The effect of the above configuration file is to generate a sass file, and generate some common styles for all the images whose filename is "button" suffixed. Please check the difference of the two configuration files, the content followed "button:" are copied to the automatically generated css or sass file. So please input the content according to the syntax of css or sass.

Notice, once you changed the configuration file, please make sure stop and start the css_sprite to take effects.

Finally, let's look at the automatically generated css file.

.login_button, .logout_button {
  text-indent: -9999px;
  display: block;
  cursor: pointer;
  font-size: 0;
  line-height: 15px;
  border: 0; }
.twitter_icon, .facebook_icon, .login_button, .logout_button {
  background: url('/images/css_sprite.png?1270170265') no-repeat;
}
.twitter_icon { background-position: 0px 0px; width: 14px; height: 14px; }
.facebook_icon { background-position: 0px -19px; width: 14px; height: 14px; }
.login_button { background-position: 0px -38px; width: 103px; height: 36px; }
.logout_button { background-position: 0px -79px; width: 103px; height: 36px; }

Don't hesitate to use the css_sprite to speed up your productivity, http://github.com/flyerhzm/css_sprite

css sprite最佳实践

2010-04-02 11:29:17 +0800

css sprite best practices (english version)

应用css sprite的好处在于可以大量减少http请求数,从而达到更快加载页面的效果。

但是对于像我这样的懒人,你让我每次都一个一个把图片copy到一个css_sprite图片里,还得量一下这个每个图片对应的x和y坐标,实在是一种折磨。

去年我就写了一个css_sprite的插件,但是由于需要在配置文件中定义所有需要组合在一起的图片,用起来还是很麻烦,不够傻瓜化。最近我把css_sprite插件重写了一遍,默认不需要使用配置文件,遵循rails的Convention Over Configuration的思想,可以做到全自动的css sprite操作。

首先,让我们看看目录结构的Convention是如何定义的

上图中蓝色部分就是Convention的css sprite目录,也就是在public/images目录下面的css_sprite目录或者以css_sprite结尾的目录(比如another_css_sprite),需要执行css_sprite操作。

绿色部分则是需要被css sprite的图片,你可以动态的在css sprite目录下面增加或删除图片,css sprite操作就会被自动触发。

而红色部分都是自动生成的,每个对应的css sprite目录,都会生成一个css sprite图片(图片内容为该css sprite目录下的所有图片组合),生成一个css sprite的css文件或者sass文件。

那么生成的css文件是怎么样的呢

.twitter_icon, .facebook_icon, .login_button, .logout_button {
  background: url('/images/css_sprite.png?1270170265') no-repeat;
}
.twitter_icon { background-position: 0px 0px; width: 14px; height: 14px; }
.facebook_icon { background-position: 0px -19px; width: 14px; height: 14px; }
.login_button { background-position: 0px -38px; width: 103px; height: 36px; }
.logout_button { background-position: 0px -79px; width: 103px; height: 36px; }

也就是说,它生成的css文件遵循如下一个命名规范:一个css sprite目录下的图片对应css里的一个class,图片的名字就是class的名字。这样的好处在于开发人员只需要知道css sprite目录下面有哪些图片,他就可以在html页面上面使用哪些class名字来显示这些图片,而且当css_sprite的算法发生变化的时候也不会对页面显示产生任何影响。

实际使用当中你可能会碰到这样的问题:你除了要使用这些css_sprite生成的class名来显示图片,还需要为它们定义额外的style,而这个可以分为两部分:

1. 一些相关的class有许多共同的style,比如对于button来说,你会把它应用到input或这a上面,一般就需要隐藏input或a标签上面的文字,需要去掉边框等等,所以你需要为这些相关的class生成共同的style,比如

.login_button, .logout_button {
  text-indent: -9999px;
  display: block;
  cursor: pointer;
  font-size: 0; # for ie
  line-height: 15px; # for ie
  border: 0; }

这些style应该根据用户的定制加入到自动生成的css文件中去。

2. 某个具体class应用的style,比如login_button需要定义margin或float

.login_button {
  margin: 0 10px;
  float: left; }

这些style应该写在用户自己的css文件中,而不应该加入到自动生成的css文件中去。

遵循以上的规则,我需要做的事情就是把一个新的图片扔到css_sprite目录下,然后在页面上使用这个图片对应的class name来显示这个图片,其它的事情(生成css sprite图片和css)都应该是自动完成的,当然当我把一个图片从css_sprite目录下面移除的时候,它也会自动从css_sprite图片和css中移除。听起来很不错吧!

上面就是我定义的css_sprite最佳实践,理论还不错,下面看看在rails项目中是如何使用的?

1. 当然是安装我的css_sprite的gem/plugin

sudo gem install css_sprite

或者

script/plugin install git://github.com/flyerhzm/css_sprite.git

注意,css_sprite依赖于rmagick gem,所以先请确保RMagick已经在你的系统中成功安装。

然后就是在environment.rb文件或者Gemfile文件中增加css_sprite gem

2. 在public/images目录下面生成css_sprite目录或者以css_sprite结尾的的目录(如:another_css_sprite)

3. 如果你是通过gem安装的css_sprite,那么需要在Rakefile中引用css_sprite的task

require 'css_sprite'

如果你是通过plugin安装的,可以跳过这一步

4. 开始css sprite自动化之旅

rake css_sprite:start

5. 把需要做css sprite操作的图片都放入css_sprite目录下面,然后你会看到自动生成的css_sprite图片和css文件,现在你就可以在html页面上引用图片对应的class名字来显示图片咯。对了,别忘了引用生成的css文件哦

<%= stylesheet_link_tag 'css_sprite' %>

是不是感觉从机械的css_sprite工作中解脱出来了呀。下面再介绍些额外的tasks

如果你想结束css_sprite自动化之旅,执行

rake css_sprite:stop

如果你想重新开始css_sprite自动化之旅,执行

rake css_sprite:restart

如果你不需要css_sprite自动化执行,而只想手动执行css_sprite操作,执行

rake css_sprite:build

上面这些流程都是在没有配置的默认情况下完成的,如果你需要使用sass,或者你需要为某些相关的class定义共同的style,只需要定义config/css_sprite.yml配置文件即可

suffix:
  button: |
    text-indent: -9999px;
    display: block;
    cursor: pointer;
    font-size: 0;
    line-height: 15px;
    border: 0;
    outline: 0;

上面这个配置文件的作用是为所有的文件名以button结尾的图片,生成一段共同的style。

engine: sass
suffix:
  button: |
    text-indent: -9999px
    display: block
    cursor: pointer
    font-size: 0
    line-height: 15px
    border: 0
    outline: 0

上面这段配置文件的作用是指定css_sprite自动生成sass文件,同时为所有的文件名以button结尾的图片,生成一段共同的style。注意两段配置文件的不同,button下面的内容都会完整的复制到自动生成的css或sass文件,所以你需要根据css和sass的语法来填入。

注意:当修改了配置文件,需要stop再start css_sprite才能生效。

最后,来我们来看一段自动生成的css文件吧

.login_button, .logout_button {
  text-indent: -9999px;
  display: block;
  cursor: pointer;
  font-size: 0;
  line-height: 15px;
  border: 0; }
.twitter_icon, .facebook_icon, .login_button, .logout_button {
  background: url('/images/css_sprite.png?1270170265') no-repeat;
}
.twitter_icon { background-position: 0px 0px; width: 14px; height: 14px; }
.facebook_icon { background-position: 0px -19px; width: 14px; height: 14px; }
.login_button { background-position: 0px -38px; width: 103px; height: 36px; }
.logout_button { background-position: 0px -79px; width: 103px; height: 36px; }

还等什么,赶紧来使用css_sprite来加快你的工作效率吧:http://github.com/flyerhzm/css_sprite

button_to的使用

2010-03-02 16:15:28 +0800

页面间的跳转或者请求,用得最多的就是link_to和form_for,一个发送get或delete请求,一个post或put请求。但是碰到投票之类的链接,虽然是一个post请求,但是form里面却不需要任何数据,碰到这样的情况,我们希望像link_to那样一行搞定。

也许你会通过link_to 'xx', 'xx', :method => :delete联想到link_to 'xx', 'xx', :method => :post,但是很不幸,没有这样使用的。还好,rails提供了一个简单的helper——button_to

button_to 'Vote', post_votes_path(post), :class => 'vote_icon'

生成的html代码如下

<form class="button-to" action="/post/1/comments" method="post">
    <div>
        <input type="submit" value="Vote" class="vote_icon" />
        <input type="hidden" value="zbT/x/CpCjDQdTb2IZQ+ttGqNfv5PfsAJ3/BRK+wBqM=" name="authenticity_token" />
    </div>
</form>

另外说一下页面的显示吧,我们使用vote_icon的class,定义一个background image,问题出来了,使用input会出现一个边框,鼠标放上去是箭头,另外vote icon上面还会显示出字来,解决的方法就是

.vote_icon {
    border: 0;
    text-indent: -999px;
    cursor: pointer;
}

嗯,现在就和link image完全一样了。别急,打开IE7和IE6看看,vote icon上面仍然显示出字来,原来IE7和IE6不支持input上面的text-indent,所以要额外加上

.vote_icon {
    border: 0;
    text-indent: -999px;
    cursor: pointer;
    font-size: 0;
    line-height: 10px;
}

现在一切就都OK咯!

hover and png for ie6

2010-02-24 17:49:26 +0800

IE6可以说是前端设计师们的最大梦魇,不支持圆角,margin double等等问题,使得书写css的时候不得不专门针对IE6浏览器增加额外的规则。

hover和png透明也是IE6所不支持的,解决方法如下:

hover可以通过Whatever:hover脚本来hack,使用方法很简单,在ie6.css文件中定义

.need_hover_element {
  behavior: url("/stylesheets/csshover3.htc"
)

png透明需要iepngfix脚本来hack,使用方法稍微复杂些,首先在ie6.css文件中定义

.need_png_transparent_element {
  behavior: url("/stylesheets/iepngfix.htc")
}

接着在html文件中引入iepngfix_tilebg.js

然后修改iepngfix.htc文件,修改其中的blank.gif文件路径

IEPNGFix.blankImg = '/images/blank.gif';

好了,你的网站现在能够使IE6支持hover和png透明了,不过当png文件是在hover之后才出现的,png透明似乎就不起作用了。