https://realazy.com/favicon.icohttps://realazy.comRealazyrealazy.comRealazy2023-02-03T10:00:00+08:00<p>在终端编辑多行命令会非常不顺手和容易出错,此时你应该将 <code>EDITOR</code> 环境变量设置为你顺手的编辑器。起初我直接 <code>export EDITOR=emacs</code>,但这样会调起桌面端 Emacs 编辑器;然后我买了个乖 <code>export EDITOR=emacs -nw</code>,这样 Emacs 直接在终端启动了,但需从头到尾加载 Emacs 的配置,无论时间长短,打断感极强。好在 Emacs 提供 C/S 模式,即是说,配置加载只发生在 server 启动阶段,client 启动时间约等于连接 server 的时间,瞬间。</p><h2 id="server">启动 server</h2><p>启动 server 最简单的方式,是在终端输入 <code>emacs --daemon</code>,或者在已打开的 Emacs 编辑器内 <code>M-x server-start</code>。</p><p>如果需要随系统自启动,由于我用的是 macOS 系统,特别说明下该系统下的自启动方法:</p><ol><li>使用 <code>brew</code> 安装 <a href='https://github.com/d12frosted/homebrew-emacs-plus'><code>emacs-plus</code></a>,它附赠了 service:<code>brew services start emacs-plus@29</code>。(注意 <code>@29</code> 需更换为你所安装的版本)</li><li>开启 Script Editor(位于 <code>/Applications/Utilities/</code> 内,或用 Spotlight 搜索),新建文件,输入以下内容:<pre class='applescript'><code class='applescript'>tell application "Terminal" do shell script "/Applications/Emacs.app/Contents/MacOS/Emacs --daemon"end tell</code></pre>并保存为 <code>Application</code> 的文件格式。进入 <code>System Settings -> General -> Login Items</code>,将刚才保存的文件拖入 <code>Open at Login</code>。</li></ol><p>其他操作系统可参考:<a href='https://www.emacswiki.org/emacs/EmacsClient'>https://www.emacswiki.org/emacs/EmacsClient</a>。</p><h2 id="client">启动 client</h2><p>在 <code>~/.zshrc</code>(如果你还用 <code>bash</code>,则是 <code>~/.profile</code>)中设置 <code>EDITOR</code>:</p><pre class='sh'><code class='sh'># 如果想启动 GUI emacs,则将 -t 改为 -cexport EDITOR="emacsclient -a '' -t"</code></pre><p>在终端中,编辑命令时,按 <code>C-x C-e</code> 就会调出 Emacs。如果上述一切准备就绪的话,Emacs 就会瞬间开启,没有任何配置加载过程。在开启的 Emacs 内编辑完毕,可按 <code>C-x #</code> 完成编辑返回到终端。</p><h2 id="">免应答优雅退出</h2><p><code>C-x #</code> 退出时,如果产生了编辑,Emacs 会询问是否保存。绝大部分情况下肯定都是编辑过,这个询问非常干扰多此一举。搜索一番并无定制的选项可禁止,只好自己去阅读下源码,看有没有解决方案。</p><p>首先,需要了解该快捷键调用了哪个方法。在 Emacs 中,可以在 <code>C-h k</code> 后键入 <code>C-x #</code> 一探究竟。一通操作猛如虎,最终发现在 <a href='https://github.com/emacs-mirror/emacs/blob/emacs-29/lisp/server.el#L1626'>https://github.com/emacs-mirror/emacs/blob/emacs-29/lisp/server.el#L1626</a> 中执行了 <code>y-or-n-p</code> 这个询问函数。这里没有给任何的 hook,override 整个 <code>server-done</code> 显然是下下策。如果能在 <code>server-done</code> 内临时让 <code>y-or-n-p</code> 返回 <code>t</code> 就能皆大欢喜了。我的解决方案如下:</p><pre class='elisp'><code class='elisp'>(defun cxa/yes-never-no (&rest _args) "Override `y-or-n-p' to always retrun t." t)(defun cxa/yes-then-restore (orig-fun &rest args) "Make `y-or-n-p' always return t before ORIG-FUN, and restore after applied ARGS." (advice-add 'y-or-n-p :override 'cxa/yes-never-no) (apply orig-fun args) (advice-remove 'y-or-n-p 'cxa/yes-never-no))(advice-add 'server-done :around 'cxa/yes-then-restore)</code></pre><p>代码即解释。如果是首次加入这段配置,记得重启 Emacs server。</p>n%C5%20$%0D%ED2%3C%7DG#P%03%D0Y%84作为命令行编辑器的 Emacs2023-02-03T10:00:00+08:00https://realazy.comRealazy<p>Quickly switch to specific app on macOS, I'm not meaning using app switcher (that one triggered by "<code>⌘ - tab</code>") which may require looping the app list.</p><p>I'll show you a technique, by which you can quick-switch to specific app by only one shortcut, even if that app hasn't launched yet, and no app switcher shown. This is powered by open-source software tools, you'll find it's easier if you have some command-line skills.</p><h2 id="Prerequisite">Prerequisite</h2><ul><li>Follow <a href='https://brew.sh'>https://brew.sh</a> to install "<code>brew</code>" if you have never done that before</li><li>Install "<code>skhd</code>" by the guide on <a href='https://github.com/koekeishiya/skhd'>https://github.com/koekeishiya/skhd</a>, and make sure the "<code>skhd</code>" service running on your system</li></ul><h2 id="Craft-some-shell-scripts">Craft some shell scripts</h2><p>Creat a file using below content, and save it as "<code>activate-app.sh</code>", place under the same directory where you store "<code>skhdrc</code>", I assume your directory is "<code>~/.config/skhd</code>".</p><pre class='sh'><code class='sh'>#!/usr/bin/env sh/usr/bin/env osascript <<SCRIPT-------------------------------------tell application "$1" activate if (count windows) is 0 then do shell script "open -a '$1'" end ifend tell-------------------------------------SCRIPT</code></pre><p>Open "<code>Terminal.app</code>" or other terminal simulator you favorite, make our new shine script executable:</p><pre class='sh'><code class='sh'>chmod +x ~/.config/skhd/activate-app.sh</code></pre><p>The last step, is write your own shortcuts, open "<code>~/.config/skhd/skhdrc</code>", add some lines like:</p><pre class='sh'><code class='sh'>cmd - return : $HOME/.config/skhd/activate-app.sh iTermalt - return : $HOME/.config/skhd/activate-app.sh "Google Chrome"</code></pre><p>The only points for attention, make app name quoted if it contains spaces.</p><p>Now, restart "<code>skhd</code>" by "<code>brew services restart skhd</code>", you can press "<code>⌘ - return</code>" to switch to "<code>iTerm</code>", without any glitches.</p>%C1%12%A0%C4M%CE%98%16%1B%FBE%08%91%1A%5BCmacOS: Quickly Switch to App Using Keyboard Shortcut2022-05-09T10:00:00+08:00https://realazy.comRealazy<p>So many static site generators on the earth, they can be easily divided into two main categories:</p><ul><li>Purely static, like 11ty</li><li>Full hydration, like Gatsby</li></ul><p>If you need some interactive features on the client, you'll need to add extra layers on purely static content. Interactive mode enabled by default on full hydration, but it's too heavy for a site which mainly informative not app-like.</p><p>After some investigation, I found <a href='https://github.com/Elderjs/elderjs'>Elder.js</a> that supports partial hydration, which means you can control the necessary parts to be hydrated. I gave it a try, and you can check out <a href='https://www.rocbank.com/en/'>https://www.rocbank.com/en/</a> to see the result.</p><h2 id="Pros">Pros</h2><ul><li>Svelte component, all benifits from Svelte ecosystem</li><li>Static content by default, contrallable hydration</li><li>Shortcodes</li></ul><h2 id="Cons">Cons</h2><ul><li>Lacks assets versioning</li><li>No default way to seperate assets (in order to serve on CDN hosts)</li><li>Rare communtiy plugins</li></ul>NO%94%BA%EE%02%DE%9D%91%3C%097%E8%92%C0%06Elder.js first impression2021-06-05T10:00:00+08:00https://realazy.comRealazy<p>我用 <a href='https://calibre-ebook.com'>Calibre</a> 管理电子书籍已有很多年。它推出阅读器也有一段时间了,虽然没有 iBooks 那么美观,但胜在方便,而且直接支持 mobi 格式,渐渐我就用它替换了 iBooks。</p><p>Calibre 阅读器并非 native 的实现,选择文本后无法使用 macOS 的系统字典进行查询,也无法通过 PopClip 这种工具来转接。但它提供了一个自定义词典功能,可以通过 HTTP 地址进行查询。虽然有很多选择,但我最想用的还是系统字典。那么,能够通过 HTTP 查询系统字典,是我想做到的。</p><h2 id="">调研</h2><p>一番搜索,<code>Core Services</code> 的 <code>Dictionary Services</code> 提供了查询接口,遗憾的是功能有限,没有能覆盖到 <code>Dictionary.app</code> 提供的功能。然而,API 是藏不住的,<a href='https://nshipster.com/dictionary-services/'>https://nshipster.com/dictionary-services/</a> 提供了这些私有接口。</p><p>一开始我想直接创建一个原生应用程序提供 HTTP 服务,但仔细看 API,它的输出有纯文本,也有 HTML (带 CSS 或者不带)。既然有纯文本,做成一个命令行工具,用途可以更宽广。然后再通过 Node 简单封装一下 HTTP 服务,开发效率会高很多。</p><h2 id="">实现</h2><h3 id="">命令行工具</h3><p>我一直希望能脱离 Xcode 用 <code>make</code> 来管理一下项目,这个小项目正是一个好机会。<code>Core Services</code> 类型都是 <code>Core Foundation</code> 类型,我也决定不用 ObjC,用纯 C 来练练手。</p><p>最终我把它命名为 <code>dicmd</code>,开源在 <a href='https://github.com/cxa/dicmd'>https://github.com/cxa/dicmd</a>。为了方便安装,学会了简单的 <a href='https://github.com/cxa/homebrew-formulae/blob/main/dicmd.rb'>homebrew formulae</a>。</p><h3 id="HTTP">HTTP 服务</h3><p>我选择了简单直接的 <a href='https://github.com/vercel/micro'>micro</a>,这个倒没有什么好说。难点在于,词条的输出就是一个完整的 HTML 文档,如何将多词条拼凑在同一个页面。iframe 并不能自适应尺寸,<a href='https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM'>Shadow DOM</a> 是一个优雅的解决方案。具体可参考 <code>dicsrv</code>,开源在 <a href='https://github.com/cxa/dicsrv'>https://github.com/cxa/dicsrv</a>。</p><h2 id="">效果</h2><p>如图。</p><p><img src='/assets/posts/2021_01_13_sample.png' alt='Screen shot' /></p>%97w%05%E5k%DBi%DC%A7%F1%92#%E4JJ%9A记一款小工具的诞生2021-01-13T10:00:00+08:00https://realazy.comRealazy<h2 id="dotfiles">dotfiles 困境</h2><p>Unix 系统下,软件配置一般保存在 <code>$HOME</code> (即你的用户主目录)下,比如 <code>zsh</code> 的配置就保存在 <code>$HOME/.zshrc</code> 文件里。这些文件的特点是以 <code>.</code> (dot) 开头,因此我们姑且称之为 dotfiles。</p><p>由于这些文件直接位于 <code>$HOME</code> 下面,无论是备份、版本化管理、还是设备间共享,都会造成相当大的困扰,因为相关工具一般都以目录为单位,你又不可能版本化管理整个 <code>$HOME</code>。</p><h2 id="">粗糙解决方案</h2><p>一个解决方案是,把这些 dotfiles 纳入同一个目录,<code>ln</code> 到正确位置。例如:把 <code>.zshrc</code> 放在 <code>~/dotfile</code> 目录里,然后 <code>ln -s ~/dotfiles/.zshrc ~/.zshrc</code>。之后只需管理 <code>~/dotfiles</code> 这个目录。</p><p>但是有些配置不止一个文件,手工 <code>ln</code> 难免挂一漏万,精神一恍惚也容易出错。</p><h2 id="GNU-Stow">GNU Stow</h2><p><a href='https://www.gnu.org/software/stow/'>GNU Stow</a> 原本目的是啥我不知道,不过它正好满足了我们的管理 dotfiles 的几乎所有需求。</p><p>首先安装 Stow,如果你是 macOS 系统,<code>brew install stow</code> 即可。</p><p>我们继续以 <code>.zshrc</code> 为例,读君代码几行,胜过千言万语:</p><pre class='sh'><code class='sh'>mkdir -p ~/dotfiles/zshmv ~/.zshrc ~/dotfiles/zshcd ~/dotfilesstow zsh</code></pre><p>就是这么简单,先建立个总目录 <code>~/dotfiles</code>,在此目录下,以软件名称为单位再建目录,将已有配置迁移到该目录下,然后运行 <code>stow</code> 即可。</p><p>不过需要注意的是,<code>dotfiles</code> 目录直接建立在 <code>$HOME</code> 目录下,如果不属于这种父子层级,比如我的位于 <code>~/Dropbox/dotfiles</code>,那么需要以 <code>stow zsh -t ~</code> 的方式运行。有个简单的方案,运行一次,一劳永逸,再也不用加 <code>-t ~</code>:</p><pre><code>cd ~/Dropbox/dotfilesmkdir stowecho '--target=~' > stow/.stowrcstow stow -t ~</code></pre><p>其他的软件配置类推,这样你的 dotfiles 就始终在一个目录下,你想备份,版本管理,设备间共享(Dropbox),开源共享(GitHub)都没问题啦。</p><h2 id="">设备间共享与私有配置</h2><p>我自己有两台电脑运行 macOS,大部分配置都一致,但是一台私用,一台工作,我并不想把两者间不同的共享。</p><p>我的解决方法是,两台电脑用不同的用户名,在共享的配置中,加载各自的私有配置,以 <code>.zshrc</code> 为例:</p><pre><code># 省略了很长一段一段共享配置zshrc_local=$HOME/.zshrc.$(whoami)if [ -f "$zshrc_local" ]; then source "$zshrc_local"fi</code></pre><p>这种情况下,</p><ul><li><code>cxa</code> 用户的加载 <code>.zshrc.cxa</code>;</li><li><code>realazy</code> 用户的加载 <code>.zshrc.realazy</code>。</li></ul>%7D%93'%89%01%99C%C9%00%81U(%CAG%AFT使用 Stow 管理 dotfiles2020-11-22T10:00:00+08:00https://realazy.comRealazy<p>译自 <a href='https://github.com/hcvst/erlang-otp-tutorial'>https://github.com/hcvst/erlang-otp-tutorial</a>.</p><p>这是一篇写给 Erlang 新手的 Erlang/OTP 教程。若你已熟悉 Erlang 语法,知道如何编译模块,有探索 OTP 的好奇心,想在你自己的应用程序中发挥它的能力,那么本教程是为你量身定制的。我们以一个非 OTP 的服务器和监督器(supervisor)开始,看看它们有何不足,然后改进为正确的 OTP 应用程序。</p><p>本教程会涉及:</p><ul><li>如何白手起家一个非 OTP 服务器,并使用非 OTP 监督器实现自动重启。</li><li>这种实现有何缺点,如何用 OTP 克服。</li><li>略略介绍下使用 rebar 之类的工具捋顺 OTP 之旅,了解文件组织的惯例。</li><li>如何使用 OTP 的 <code>gen_server</code> 和监督器行为。</li><li>连结这一切的 OTP 应用程序行为</li></ul><p>不过我们先来几发开场白。</p><h2 id="">你</h2><p>熟悉 Erlang 语法,知道 Erlang 模块,怎样编译,了解一些诸如递归和单次赋值变量的函数式编程概念了吗?答案是肯定的?好极了,来学点 OTP 吧。你会发现起步并不难,但让我先简单说说我的 Erlang 体验吧。</p><h2 id="">我</h2><p>我跟你还在同一起跑线呢,不同之处可能只在于,我已经开始阅读 Manning 的 Erlang & OTP in action 这本书(中译版<a href='http://www.ituring.com.cn/book/828'>《Erlang/OTP 并发编程实战》</a>)。毫无夸张地说,这是一本我最爱的技术书籍之一,里面容易吸收的知识随处可见。可以的话买一本。</p><p>同等水平的人教同等水平的人,我觉得就是心理学课程所说的「脚手架」。写这篇教程,一是加固我对 OTP 的理解,二是当作你学 OTP 的助推器。一起学习,如果觉得哪不对请留言。</p><h2 id="OTP">OTP</h2><p>OTP 表示 Open Telecom Platform(开放通信平台),虽然叫这名,但它并不必一定要跟通信应用程序搭上关系。把 OTP 当作一个 Erlang 平台的框架,有了它可以写出健壮和高可用性的应用程序。</p><p>特别是,用这几个概念来理解 OTP 你可能会觉得更合理:</p><ul><li>要求模块暴露哪些函数的一种规范(被称为「行为」)</li><li>遵循某个行为并实现相应函数的一个具体模块</li><li>在 OTP 语境下执行拥有行为的模块的运行时组件</li></ul><p>大致可以类比面向对象中的:</p><ul><li>定义行为的接口或抽象类</li><li>接口或抽象类的实现</li><li>管理实现的实例的容器</li></ul><p>当然,这只是表面的相似而已。已有的知识对我们理解新概念可能会有所帮助,但苹果和桔子必须是要分清的啊。</p><h2 id="OTP_1">非 OTP 的服务器</h2><p>开场白叽歪完了,让在同一水平的我们来写一个非 OTP 的简易服务器吧。这是代码:</p><pre class='erl'><code class='erl'>%%%---------------------------------------------------------------------------%%% @doc 非 OTP 的建议服务器%%% @author Hans Christian v. Stockhausen%%% @end%%%----------------------------------------------------------------------------module(no_otp). % 模块名(跟 erl 文件名一致, % 例如 no_otp.erl)%% API-export([ % 需暴露出去的函数,亦即 API start/0, % - 启动新的服务器进程 stop/0, % - 关停服务器 say_hello/0, % - 打印 "Hello" 到标准输出 get_count/0 % - 回复轮询主体的调用次数 ]).%% 回调-export([init/0]). % 暴露出来以便在 start/0 中创建 % 单独列出来提示客户端不要使用-define(SERVER, ?MODULE). % SERVER 宏是 MODULE 的别名, % 会展开成 'no_otp'-record(state, {count}). % 服务器状态的记录。 % 有点随心所欲, % 用来追踪主要轮询的频度 - 见 loop/1%=============================================================================% API - 客户端跟服务器交互的函数%=============================================================================start() -> % 创建调用 init/0 的新进程 spawn(?MODULE, init, []). % 返回新进程的 PIDstop() -> % 发送 stop 原子到进程 ?SERVER ! stop, % 吩咐服务器关闭 ok. % 并返回 ok 给调用者say_hello() -> % 发送 say_hello 原子到进程 ?SERVER ! say_hello, % 以打印 "Hello" 到标准输出 ok.get_count() -> % 给调用者发送 Pid 和 get_count 原子 ?SERVER ! {self(), get_count}, % 请求 count 的值 receive % 等待匹配的响应并返回 {count, Value} -> Value % Value 给调用者 end.%=============================================================================% 回调函数 - 客户端不要直接用%=============================================================================init() -> % 会被 start/0 触发 register(?SERVER, self()), % 在 no_otp 注册新进程 PID loop(#state{count=0}). % 启动服务器的主要轮询%=============================================================================% 内部函数 - 注意,这些函数不暴露%=============================================================================loop(#state{count=Count}) -> % 服务器的主要轮询 receive Msg -> % API 函数发送消息时 case Msg of % 检查里面包含的原子 stop -> % 如果是 'stop' 原子 exit(normal); % 退出进程 say_hello -> % 如果是 'say_hello' 原子 io:format("Hello~n"); % 写 "Hello" 到标准输出 {From, get_count} -> % 如果 Msg 是元组 {Pid(), get_count} From ! {count, Count} % 以标记元组 {count, Value} 的形式 end % 回复当前的计数值 end, loop(#state{count = Count + 1}). % 以已更新的状态递归</code></pre><h2 id="">试验</h2><ul><li>保存上面的代码到 no_otp.erl 文件。</li><li>在文件所在目录中以 <code>erl</code> 命令打开 Erlang shell。</li><li>编译模块:<code>c(no_otp)</code>。</li><li>启动服务器:<code>no_otp:start()</code>。</li><li>调教服务器打印几次“Hello”:<code>no_otp:say_hello()</code>。</li><li>查询当前计数器的值:<code>no_otp:get_count()</code>。</li><li>关闭服务器:<code>no_otp:stop()</code>。</li></ul><p>这是我的 shell 纪录。诚然,你想知道 <code>get_count/0</code> 意义何在。没有什么目的,只是想演示如何实现一个同步函数。</p><pre><code>Eshell V9.2 (abort with ^G)1> c(no_otp).{ok,no_otp}2> no_otp:start().<0.70.0>3> no_otp:get_count().04> no_otp:say_hello().Hellook5> no_otp:get_count().26></code></pre><p>对于这个语境下<em>服务器</em>这个术语,一开始我很困惑。这怎么就成了一个服务器啦?细思量,这跟分发此页面到你浏览器的 HTTP 服务器异曲同工。除非停掉否则会一直运行,它遵守某种协议(只不过是 Erlang 的内部信息协议而非 HTTP 协议),对输入回应消息或执行副作用的操作。这就是个服务器,没错。</p><p>我不打算讨论代码细节,注释已详述。我只想简单说说代码结构:</p><ul><li>用一层薄薄的 API 层对客户端隐藏实现细节。<code>start/0</code>,<code>stop/0</code> 等函数把客户端从内部协议(比如这些函数要发送的原子 <code>start</code>,<code>stop</code> 等等)和内部函数解耦出来。这样更改两者中任何一个都不会破坏客户端代码。</li><li><code>init/0</code> 函数初始化状态并启动服务器的主要轮询,并按惯例以模块名注册服务器,也可以同时执行诸如连接数据库初始化等操作。注意,尽管并不想让客户端调用,但也要把这个函数暴露出来,否则在 <code>init/0</code> 里调用不了 <code>spawn/3</code>。</li><li>主要轮询等待消息,在回应中执行一些动作,必要时更新服务器状态,最后又反复调用自身。</li></ul><h2 id="">有何问题?</h2><p>真没什么毛病。有了 Erlang 它就能一直跑一直跑一直跑。我们有足够的信心认为它是正确无误的实现,毕竟是个非常简单的服务器。同时,依托于 API,发送格式不对的数据,客户端也不会意外崩溃——哦,能打包票吗?</p><p>照前面的步骤启动服务器,然后在 shell 终端键入 <code>no_otp ! crash.</code>。</p><p>服务器注册到 <code>no_otp</code> 下(参见 <code>init/0</code>),因此我们可以随心所欲给它发送乱七八糟的消息,但代码里并没有匹配 <code>crash</code> 的从句。哦哟!真的 crash (崩溃) 了。</p><p>怎么办?我们可以加上一条匹配任何消息,不对消息做任何回应的从句就好了。然而,我们知道这跟处理异常时不区分类型一刀切的捕获一样是有害的,臭虫可能会遁地无形。</p><p>Erlang 信仰的哲学是「鼓励崩溃」。别嫌我啰嗦,这段话要过目几遍。让我给你——Erlang 程序员——划下重点:别防御,不要囊括所有状况,专注规范,专注代码需求并保持精炼紧凑。如果代码出错,虽会崩溃但可修复。因客户端代码出错的崩溃,随它崩,在保存良好的状态中重启,而不是不惜一切代价让它一直运行。不要为了解决客户端的烂摊子而去编写更复杂的代码,让客户端去修复,了解了么?</p><h2 id="">监督</h2><p>好吧,随它崩,但崩溃后怎么样自动重启服务器呢?答案是编写另一个进程,用来监视服务器,一旦发现服务器挂了就重启。这个进程就叫做<em>监督器</em>,接下来是非 OTP 版本的代码。</p><h2 id="OTP_2">非 OTP 监督器</h2><p>开始前先看看以下带注解的 Erlang shell 纪录,了解下如何练习。函数 <code>whereis/0</code> 返回注册到某个名字上的进程的 <code>Pid</code>。可以看到 <code>sup:start_server/0</code> 已经启动了 <code>no_otp</code> 服务器,因为它显示了进程标识 <code><0.72.0></code>。自动重启看起来也跟预期的一样没有什么问题。</p><pre><code>Eshell V9.2 (abort with ^G)1> c(sup). % 编译监督器模块{ok,sup}2> sup:start_server(). % 启动监督器<0.70.0> % 得到进程标识 ..70..3> whereis(no_otp). % 看看 `no_otp` 进程在哪<0.72.0> % 得到 ..72..4> no_otp:say_hello(). % 试试 APIHello % 没毛病ok5> no_otp:get_count(). % 也没毛病16> no_otp ! crash. % 是时候让它崩溃了crash=ERROR REPORT===Error in process <0.40.0> with exit value: {{case_clause,crash},[{no_otp,loop,1}]} % 如其所愿,发生 case_clause 错误7> no_otp:get_count(). % 但新进程已启动0 % 证据在此8> no_otp:stop(). % 正常的方式停止ok9> whereis(no_otp). % 检查新的实例undefined % 然而并没有。正确!10></code></pre><p>这是我们的监督器代码:</p><pre class='erl'><code class='erl'>%%%----------------------------------------------------------------------------%%% @doc 非 OTP 监督器%%% @author Hans Christian v. Stockhausen%%% @end%%%-----------------------------------------------------------------------------module(sup).%% API-export([start_server/0]).%% Callbacks-export([supervise/0]).%%=============================================================================%% API%%=============================================================================start_server() -> spawn(?MODULE, supervise, []).%%=============================================================================%% Callbacks%%=============================================================================supervise() -> process_flag(trap_exit, true), % 链接进程挂了之后, % 不要崩溃,而是发一条消息 Pid = no_otp:start(), % 启动服务器 link(Pid), % 并链接 receive {'EXIT', Pid, Reason} -> % 等待通知进程关闭的消息 case Reason of % normal -> ok; % 如果理由是 'normal',无视 _Other -> start_server() % 否则重新来过 end end.</code></pre><p>在开场白提到,我也是个 Erlang 菜鸟。我对上面的监督器代码并没有多少信心,_ 这正是问题所在 _。用 “Erlang 流” 来写应用程序,要拥抱「鼓励崩溃」的哲学,必须以服务器,监督器,监督器的监督器为单位来组织代码。大量结构性,重复的样板代码,给臭虫的引入提供了充足的机会。服务器和监督器层级的概念不难理解,但能不能也容易做对呢?</p><h2 id="OTP_3">OTP 打救你</h2><p>很幸运,有人已经解决了大难题。OTP 这个特制的框架,有助于组合服务器、监督器和其他组件,我们稍后会一探究竟。OTP 开发者提供了一个坚如磐石,久经沙场,谁都知道能用的框架,我们可以接入自己的,可能是脆弱的代码。</p><p>到用 OTP 行为重写服务器和监督器的时候了。我们先看看两个有所裨益的工具。</p><h2 id="Rebar-Emacs">Rebar 和 Emacs</h2><p>有了 OTP,不必重做监督层级的轮子,但任然需要手工输入一定量的代码,填充样板很快就会厌恶。而且,要养成用 Edoc 注释写文档的好习惯,还需更多的重复输入。因此,我们需要一个小帮手。</p><p><strong><a href='https://github.com/rebar/rebar'>Rebar</a></strong> 是 Erlang 的构建工具。我只涉及必要部分。</p><p>运行以下命令,安装 rebar 并创建 "hello" 应用的脚手架。</p><pre class='shell'><code class='shell'>mkdir hellocd hellowget https://raw.github.com/wiki/rebar/rebar/rebarchmod u+x rebar./rebar create-app appid=hello</code></pre><p>新创建的 <code>src</code> 目录中生成以下文件:</p><ul><li><code>hello_app.erl</code>: OTP 应用程序行为(现在先不用管)</li><li><code>hello.app.src</code>: OTP 应用程序配置模版(也先不管吧)</li><li><code>hello_sup.erl</code>: 根监督器的最小实现</li></ul><p>缺少的是包含服务器的文件,让 rebar 来生成一个。</p><pre class='shell'><code class='shell'>./rebar create template=simplesrv srvid=hello_server</code></pre><p>不想用 rebar,你也可以手工创建这些文件,并放到 <code>src</code> 目录下。<code>src</code> 命名是个惯例,稍后我会解释。</p><p><strong>Emacs</strong> 提供 Erlang 模式,真的能加速“编辑-编译-运行”交替的开发方式,还提供了模版。我常用 vi 来完成日常编辑,但总想试试 Emacs。Erlang 模式正是好机会,还没用 Emacs 的话我建议你也试试。</p><p>(_ 译注:省略部分关于 Emacs 的例子,无关大体 _)</p><h2 id="OTP_4">OTP 文件夹</h2><p>使用以下命令</p><pre class='shell'><code class='shell'>mkdir -p my_app/{doc,ebin,priv,include,src,test}</code></pre><p>可以创建 OTP 文件夹结构,也可用 <strong>rebar</strong> 帮忙。只有 <code>src</code> 和 <code>ebin</code> 是必须的。</p><ul><li><code>doc</code>: 文档,通常是自动生成的</li><li><code>ebin</code>: 编译好的代码(<code>.beam</code>)</li><li><code>priv</code>: 诸如 html,css 之类的资源文件</li><li><code>include</code>: 客户端可能用到的公共头文件(<code>.hrl</code>)</li><li><code>src</code>: 代码和私有头文件(<code>.erl</code>,<code>.hrl</code>)</li><li><code>test</code>: 测试(<code>_test.erl</code>)</li></ul><h2 id="OTP-gen-server">OTP <code>gen_server</code></h2><p>如其名,<code>gen_server</code> 行为帮我们写通用服务器。这是用 OTP 行为重写的服务器。</p><pre class='erl'><code class='erl'>%%%----------------------------------------------------------------------------%%% @doc OTP gen_server 例子%%% @author Hans Christian v. Stockhausen%%% @end%%%-----------------------------------------------------------------------------module(hello_server). % 这部分没什么新鲜的, % 除了下一行,我们会告诉编译器-behaviour(gen_server). % 此模块实现 gen_server 行为。 % 编译器会警告我们,-define(SERVER, ?MODULE). % 如果不提供行为要求的回调函数。 % 它通过调用-record(state, {count}). % gen_server:behaviour_info(callbacks) % 能找到响应函数。试试看。%%-----------------------------------------------------------------------------%% API 暴露函数%%------------------------------------------------------------------------------export([ % 跟之前定义 API 函数一样 start_link/0, % - 启动并链接进程,一步到位 stop/0, % - 关停 say_hello/0, % - 答应 "Hello" 到标准输出 get_count/0]). % - 返回计数状态%% ---------------------------------------------------------------------------%% gen_server 暴露函数%% ----------------------------------------------------------------------------export([ % 行为回调函数 init/1, % - 初始化进程 handle_call/3, % - 处理同步调用(有回复) handle_cast/2, % - 处理异步调用(无回复) handle_info/2, % - 处理带外消息(以 ! 发送) terminate/2, % - 在关闭时调用 code_change/3]). % - 处理代码变更%% ---------------------------------------------------------------------------%% API 函数定义%% ---------------------------------------------------------------------------start_link() -> % start_link 以一步原子操作创建和链接进程 gen_server:start_link( % 参数: {local, ?SERVER}, % - 本地进程的注册名 ?MODULE, % - init/1 回调函数所在模块 [], % - init/1 所需参数 []). % - start_link 的其他选项stop() -> % 注意我们不用 ! 了 gen_server:cast( % 用 cast 异步发送消息到注册的名字 ?SERVER, % 这是异步的, stop). % 我们不想要回复say_hello() -> % 跟 stop 差不多, gen_server:cast( % 只不过是发送 say_hello 原子。 ?SERVER, % 也不想要回应, say_hello). % 只对副作用感兴趣。get_count() -> % 而在此,我们希望得到回应, gen_server:call( % 因此使用 call 同步触发服务器。 ?SERVER, % 这个调用是阻塞的,直到得到回应。 get_count). % gen_server:call/2 帮我们 % 隐藏 send/receive 逻辑。漂亮。%% ---------------------------------------------------------------------------%% gen_server 函数定义%% ---------------------------------------------------------------------------init([]) -> % 这是行为的回调函数。 {ok, #state{count=0}}. % init/1 在 gen_server:start_link/4 中调用, % 并初始化状态handle_call(get_count, _From, #state{count=Count}) -> {reply, Count, % 同步回复 Count #state{count=Count+1} % 同时更新状态 }.handle_cast(stop, State) -> % 这是第一个 handle_case 从句,处理 stop 原子。 {stop, % 我们吩咐 gen_server 正常停止 normal, % 并返回当前的 State,不要作任何改变 State % }; % 注意这有个分号handle_cast(say_hello, State) -> % 处理 say_hello 原子 io:format("Hello~n"), % 打印 "Hello" {noreply, % 这也是异步的,所以是 noreply #state{count= State#state.count+1} }. % 同时更新状态handle_info(Info, State) -> % handle_info 处理带外消息, error_logger:info_msg("~p~n", [Info]), {noreply, State}. % 比如不是经由 cast 或 call 发出的消息。 % 在此我们只是简单记录下消息terminate(_Reason, _State) -> % 这是由 gen_server 容器在关闭时触发的。 error_logger:info_msg("terminating~n"), ok. % 我们记录下来,并回复 ok.code_change(_OldVsn, State, _Extra) -> {ok, State}. % 升或降级的发布时更新内部状态%% ------------------------------------------------------------------%% 内部函数定义%% ------------------------------------------------------------------% 还没有呢。</code></pre><p>进入应用程序目录(前面提到的 <code>src</code> 的上级)并运行 <code>./rebar compile</code>,或者,你还没有用 rebar 的话,运行 <code>erlc -o ebin scr/*.erl</code>,就可以编译这个 OTP 服务器了。rebar 会自动创建 <code>ebin</code> 目录,用 <code>erlc</code> 请先自行创建该目录。</p><p>来测试下这个 OTP 服务器,这是输出:</p><pre><code>$> erl -pa ebin # 以 -pa (path add,加入路径) ebin 为参启动 erl1> hello_server:start_link(). % 启动服务器{ok,<0.34.0>}2> hello_server:say_hello(). % 试试 say_hello/0 这个 API 函数Hellook3> hello_server:get_count(). % 观察服务器如何追踪状态14> hello_server:get_count().25> hello_server ! stop. % 发送带外信息=INFO REPORT=== % error_logger 的输出stopstop6> hello_server:stop(). % 调用 stop/0 API 函数=INFO REPORT=== % 这是 terminate 输出的信息terminatingok7> whereis(hello_server). % 进程已不存在undefined8></code></pre><p>从非 OTP 版本到 OTP 的 <code>gen_server</code>,予我们何益?</p><ul><li>首先,熟悉 <code>gen_server</code> 的开发者一眼就看出代码的组织形式,暴露的 API 和实现细节之处。这就是模式的力量。</li><li>三种不同的消息机制:同步、异步和带外。</li></ul><p>当然不止这些。如下记录所示,开启 SASL (System Application Support Libraries,系统程序支持库),然后强行崩溃,会收到非常有用的错误报告,这是非 OTP 版本所不具备的。</p><pre><code>1> application:start(sasl). % 开启 SASLok2>=PROGRESS REPORT==== % 为省版面我删了启动消息 application: sasl % 只保留了最后一条 started_at: nonode@nohost % SASL 起来了。2> hello_server:start_link(). % 启动服务器{ok,<0.44.0>}3> whereis(hello_server). % 检查是否已注册<0.44.0>4> gen_server:cast(hello_server, crash). % 来,崩了它=INFO REPORT==== 17-Dec-2012::11:00:56 === % 不错,gen_server 调用了 terminateterminating % 因此可以清理,关闭 sockets 等操作=ERROR REPORT==== 17-Dec-2012::11:00:56 === % 以下是 SASL 的报告** Generic server hello_server terminating % 只需用 OTP 就无偿获得** Last message in was {'$gen_cast',crash}** When Server state == {state,0}** Reason for termination == % 嗯嗯,这就是出错的地方** {function_clause,[{hello_server,handle_cast,[crash,{state,0}]}, {gen_server,handle_msg,5}, {proc_lib,init_p_do_apply,3}]}=CRASH REPORT==== 17-Dec-2012::11:00:56 === % 这里有更多环境信息 crasher: initial call: hello_server:init/1 pid: <0.44.0> registered_name: hello_server exception exit: {function_clause, [{hello_server,handle_cast,[crash,{state,0}]}, {gen_server,handle_msg,5}, {proc_lib,init_p_do_apply,3}]} in function gen_server:terminate/6 ancestors: [<0.32.0>] messages: [] links: [<0.32.0>] dictionary: [] trap_exit: false status: running heap_size: 377 stack_size: 24 reductions: 142 neighbours: neighbour: [{pid,<0.32.0>}, {registered_name,[]}, {initial_call,{erlang,apply,2}}, {current_function,{io,wait_io_mon_reply,2}}, {ancestors,[]}, {messages,[]}, {links,[<0.26.0>,<0.44.0>]}, {dictionary,[]}, {trap_exit,false}, {status,waiting}, {heap_size,2584}, {stack_size,30}, {reductions,6595}]** exception exit: function_clause in function hello_server:handle_cast/2 called as hello_server:handle_cast(crash,{state,0}) in call from gen_server:handle_msg/5 in call from proc_lib:init_p_do_apply/35> whereis(hello_server). % 进程虽崩溃了,undefined % 至少以上报告给我们一个清晰的原因</code></pre><h2 id="OTP_5">OTP 监督器</h2><p>这是 rebar 生成的监督器模版,并未改造。</p><pre class='erl'><code class='erl'>-module(hello_sup).-behaviour(supervisor).%% API-export([start_link/0]).%% 监督器回调-export([init/1]).%% 子监督器的宏助手-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).%% ===================================================================%% API 函数%% ===================================================================start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).%% ===================================================================%% 监督器回调%% ===================================================================init([]) -> {ok, { {one_for_one, 5, 10}, []} }.</code></pre><p>哇,这代码不长呢!哇,这到底是什么东东?待会儿我们就会细究然后把模版留的坑埋了,让它能配合 <code>gen_server</code>。尽管细节尚需捋顺,但大部分是我们已经掌握的了。</p><ul><li>这个模块实现了 OTP 监督器行为</li><li>里面有一个 <code>start_link/0</code> API 函数和行为回调函数 <code>init/1</code></li><li>整个模块里只有一次单一的函数调用:<code>supervisor:start_link/3</code></li><li>神奇作用的 <code>init/1</code> 返回一个元组</li></ul><p>好吧,<code>init/1</code> 是神奇所在。对比必须手动监视和重启服务器的非 OTP 版本,这里是一个纯宣言式的,尽管配置监督器容器的元组形式很是神秘。</p><p><code>-define(CHILD(I, Type),...)</code> 什么来头?这是一个宏,像之前的老熟脸 <code>-define(SERVER, ?MODULE).</code>,只不过这次是两个参数。在宏名前加上 <code>?</code>,给 <code>I</code> 和 <code>Type</code> 赋值即可使用该宏。例如,<code>?CHILD(hello_server, worker)</code> 会在编译时展开为 <code>{hello_server, {hello_server, start_link, []}, , permanent, 5000, worker, [hello_server]}</code>。但这段代码里还没有用这个宏的地方,该怎么调教它呢?</p><p>看看 Emacs 怎样帮我们生成<em>监督器</em>模版。先忽视其它的,现在我们只对 <code>init/1</code> 函数感兴趣。来试试看,<code>M-x tmm-menubar, E, S, 1</code>。<code>E</code> 表示 Erlang;S 表示骨架(Skeletons),1 选择生成监督器。这是结果:</p><pre class='erl'><code class='erl'>init([]) -> SupFlags = #{strategy => one_for_one, intensity => 1, period => 5}, AChild = #{id => 'AName', start => {'AModule', start_link, []}, restart => permanent, shutdown => 5000, type => worker, modules => ['AModule']}, {ok, {SupFlags, [AChild]}}.</code></pre><p>(注:此处译者根据 Emacs 25.3 有所调整)</p><p>这应比 rebar 生成的好理解。当然,至少在结构上,最终应返回同一种配置元组。把元组里 <code>AName</code> 和 <code>AModule</code> 替换为上面的 <code>hello_server</code> 并展开所有变量,会得到以下结构,同时我加上一些注释以便理解:</p><pre class='erl'><code class='erl'>{ok, % ok, 这些就是监督器的要求 { { % 监督器全局选项 one_for_one, % - 使用 one-for-one 重启策略 1000, % - 允许各个子进程 1000 次重启 3600 % - 在一个小时内 }, [ % 需监督的子进程列表 { % 在此只有一个 hello_server, % - 注册到 hello_server 名下 { % - 以下是查找和启动子进程的代码 hello_server, % * 模块叫 hello_server start_link, % * 需要调用的函数叫 start_link [] % * 这是默认参数列表 }, permanent, % - 子进程应持续运行,崩溃后重启 2000, % - 系统停止时,提供 2 秒时间让子进程进行清理,然后再杀掉 worker % - 另外,这个子进程是工作者,不是监督器 [hello_server] % - 这些是进程用到的模块 } ] }}.</code></pre><p>你可以自己决定使用哪种格式,rebar 的紧凑标记,emacs 的啰嗦模版或是上面这个展开的片段。作为练习,请实现监督器并编译。在测试前解释一下上面的参数。</p><ul><li><strong><code>one_for_one</code></strong>: 这个重启策略指示监督器只重启崩溃了的子进程。我们例子中只有一个,多个被监督的子进程则不会受到影响。用 <code>one_for_all</code> 重启策略,一旦有子进程崩溃了就会自动重启所有的子进程。为自治进程配置 <code>one_for_one</code>。除此之外,来自紧凑耦合子系统的进程,最好是配置 <code>one_for_all</code> 以重启整个子系统。访问 <a href='http://erlang.org/doc/man/supervisor.html'>http://erlang.org/doc/man/supervisor.html</a> 作深入了解,特别关注下 <code>simple_one_for_one</code>,它可以进程工厂的方式使用监督器。</li><li><strong><code>permanent</code></strong>: 告诉监督器子进程总是重启(受全局重启规则的影响)。除此之外还有 <code>temporary</code> 和 <code>transient</code>。<code>temporary</code> 进程永不重启,但 <code>transient</code> 碰到异常终止时(比如崩溃)会。在我们例子中 <code>permanent</code> 是合适的,但在进程工厂场合中,<code>temporary</code> 和 <code>transient</code> 可能会更般配。</li><li><strong><code>worker</code></strong>: 在此我们要监督 <code>gen_server</code> ——它是工作者。构建监督器层级也很普遍。设置 <code>supervisor</code> 则表明子进程也是监督器。这有助于高效管理子进程。</li></ul><p>来试试 OTP 监督者。</p><pre><code>1> hello_sup:start_link(). % 启动根监督器{ok,<0.34.0>}2> whereis(hello_server). % 确认子进程连带启动<0.35.0>3> hello_server:say_hello(). % 调用子进程 APIHellook4> hello_server:get_count(). % 检查是否跟以前一样有效15> gen_server:cast(hello_server, crash). % 崩掉工作者进程=INFO REPORT==== % 不错,terminate 还是被调用了terminating=ERROR REPORT==== 17-Dec-2012::13:55:51 ===** Generic server hello_server terminating** Last message in was {'$gen_cast',crash}** When Server state == {state,2}** Reason for termination == % 可以看到崩溃原因** {function_clause,[{hello_server,handle_cast,[crash,{state,2}]}, {gen_server,handle_msg,5}, {proc_lib,init_p_do_apply,3}]}ok6> whereis(hello_server). % 新的子进程已经启动<0.40.0>7> hello_server:get_count(). % 显然,状态是初始化过的08> hello_server:stop(). % 调用 stop 这个 API 函数=INFO REPORT===terminating % 看起来没毛病ok9> whereis(hello_server). % 监督器已开启另一个服务器<0.44.0>10></code></pre><p>这虽按照设计规则运行,但可能有些结果并不是想要的。<code>stop/0</code> 原本要停止进程,但监督器马上启动了另一个新实例。我们可以把重启参数更换为 <code>transient</code>, 但会留下一个无任何子进程的监督器。我觉得这没问题,比模模糊糊的结果好。因此让我们转移注意力到 <strong>OTP 应用程序</strong> 上,学习怎么整体上启动和停止应用程序。</p><h2 id="OTP_6">OTP 应用程序</h2><p>前面已说过一个通过 <code>application:start(sasl)</code> 运行应用程序的例子:SASL。如何把根监督器和服务器打包到一起,用 SASL 的方式来运行呢?</p><p>跟 <code>gen_server</code> 和 <code>supervisor</code> 的实现不同,OTP 应用程序的 <code>application</code> 需要两个文件:一个通常命名为 <code>应用名称.app</code> 的配置文件,和命名为 <code>应用名称 _app.erl</code> 的 Erlang 源文件。在我们的例子中就是:</p><ul><li><code>ebin/hello.app</code></li><li><code>src/hello_app.erl</code></li></ul><p>注意 <code>hello.app</code> 放在 <code>ebin</code> 目录中,显而易见,<code>.app</code> 文件告诉 Erlang 如何运行程序。通常不会分发源代码,只会保留编译好的 <code>.beam</code> 来发布程序,因此 <code>ebin</code> 才是 <code>.app</code> 归属。</p><p>作为 rebar 用户,你会发现多了一个前面提过的 <code>src/hello.app.src</code> 的文件,它是 <code>ebin/hello.app</code> 的模版,运行 <code>./rebar</code> 编译后生成。</p><p>这是 <code>rebar</code> 生成的 <code>hello_app.erl</code>,实现了 OTP <code>application</code> 行为,不用我说太多了吧:</p><pre class='erl'><code class='erl'>-module(hello_app).-behaviour(application).%% Application callbacks-export([start/2, stop/1]).%% ===================================================================%% Application callbacks%% ===================================================================start(_StartType, _StartArgs) -> hello_sup:start_link().stop(_State) -> ok.</code></pre><p>这是告诉 Erlang 如何启动应用程序的配置文件:</p><pre class='erl'><code class='erl'>{application,hello, [{description,[]}, {vsn,"1"}, {registered,[]}, {applications,[kernel,stdlib]}, {mod,{hello_app,[]}}, {env,[]}, {modules,[hello_app,hello_server,hello_sup]}]}.</code></pre><p>只是一个元组而已。第一个元素是 <code>application</code> 原子,第二是应用程序名称,接下来是键-值元组列表。使用 rebar 的话别忘了最后那个句点。简单看看键值对:</p><ul><li><code>description</code>: 描述,我们可以改成“OTP 教程的 hello 应用程序”</li><li><code>vsn</code>: 应用程序版本</li><li><code>registered</code>: 应用程序将之注册到其下的名称列表。一般来说只有跟监督器的名称。本例子中应加上 <code>hello.sup</code>,但并不是必须的。</li><li><code>applications</code>: 依赖的应用程序列表。所有的应用都依赖于 <code>kernel</code> 和 <code>stdlib</code> 启动和运行。试试把 <code>sasl</code> 也添进去,看看启动时会发生什么事。</li><li><code>mod</code>: 告诉 Erlang 是哪个模块包含 <code>application</code> 行为。</li><li><code>env</code>: 传给应用程序的环境变量列表。</li><li><code>modules</code>: 组成应用程序的模块列表。</li></ul><p>进入 Erlang shell,启动应用程序。</p><pre><code>1> application:start(hello).ok2> whereis(hello_sup).<0.37.0>3> whereis(hello_server).<0.38.0>4> hello_server:say_hello().Hellook5> application:stop(hello).=INFO REPORT==== 17-Dec-2012::15:37:47 === application: hello exited: stopped type: temporaryok6> whereis(hello_sup).undefined7> whereis(hello_server).undefined</code></pre><p>棒棒,只需名字就能启动和停止应用程序了。可好玩的事儿没完呢,还记得把非 OTP 服务器换成 <code>gen_server</code> 时,如何通过 SASL 自动得到更详尽的报告吗?下次试试先 <code>appmon:strat()</code>(译注:新版 Erlang 已用 <code>observer:start()</code> 取代)启动应用程序监控器,在运行 <code>hello</code> 点击 hello 按钮就会显示进程的层级。漂亮极了,你觉得呢?</p>%7C%E7%AD%AD(%81%B3%D6%0F8%9C(%25%A3%FD%17Erlang OTP 新手教程2018-03-20T10:00:00+08:00https://realazy.comRealazy<p>译自 <a href='http://www.cs.otago.ac.nz/staffpriv/ok/Joe-Hates-OO.htm'>http://www.cs.otago.ac.nz/staffpriv/ok/Joe-Hates-OO.htm</a>.</p><p>初识面向对象编程概念时,我总觉得哪儿不对劲,但又说不出来。面向对象编程一经面世就人气不衰(后面我会解释原因),批判它有点像「在教堂里念咒」(译注:我猜就是清风吹不起半点涟漪之意)。没有面向对象,你都不好意思说自己是门体面的语言。</p><p>随着 Erlang 进入大众视野,我们经常被人讨教「Erlang 是面向对象的吗」。显然,正确的答案是「绝对不是」。但我们不敢扯高嗓门,只好捏造一些机智的回答:不求甚解的话,你说是就是((某种程度上);观言察行,连书上小字也不放过的话,Erlang 并不面向对象。</p><p>说到这我想起了一件小事,巴黎第七届 IEEE Logic 编程会议上,一名后来担任 IBM 老大的家伙的讲演。在问到为何要给 IBM Prolog 语言添加了很多面向对象的扩展时,他回道:</p><p>“客户要,我们给。”</p><p>我还记得当时想:「多没脑子,不于心有愧,不反思,不问『这事对吗』……」</p><h2 id="">为何面向对象糟透了</h2><p>面向对象编程,我最大的不苟同得追溯到基础概念,我会列出这些概念,以及我异议的理由。</p><h3 id="">异议一:数据结构和函数不应绑到一起</h3><p>对象将函数和数据结构绑死到一个不可分割的单元中。我认为这是一个根本错误,因为函数和数据结构属于两个完全不同的世界:</p><ul><li>函数是干活的。它们有入有出。输入和输出是会被函数改变的数据结构。大多数语言中,函数是靠命令流构建的:「先干这茬然后那茬……」。要理解函数必须理解执行结果的顺序。(惰性函数式语言和逻辑语言这个限制较为宽松。)</li><li>数据结构,如其名。它们不干活,只昭告自己是什么。「理解」数据结构比「理解」函数要容易得多。</li></ul><p>函数是转换输入和输出的黑盒子。一旦理解了输入和输出,那么函数我就掌握了。<strong>但这并不等于说我能够编写这个函数</strong>。</p><p>人们对函数的认知,是它作为运算体系中的角色,职责是转换数据结构到另一种类型。</p><p><strong>函数和数据结构是风马牛,强行关在同一个笼子里根本就是错的。</strong></p><h3 id="">异议二:万物皆对象</h3><p>举个例子,「时间」。在面向对象语言中,「时间」必须是对象(在 Smalltalk 里,甚至数字“3”也是对象)。在非面向对象语言中,「时间」只是某个数据类型的实例。例如,Erlang 中时间有各种不同表达,但可以使用类型声明清晰明了地描述:</p><pre class='erlang'><code class='erlang'>-deftype day() = 1..31.-deftype month() = 1..12.-deftype year() = int().-deftype hour() = 1..24.-deftype minute() = 1..60.-deftype second() = 1..60.-deftype abstime() = {abstime,year(),month(),day(),hour(),min(),sec()}.-deftype hms() = {hms,hour(),min(),sec()}.…</code></pre><p>注意这些定义不属于任何一个特定的对象。它们无处不在,系统里的函数也可以操作这些表述时间的数据类型。</p><p>而且并没有相应的方法(译注:指对象里的 method)。</p><h3 id="">面向对象语言中,数据类型定义散落在天涯</h3><p>面向对象语言中,对象包养了数据类型定义,因此我不能在同一处一览众定义。Erlang 和 C 可以定义包含文件或数据字典,一个文件囊括所有;面向对象语言我却毫无办法,数据类型定义分散在各个对象中去了。</p><p>让我举个例子。假设我要设计一个全局的数据类型,「全局」是指整个系统可见。</p><p>Lisp 程序员已达成的一个长期共识是,尽量少全局数据类型及大量操作这些数据类型的小函数,相比大量全局数据类型及少数操作数据类型的函数,前者要比后者好。</p><p>链表、数组、哈希表,或更高级的东西如时间、日期、文件名。</p><p>要定义一个全局数据结构,面向对象语言要求我必须选择某个基类。所有其他欲使用这个数据结构的对象必须继承它。现在我要创建一个「时间」对象,它属于谁,在哪个对象里呢?</p><h3 id="">对象有私有状态</h3><p>状态是万恶之源。特别是,带副作用的函数,应该离远点。</p><p>虽然状态在编程语言中不得人心,但它充斥着整个现实世界。我很在意银行账户状态,在存取款后我要求账户状态的更新是正确的。</p><p>既然现实世界存在着状态,编程语言应提供什么样的功能来处理它呢?</p><ul><li>面向对象语言宣称“藏起来,不要把状态告诉程序员”。状态是隐藏的,只有调用函数才能把它呼唤出来。</li><li>传统编程语言(C,Pascal)称状态由语言本身的作用域规则来控制。</li><li>纯宣言式语言则表示,哪来的状态啊?系统全局状态进出所有函数。使用 monad(函数式编程语言)和 DCG(逻辑语言)等机制隐藏状态,程序员感觉「彷佛无关状态」,但如果有必要存取系统状态是完全可行的。</li></ul><p>面向对象语言在「对程序员隐藏状态」上的选择可能是最糟糕的。不暴露状态,不找办法解决状态的麻烦,藏起来万事大吉。</p><h2 id="">面向对象流行的原因</h2><ol><li>被认为容易学习</li><li>被认为容易复用代码</li><li>浮夸宣传</li><li>催生了新的软件产业</li></ol><p>1 和 2 我看不到证据。3 和 4 看似技术背后的驱动力。如果一门差劲的语言技术,刚好催生了一门解决自身问题的新产业,那么这真是赚大钱的好生意。</p><p>这才是面向对象编程背后真正的驱动力。</p>%D1N%90%9F%EA%86%C3B&?%C5-%A5%5E%FD%EFErlang 之父 Joe Armstrong 谈面向对象:糟心2018-03-11T10:00:00+08:00https://realazy.comRealazy<p>2012 年,我三十岁。</p><p>三十而立。很幸运,大家庭,小家庭,基本能承担。</p><p>2010 年辞去工作,居家开发 iOS 软件,在夫妻俩的努力下,终于在 2012 年中在南宁这座离家乡不远的城市买了房。在一无所有的时候,夫人跟了我,还带来一个可爱的女儿。总算对两个女人有了交代。</p><p><img src='/assets/posts/2013_01_01_momo.jpg' alt='默默' /></p><p>2013 年,要精心打造一款计划已久的软件,并藉此带来更稳定的收入。希望 2013 年财政更自由。</p><p>民科出身的我,当然还是得不断地巩固基础。2012 年曾计划的 Lisp 已经入门皮毛,希望 2013 年能用它写些有趣的玩意。</p>+2%10%8B%1A%A0x%18@%22%F3%04M%98;/一个男人的三十岁2013-01-01T10:00:00+08:00https://realazy.comRealazy<p>这是 2011 年最大的收获。感谢老婆。</p><p><img src='/assets/posts/2012_01_27_momo.jpg' alt='默默' /></p><p>我非常庆幸我能陪着她成长。感谢 App Store 提供的机会。</p><p>2012,继续居家抱娃陪老婆,做一个翻得了墙下得了厨房写得了代码的全职奶爸。</p><p>今年的目标,创造一个有那么点牛逼的应用程序,熟悉 Lisp.</p>%D78;%C5%D8%22%C2i%03%5B%02%60)%82%0C%A420122012-01-27T10:00:00+08:00https://realazy.comRealazy<p>2003 年,我从网吧淘回了属于自己的第一台电脑,每天安装十数个 Linux 发行版,自此开启了非 Windows 操作系统的旅程。作为 Linux 菜鸟,最爱折腾的是美化桌面。不知为何,我最在意的是字体的呈现效果,以各种 Mac OS X 的截图作为参照,常常赞叹她能把字体表现得如此优美。这大概就是我跟苹果结缘的肇始。</p><p>2006 年,我开始学习编程。其时流行 Ruby on Rails. 大家知道,RoR 的很多示例视屏都是以 Mac OS X 上的一个编辑器 TextMate 来演示的。第一次看到代码编辑器可以如此好用,亦见识了 Mac OS X 操作系统优雅的介面。自此将购买 MacBook 作为自己的一个目标,在 2008 年 2 月份实现了。拿到机器以后,觉得不为它开发些软件实是人生遗憾,于是开始学习 Cocoa, 并在 2009 年初发布了一款很烂的饭否客户端 <a href='http://code.google.com/p/cocoafan/'>CocoaFan</a>. 随后,我又给饭否写了一个 iOS 客户端。</p><p>自由职业一直是我的梦想,2010 年初,我决定离开服务两年多的饭否,迈上独立开发者之路。作为「标准」的程序员,我不擅长销售,但得益于 App Store, 我只需专注我的开发工作,每个月坐等比很多公司员工工资发放更准时的销售结算汇款。这一切,都仰赖乔布斯的创举——革命性的手机 iPhone 和 App Store.</p><p>今天,世界失去了你。</p><p>当这篇 blog 发布,我决定抹去伤悲,更加努力地工作和发挥创意,即便不能像你一样改变世界,但若能给使用各种苹果设备的人带来更多软件使用便利,亦已满足。</p><p>谢谢你,乔布斯!!!</p>%F1%8Fmw%08%03%CF%8Cy%93K%9A%91%F0%D6%13谢谢你,乔布斯2011-10-06T10:00:00+08:00https://realazy.comRealazy<p>年初发生两件大事:结婚(虽然还没空去注册领证)和辞职。</p><p>年末发生一件大事:老婆意外怀孕了。2011年夏天我升级成爸爸,嗯,也就是大叔了。</p><p>而今年也是我收获还凑合的一年,无论是经济上,还是我的编程技能上。自学通了很多计算机基础知识,终于能看通一些算法教材了。</p><p>大叔2011年计划的事情:</p><ol><li>争取不用上班,能在默默(嘿嘿,我宝贝的名,男女通用)出生前攒够一套南宁房产的首付。</li><li>学习设计,提高设计感。平时没事要练练素描。</li><li>继续学习计算机基础知识,深入算法,钻钻汇编。</li></ol><p>至于做奶爸,那已经是板上钉钉,不用计划的了。</p>%B2b%9E%0D%C7%5E%FE%82%BF%5E%AA%A2t%5D%C6%8020112010-12-29T10:00:00+08:00https://realazy.comRealazy<p><a href='http://www.w3.org/News/2009#item119'>为给 HTML 5 让路,W3C 解散了 XHTML 2.0 工作组</a>。这造成了对 XTHML 和 HTML 之间的很多误解,所以就有人画了漫画来解释(<a href='http://www.smashingmagazine.com/2009/07/29/misunderstanding-markup-xhtml-2-comic-strip/'>英文</a>,<a href='http://www.blueidea.com/tech/web/2009/6920.asp'>中文</a>)。为了避免更大的混乱,我就不多作评论了。</p><p>关于本书,可以参考一下我去年的<a href='/posts/2008-05-27-refactoring-html-review.html'>书评</a>。对于前端工程师来说,很多情况下都需要重新捡起以前的代码,不管是不是自己写的,都会面临抉择:重新来过还是基于现状?大部分情况下,后者是唯一选择。勿怨天尤人,重构是你的好朋友。即便是在全新的工作进程中,当你闻到坏坏味道时,也需要很多小型的重构。那么,到底什么是重构?这可不是前几年流行的《网站重构(Designing with Web Stardards)》那本书里说的「重构」,而是 “Refactor”,编程术语上的「重构」……说到底,还是得买一本才能了解透彻的,哈哈。</p><p>随着网页的程序化(就是 Web Application 越来越多了),眼观六路,耳听八方,前端工程师跟浏览器打交道,除了在掌握浏览器在展示页面上的知识,还需要有浏览器跟后台交互的基本知识,本书涉猎 HTTP 的基本要点,虽不深入但亦可管中窥豹了。</p><p>作为第一本在 HTML 领域探讨重构的《重构 HTML: 改善 Web 应用的设计》终于上市了,各大网上书店均已铺货。</p>%DA%A5m%3E%B3%D7%97j%05%CFwC%C9%BD/%E1《重构 HTML: 改善 Web 应用的设计》上市2009-08-04T10:00:00+08:00https://realazy.comRealazy<p>拥有 MacBook 已经一年半了吧,有机会接触 Mac 的编程环境,兴趣盎然,一发不可收拾,经过一年的 C 和 Objective-C 语言入门,今年初即开始一些最基本的 Mac 编程,先后做了<a href='http://code.google.com/p/cocoafan/'>可可饭</a>和<a href='http://realazy.com/fanfouapp'>饭否 iPhone 客户端</a>。</p><p>有一天想用 iPhone 看一本只有 CHM 格式的电子书(Quartz 2D Graphics for Mac OS X's Developers),发现 App Store 存在两款软件,试用其中一款,觉得非常不满意,甚至无法满足最基本的阅读需求,就产生了做一个更好的 CHM 阅读器的念头。满足自己的需求,也希望藉此赚点零花钱买个 iPhone 3GS.</p><p>于是有了 <a href='http://chmate.com'>CHMate</a>, 今天已经可在 App Store 安装,定价 $4.99. 如果有机会对比一下 CHMate 跟其他 iPhone CHM 阅读器,你会发现 $4.99 不会花得冤枉。根据我的老本行积攒的知识,我可以对 CHM 文件内的 HTML 做到最适合 iPhone 的屏幕阅读。CHMate 的优势是:对文档进行了重排,原来文档内的样式,除了那些依附在标签(tag)上的,基本上都被重写优化了,而且通过设置让你有控制样式的机会。当前尚未有同类产品有这个功能,这是我作为一个前端开发者所引以自豪的一件事情。</p>U%81%95%8C%B6*.%F8%10%02%DD%D0%EB%F5%16%19CHMate 上线2009-07-31T10:00:00+08:00https://realazy.comRealazy<p>入行已久,做的领域也从浏览器扩展到桌面端甚至是手机端,对 Web 标准多少有些自己的看法,今日斗胆一说。</p><h2 id="">两种家</h2><p>我们困惑不解、迷惑不安,很大程度上源于没有指导思想。要摆正自己的位置,我们究竟是想做科学家,还是想做工程师。简明扼要,科学家经常要问「为什么」,他们关心了解人类不懂的知识;工程师则利用科学家发现的知识,制造对人类有用的物体或工具。前者研究,后者实战。很明显,我们大多数人属于工程师,W3C 那一群才是科学家。端正自己的态度,很多疑问就会迎刃而解。</p><h2 id="">两种用法</h2><p>HTML 生为标记语言,是组织文档的一种格式。随着技术和社会的不断进步,HTML 的用途也逐渐升级。今天它不仅出现在浏览器上(普通网页),它还出现在桌面程序上(Adobe AIR),出现在手机程序上(PalmPre WebOS);它不仅用来展示网页,也用来构建程序的用户界面。Web 标准要求我们,HTML 必须有良好的语义化,对于展示内容的文档来说,这是毋庸置疑的,但对于只是作为构建用户界面的程序来说,强调语义是没有多大意义的。要注重语义的时候一定不能松懈,只是用户界面而已的话,怎么方便怎么来,利用最方便的手段做最适合的布局。</p><h2 id="">实用主义的前提</h2><p>工程师信奉的是实用主义,但不等于可以放弃原则和规范。工程师关键任务是在遵守规范的前提下,发现、理解并结合实际的局限来达到满意的结果。作为一个流量巨大的网站,Google 对待 HTML 的态度是一个非常好的例子,省略 <code></body></code> 和 <code></html></code> 的做法我们何曾想过呢?但这却是符合 HTML 4 规范的。详见: <a href='http://code.google.com/speed/articles/optimizing-html.html'>http://code.google.com/speed/articles/optimizing-html.html</a>(需自行翻墙)。</p>%BC%80%93%F3%E26*%F4.%FB%81%17%086%CAL工程师与科学家2009-06-29T10:00:00+08:00https://realazy.comRealazy<p>最近总算有精力投入到 Cocoa 的学习了,做了一个饭否专用的客户端,名曰可可饭,倒腾了一个多月算是有了一个基本可用的版本:<a href='http://code.google.com/p/cocoafan/'>http://code.google.com/p/cocoafan/</a>.</p><p>门还在入,欢迎各界人士多多指教。</p>%E3%FF%98aS%AA%84-3Dy%FF%ED%60%3EtCocoaFan alpha12009-03-31T10:00:00+08:00https://realazy.comRealazy<p>C 可能是世界上最简洁且功能强大的语言,同为 C 的超集(C++现在可能不认为自己是 C 的超集或者相反,我们姑且如此认为吧,至少历史上有过这样的共识),Objective-C 比 C++ 要简洁得多。Objective, 顾名思义,对象者也,为不提供面向对象编程基本功能的 C 添加面向对象的支持,但它不像 C++ 繁冗复杂,最大限度上保持 C 的简洁性。</p><p>Objective-C 不像 C 有国际标准,它和 C++ 一样不存在标准。目前主要是 Apple 在维护,最近衍生了 Objective-C 2.0。使用 Objective-C 的大户也当属 Apple, 因此 Apple 提供的库属于准标准库了。对于开发 Mac 内在观感(native)的 UI 程序来说,使用的是 Cocoa 这个框架,包含 Foundation 和 Application Kit (AppKit).</p><p>Objective-C 也使用头文件 (header files),后缀为 <code>.h</code>, 但使用 <code>.m</code>(即 message, 其他面向对象编程语言也叫 method),作为源文件的后缀。</p><p>Objective-C 引入了 <code>#import</code> 指令,它的作用不仅解决头文件重复包含的问题,而且只要引入框架的单个主头文件,就可以使用框架的所有功能。</p><p>使用和查看 Cocoa 提供的 API 时,通常会发现它们大部分以 <code>NS</code> 打头,它是 NeXTStep 的缩写,至于 NeXTStep 和 Apple 的渊薮,大家还是自己 Wikipedia 吧。这是因为 Objective-C 没有命名空间(namespace),为避免冲突而加上的。作为一个良好的编程习惯,开发者不应再以 NS 开头命名自己的类和函数等,通常的习惯是使用自己或公司名称的缩写(Realazy 是否可以缩写成 RZ? 呵呵)。</p><p>另外,Cocoa 的编程风格鼓励表达清晰而非含糊的命名,所以你会看到 Cocoa 中非常长的、极少看到缩写的类名或消息名。</p>%804%D4%F6%A1%7C%DC%FC%B7%CC%EA%1E%E6%8Bm%8FObjective-C 学习笔记(一)——简介2009-02-02T10:00:00+08:00https://realazy.comRealazy<p>最近利用 <a href='http://www.adobe.com/products/air/'>Adobe AIR</a> 做了一个饭否客户端:<a href='http://ifan.realazy.org/'>爱饭</a>,并将之<a href='http://code.google.com/p/ifan/'>开源</a>。使用 HTML, CSS 和 JavaScript 对着 API 文档照虎画猫,大概三个星期完工,有一些感想和总结。</p><ol><li>AIR 的开发对 Web 开发者非常友好,基本上不需要额外的程序知识了,甚至可以使用已有的 JS 库,爱饭就使用了 YUI。但是生成的程序有一通病,那就是占用内存高(爱饭在 Windows 下占用 40m 左右),而且不存在优化之说。做严肃的应用 AIR 还是上不了台面。很多时候觉得,打开一个 AIR 程序,其实就是打开了一个浏览器。</li><li>absolute 的 CSS 布局方式非常灵活,对窗口缩放这种情况具有非常好的适应性。使用 webkit 引擎的 AIR 对 absolute 完全支持。如果是 IE 这种支持残缺的引擎,那得费非常多的 JS 代码。在 AIR 下写 CSS 有一种莫名的快感。正好 24ways 上发布了一篇关于 absolute 方式布局的文章,免却了我的罗嗦,见:<a href='http://24ways.org/2008/absolute-columns'>Absolute Columns</a>。</li><li>AIR 对 Linux 的支持还是存在缺失,比如无法给窗口加阴影,看来是 Linux 下的 Flash 支持跟不上。</li></ol><p>完毕。</p>%93%84M%E6%FD%FE%0C%8A%97%C7%8A*%DDUU-AIR 的尝试2009-01-11T10:00:00+08:00https://realazy.comRealazy<p>异步操作数据的方式有两种常见的方式:<code>XMLHttpRequest</code> 和 <code>iframe</code>. 孰优孰劣在此我们不争论,只是想举一个例子说明在获取网片片段上,使用 <code>iframe</code> 有一个比 <code>XMLHttpRequest</code> 更易企及的好处。</p><p>Ajax 常见的一种使用方法是加载网页片段填充某个区域。假设我们要在网页 foo.com/index 上请求 foo.com/partial。我们假设 partial 就是 HTML,不涉及 JSON 或 XML 格式。在这种情况下:</p><ol><li>使用 <code>XMLHttpRequest</code> 直接访问 partial,获取 responseText 后赋予 index 页面上某个元素的 innerHTML 即可完成。partial 必须是一个纯粹的 HTML 片段(基于以上假设)。</li><li>在页面上创建一个隐藏的 iframe, 使用 iframe 的 src 请求 partial, partial 可以作为一个完整的页面,里面包含 JavaScript,由 partial 里的 JS 完成替换 index 上页面片段替换。</li></ol><p>第二种看起来更繁琐,但能给我们更多控制权。例如,假如用户直接访问 foo.com/partial(这种情况很容易发生,假设您是 unobtrusive 的拥护者,机会更大,例如需要点击网页上的链接更新某部分内容时,用户使用新窗口打开了改链接), 你希望她看到的是什么内容呢?</p><p>在第一种情况中,用户看到的是代码片段,绝大部分下没有任何样式,也没有任何额外提示,导致用户体验的下降。因为只是一个 HTML 片段,你什么事都干不了。</p><p>但在第二种情况下,用户看到内容可能也只是 HTML 片段,但这却是一个完整的页面,一个可以执行 JS 的完整页面。我们只需检查这个页面的 parent 对象有没有我们预设的值,就可以判断它是不是在 iframe 之内了,然后我们可以让它跳转到正常的页面。</p><p>Demo: <a href='http://realazy.com/lab/xhrvsiframe/'>http://realazy.com/lab/xhrvsiframe/</a></p>t%91%C3dA%AF%FD#Q%DA%E7%C8%EAW%B8R使用 iframe 获取网页片段的一个好处2008-11-30T10:00:00+08:00https://realazy.comRealazy<p>Firefox 3 有一个很让人讨厌的 bug:基于某种目的,在表单提交时 disable 掉提交按钮,通过后退键回到这个页面后,这个提交按钮的状态依旧保持为 <code>disabled</code> 的状态,重新载入(软硬刷新)也无法改变。</p><p>google 良久,从 <a href='https://developer.mozilla.org/En/Using_Firefox_1.5_caching'>https://developer.mozilla.org/En/Using_Firefox_1.5_caching</a> 中发现一个 <code>window.onpageshow</code> 事件,<code>window.onload</code> 事件无法在后退的页面中出发,但这个可以,所以解决方案就是它了。</p><pre class='js'><code class='js'>window.addEventListener( "pageshow", function (e) { // 重置你不需要 disabled 的按钮 }, false);</code></pre><p><strong>更新:</strong>网友岁月如歌的<a href='http://lifesinger.org/blog/?p=569'>解决方案</a>比我的方案简易和正宗多了:给提交按钮加上 <code>autocomplete="off"</code> 的属性。</p>ZR%5C%D4%96%8D%862%98%A3%CF%948i*%F3Firebox 3 后退后按钮 diasabled 状态不恢复的一个解决方案2008-10-27T10:00:00+08:00https://realazy.comRealazy<p>原文来自 <a href='http://cocoawithlove.com/2008/08/application-design-in-appkit.html'>Application Design in AppKit</a>.</p><p>This is a discussion of high-level application design in Cocoa that aims to explain the major class roles in an AppKit application and how they are connected. I'll show you much more detail than simply "Model-View-Controller" and I also give a specific example of how all the concepts apply to a real application.</p><p>这是一篇关于 Cocoa 高级应用程序设计的讨论,目的在于解释 AppKit 应用程序中的主类的作用,以及它们间的相互联系。我会向你展示比「模型-视图-控制器」更具体的细节,也会给出一个具体例子,展示这些概念是如何应用到一个真实的程序中去的。</p><h2 id="The-anecdote">The anecdote 轶事</h2><p>The other day, I was showing a friend how to program in Cocoa. She is a very good programmer but has never really programmed a user-application --- working almost exclusively on embedded and server applications.</p><p>不久前的一天,我向一位朋友介绍在 Cocoa 中如何编写程序。她是一位不错的程序员,但之前从没实战过用户交互的应用程序——几乎只编写嵌入式或服务器应用程序。</p><p>The experience reminded me that even good programmers can be unaware of basic design traits of user-applications which, while common to user-applications on all platforms, are not common to all programming.</p><p>这种经历告诉我,即使是好的程序员也不一定知道用户交互应用程序的设计的基本特性——虽然它在所有平台上的用户交互应用程序中非常普遍,但不见得在所有类型的编程都是常见的。</p><p>So even though it's more "novice" than my regular fare, it would appear that even simple topics can be useful to advanced programmers.</p><p>所以,即使这里讲到的比较菜,只是简单的主题,但对高级程序员也会很有帮助。</p><h2 id="A-starting-point">A starting point 开门见山</h2><p>"Model-View-Controller" is the term normally used to describe the structure of modern applications. Almost every discussion of application design begins with it and I guess I will too. It looks like this:</p><p>"模型-视图-控制器" 是用在描述现代应用程序结构的一个常见术语。几乎所有关于应用程序设计的讨论都会以它开头,我猜我也不能免俗。它大概是这个样子:</p><p><img src='/assets/posts/2008_10_10/ModelViewController.png' alt='MVC 示例图' /></p><p>Your document data (the "model") notifies the intermediary (the "controller") and it tells your user interface elements ("the view") to update. Going back in the other direction, user actions in the user-interface trigger notifications to the intermediary which modifies the data.</p><p>文档的数据(「模型」)通知中间件(「控制器」)并告诉用户界面元素(「视图」)更新。从反向来说,中间件响应用户界面中的用户动作,并对数据进行修改。</p><p>The rationale may not be immediately obvious. Let me explain it this way:</p><p>这个原理并不是那么浅显易懂。看我这样解释行不行:</p><p>User applications are big and complex --- even seemingly simple applications. To manage complexity, everything is compartmentalised. To keep the boundaries between compartments clean, connections between compartments should be simple and generic.</p><p>用户应用程序是大型和复杂的——看起来简单的应用程序也是如此。为了管理复杂性,所有东西都划分管理。为了能保持部件边界的清晰性,它们之间的联系应该是简单和通用的。</p><p>Obviously, you don't want the model and view to be the same thing because then, there is no separation at all and the application will be a tangled mess.</p><p>显然,你并不想把模型和视图搞成一样的东西,因为完全没有分离的话只会让应用程序一团糟。</p><p>Direct connections between model and view are normally frowned upon because it creates a situation where they must know too much about each other's internal state to interoperate. Instead, a controller object (which knows about connective state but little else) is used to keep the interaction simple and generic.</p><p>如果模型和视图之间的联系是直接的,要想实现互用,两者必须深入了解对方的内部状态,这是一个纠葛的过程。反之,使用一个控制器对象(它能掌握大部分联系状态,其他的基本不管)来管理模型和视图的交互,就简单和通用多了。</p><h2 id="Better-than-Model-View-Controller">Better than Model-View-Controller 比模型-视图-控制器更胜一筹</h2><p>These traits of Model-View-Controller are all good things but in reality, it says little about how to assemble an application. Real applications have many more traits in common than a separation between model and view.</p><p>模型-视图-控制器的原理非常好,但在实际操作中,它基本上没有指明应用程序该如何组织。真实的应用程序有着比模型和视图的分离更多的通用特性。</p><p>A more complete diagram of a typical application's design would look like this:</p><p>这是一个更完整的流程图,典型的应用程序设计更应如此:</p><p><img src='/assets/posts/2008_10_10/DetailedAppDesign.png' alt='App design detail 示例图' /></p><p>In this diagram, solid black arrows indicate construction and hierarchic ownership. Feint gray arrows indicate communication in response to changes.</p><p>在此图中,黑色的实心箭头表示结构和层级属主;灰色空心箭头表示对变化作出响应的通讯。</p><h3 id="Application-instance">Application instance 应用程序实例</h3><p>The application instance incorporates the program entry point and the event loop (which handles all user events like mouse and keyboard actions). As the starting point of the program, the application instance constructs the other top-level objects in the program.</p><p>应用程序实例包含了程序入口的指针和事件循环(它处理所有的用户事件,如鼠标和键盘事件)。作为程序的起点,应用程序实例建立了程序中其他顶层的对象。</p><h3 id="Application-controllers">Application controllers 应用程序控制器</h3><p>The term "Application User Interface" is used in this part of the diagram to refer to elements of the user-interface that are not part of the document or the main window.</p><p>此图中的术语「应用程序用户界面」用于非文档或主窗口的用户界面元素。</p><p>These objects are constructed by the application at startup. They should only handle things which exist before the main window or main document is open or which fall outside the bounds of these areas. Example behavior here includes the application preferences window and the Mac OS X menu bar.</p><p>这些对象在应用程序启动时就构建。它们应该只处理出现在主窗口之前,开启主文档之前或超出这些范围边界的东西。这种行为的例子包括应用程序偏好设置,以及 Mac OS X 的菜单栏。</p><h3 id="Document-instance">Document instance 文档实例</h3><p>This is the first point where a programmer begins to exercise control over the program's behavior. The document loads or constructs the program's data and constructs the windows to show it.</p><p>这是程序员控制程序行为的起点,文档载入或构建程序数据,并构建函待显示的窗口。</p><p>A common mistake is to think that your program doesn't have a "document" so you shouldn't model a document class. In reality, if a program does anything then it is changing some piece of data (a preference file, a set of objects for rendering in OpenGL, the result of a calculation). You should design your program with this piece of data as the document. Even if your program only has one window, even if it only works with the same piece of data, even if you aren't writing a "Cocoa Document-based Application"; you should always have a class at the heart of your program which can be called "the current document".</p><p>认为程序没有「文档」而不应把文档类模型化,这是一个常见的错误。实际上,程序只要干了活就会改变某些数据(比如偏好设置文件、OpenGL 中的渲染对象集合、计算的结果等等)。设计程序时应该跟文档一样考虑这些数据。你的程序即使只有一个窗口,即使以相同的数据运行,甚至你编写的不是"Cocoa 基于文档的应用程序",心中都应该怀有这样一个类——你可以称之为「当前文档」。</p><h3 id="Window-controllers">Window controllers 窗口控制器</h3><p>A window controller is the class responsible for loading a window and putting it on screen. The window controller is responsible for giving context to the views and controls within the window, connecting them to data controllers which will provide them with data.</p><p>窗口控制器是负责把窗口载入屏幕的类,负责给窗口内的视图和控制器提供上下文,连接到提供数据的数据控制器上。</p><p>It is common for window controllers to double as data controllers for some functions since the window controller knows the state required to make the connection. This is not a bad thing in itself but should be resisted in the long term since it leads to bloat in the window controller (which often has a lot of work to do already). Generic, data-specific controllers should be used for this task.</p><p>窗口控制器在一些函数上比数据控制器多一倍,这种情况是常见的,因为它需要知道产生连接的状态。这不是什么坏事,但从长远来说应该要抵制,因为它会导致窗口控制器自身的膨胀(它早已排满了各种任务)。一般来说,应该使用特定数据的控制器完成这些任务。</p><h3 id="User-Interface-Elements">User Interface Elements 用户界面元素</h3><p>Where possible, these should be generic elements: buttons, text display, image display. They end up performing specific actions when connected (through controllers) to their contextually supplied data.</p><p>可能的话,用户界面元素通常包括:按钮、文本显示和图片显示等。(通过控制器)连接相应的数据时,它们最终会执行指定的行为。</p><p>User interface elements are normally hierarchic. The screen contains windows; windows contain views; views contain subviews. One window is normally in front (main window) and one view within this window is normally the focus. The application's "event loop" will send keyboard actions, mouse events and menu selections to this focus object. Unhandled events get passed up through the hierarchy so that parents can handle events that their children don't handle.</p><p>用户界面元素通常是分级的。屏幕包含窗口;窗口包含视图;视图包含次级视图。窗口一般在最前(主窗口),这个窗口内视图一般也会聚焦。应用程序的「事件循环」会给这个聚焦的对象发送键盘键盘动作、鼠标事件和菜单选择等行为。未处理的事件可以跨越层级,所以父层能够处理子层未能处理的事件。</p><p>The handling of events should be managed as low in the hierarchy as possible. Again, consolidation in parents leads to bloat. Even "small" applications can become very big.</p><p>事件应该尽可能在低的层级上处理。父层事件的集中会导致应用程序的膨胀。就算是「小」的应用程序也能变得很大。</p><h3 id="Data-Controllers">Data Controllers 数据控制器</h3><p>These should be as generic as possible. Their purpose should be to relay information from a source to a destination about data changes.</p><p>数据控制器应该尽可能通用化。它的目的应是分发从源到目标中关于数据变化的信息。</p><p>The simplest manifestation of a data controller is for a third-party to establish or enable the Observer design pattern between two objects.</p><p>数据控制器最普遍的表现是为第三方建立或启用两个对象之间的<a href='http://cocoawithlove.com/2008/06/five-approaches-to-listening-observing.html'>观察者设计模式</a>。</p><p>The worst approach (sometimes called an anti-pattern) is an all encompassing arbiter object that receives every change request the program makes, performs the change and then updates everything that needs to be updated. This approach is unsustainable on an application-wide scale. Decomposition is key --- data controllers should have small, focussed scope.</p><p>最差劲的方法(有时被称为反模式)是使用一个包办一切的对象接收程序产生的一切变化请求,执行变化然后更新所有需要更新的东西。这种方法无法支撑应用程序级别的扩展性。分解才是关键——数据控制器应该是一个小型的、集中的范围。</p><h2 id="An-example-application">An example application 一个实例</h2><p>Now we'll look quickly at what this means in an AppKit-based application. This application is a simple program that creates and edits lists of names. I know that's a pretty trivial thing for a program to do but the example must be simple so I can describe it here properly.</p><p>现在我们快速浏览一下它们在基于 AppKit 的应用程序中的含义。这是一个简单的应用程序,用以创建和编辑名字清单。我知道对程序来说这是在微不足道,但为了表述清晰才让它必须简单的。</p><p><img src='/assets/posts/2008_10_10/DetailedAppKitDesign.png' alt='应用程序流程图' /></p><blockquote><p>You can download the <a href='http://cocoawithlove.googlepages.com/NameListEditor.zip'>project described in this diagram</a>, although it isn't necessary to understand the discussion.</p><p>你可以下载<a href='http://cocoawithlove.googlepages.com/NameListEditor.zip'>这张图所描述的应用程序</a>,尽管对理解这个讨论而言不是必要的。</p></blockquote><p>The application object is an unmodified NSApplication. This will almost always be the case in any Cocoa Application. You can achieve most customisation of the NSApplication object through data (in the Info.plist file) or by attaching an application delegate object (which can intercept control at predetermined points). The application instance handles our startup, event loop and contruction of documents (I have discussed how a Cocoa application loads in a previous post).</p><p>这个应用程序对象是一个未经修改的 NSApplication, 在 Cocoa 应用程序中几乎都是这种情形。你可以通过数据(在 Info.plist 文件中)或附加一个应用程序代理对象(它可以侦听特定情况下的控制)实现 NSApplication 的大部分定制。这个应用程序实例处理我们的启动、事件循环和文档的构建(我在<a href='http://cocoawithlove.com/2008/03/cocoa-application-startup.html'>前一篇 blog</a> 中讨论了 Cocoa 应用程序是如何载入的)。</p><p>This application doesn't have any preferences or significant data outside the scope of the document, so the "Application Controllers" section just has the Main Menu in it.</p><p>这个应用程序没有偏好设置,也没有超出文档的重要数据,所以「应用程序控制器」部分只有一个主菜单。</p><p>Documents in AppKit act as both a data controller for the data (in this case, an array of strings) and the window controller for the main document window. The document handles saving and reading to and from any file on disk. This could be done with basic NSKeyedArchiver methods to turn an array of strings into an NSData object for writing to disk. The window is loaded automatically from the window NIB file (specified in the program's Info.plist file).</p><p>AppKit 中的文档不仅作为数据(在这个例子中是字符串数组)的数据控制器,还充当主文档窗口的窗口控制器。这个文档还处理磁盘上文件的读写,是通过基本的 NSKeyedArchiver 方法把字符串数组转为可写入磁盘的 NSData 对象实现的,而窗口是从窗口 NIB 文件(在程序的 Info.plist 文件中指定)自动载入的。</p><p>The NIB file for the document contains an NSArrayController which is connected to the list of names from the document via the appropriate keyPath. This allows the NSArrayController to issue key value observing notifications when it changes the array and similarly allows it to update automatically when something else changes the array on the document.</p><p>文档的 NIB 文件含有一个 NSArrayController, 它通过合适的 keyPath 从文档连接到名字的清单。这让 NSArrayController 在改变数组的时候可以发出侦听键值的通知,或类似地在其它东西改变文档数组时也允许它自动更新。</p><p>The NIB file for the document also contains the window, which in turn contains an NSTableView. The NIB file specifies that the NSTableView's only column (displayed using the NSTextCell) should get its data from the NSArrayController. In this way, the table is updated to display the list of names contained in the document.</p><p>文档的 NIB 文件也含有依次包含 NSTableView 的窗口。NIB 文件规定 NSTableView 唯一的栏(使用 NSTextCell 来显示)应该从 NSArrayController 中获取数据。在这种情况下,文档内的表格作出更新以显示名字清单。</p><p>The NSTextCell displays the name for each row and allows editing. If an entry is changed in this way, notifications are sent back through the NSArrayController to the document.</p><p>NSTextCell 在每一行上显示名字,而且允许编辑。如果某个条目这种方式下改变,NSArrayController 会把通知发送给文档。</p><p>Similarly, the "Add New Name" button can add a new object by communicating with the NSArrayController, asking it to create a new object and insert it in the array, which triggers all relevant change.</p><p>类似地,"Add New Name" 按钮通过跟 NSArrayController 的通讯可以添加一个新对象,要求它创建并插入到数组中,这会触发所有相关变化的传播。</p><h2 id="Conclusion">Conclusion 总结</h2><p>All of this may seem like a lot of work --- setting up connections and controllers and notifications. When starting a new program, you may think that many of these elements don't apply to you. Be careful -- don't chase false simplicity.</p><p>所有这些需要的工作量看起来很大——设置连接、控制器和通知。当开始一个新的程序,你可能认为这些元素的大部分都不适用。小心——不要追求失败的简洁性。</p><p>Remember, Cocoa was written to make the approach described in this article easier than the alternatives. Classes like NSArrayController and protocols like NSKeyValueObserving and NSKeyValueBindingCreation make connecting large amounts of data as simple as point and click in Interface Builder. In many cases, it ends up being faster than manually connecting a button or text field directly to a method on your document class.</p><p>记住,对于本文描述的各种方法,Cocoa 是本着更方便而非其他目的而生的。诸如 NSArrayController 等类、诸如 NSKeyValueObserving 和 NSKeyValueBindingCreation 等协议,让大量数据的连接跟 Interface Builder 中的指向和点击一样简单。</p><p>You will always have change behaviors that cannot be connected using these generic objects but following the same structural patterns that they use will keep your application clean and make it work better within Cocoa.</p><p>你总会碰到不能使用这些通用对象连接的变化行为,但遵循这些它们用到的相同结构模式会让你的应用程序更清晰,也能更好地运行在 Cocoa 下。</p>%F6%BE%E0%E0%1F%99t%D9%B8H%B6J%E8Tr%87AppKit 应用程序设计观2008-10-10T10:00:00+08:00https://realazy.comRealazy<p>长话短说,看这个 <code>form</code> 元素:</p><pre class='html'><code class='html'><form method="post" action="_some_action_uri_" id="_form_id_"> <input type="hidden" name="method" value="1" /></form></code></pre><p>试想一下,使用 <code>document.getElementById('_form_id_').getAttribute('method')</code> 会出现什么情况。Firefox 3, Safari 3, Opera 9.5 都会得到预期 "post", 但是 IE 6 和 7 就没有那么幸运了,得到的是一个 object: 其实就是 <code><input type="hidden" name="method" value="1" /></code> 这个元素。</p><p>因此,为避免混淆和挽救 IE,最好是,as the title.</p>%D0%1F%95%1A2%01b%E8%80%82%C9%94%B5%CBv%9Cform 元素内的字段 name 不要跟 form 属性名称一致2008-10-08T10:00:00+08:00https://realazy.comRealazy<h2 id="">问题</h2><p>一个已经有内容的 <code>textarea</code> 元素,在执行该元素的 <code>.focus()</code> 方法后,不同的浏览器有不同表现。我们的预期是能够出现在内容后面,但只有 gecko 浏览器能做到。</p><h2 id="">修复</h2><p>注意:这个函数不能直接运行,函数内的 isIE, isOpera 和 isWebkit 需要你的库提供或你编写,这并不难,对吧。</p><pre class='js'><code class='js'>function fixTextareaFocusCursorPosition(elTextarea){ if (isIE || isOpera){ var rng = elTextarea.createTextRange(); rng.text = elTextarea.value; rng.collapse(false); } else if (isWebkit) { elTextarea.select(); window.getSelection().collapseToEnd(); }}</code></pre>C%90%22%98b%F5%E6X%A0%BEB%ED%AB%07%8EZfocus 进 textarea 元素后光标位置的修复2008-09-10T10:00:00+08:00https://realazy.comRealazy<p>一般情况下,访问或设置剪贴板,IE 只需使用 <code>window.clipboardData</code> 的 <code>getData</code> 或 <code>setData</code> 方法即可。Mozilla 家族的浏览器(如 Firefox)则比较麻烦,不仅开发者需要写一沱代码,用户也需要主动配合(就是需要设置允许访问剪贴板)才可以(参考 <a href='http://developer.mozilla.org/En/Using_the_Clipboard'>Using the Clipboard</a>),以致几不可用。至于 Opera 则根本不提供剪贴板,Safari 可以在 onpaste 等非 Dom 事件中访问剪贴板(参考 <a href='http://developer.apple.com/documentation/AppleApplications/Conceptual/SafariJSProgTopics/Tasks/CopyAndPaste.html'>Using the Pasteboard From JavaScript</a>)。</p><p>中国特色的网站上有一个很中国特色的应用就是,在一个输入框 focus 时自动帮你把内容复制到了剪贴板中。老实说访问剪贴板是个不安全的操作,因此即使是 IE, Windows 在后来的升级中都加入是否允许访问剪贴板的提醒。如果能够做到跨浏览器的「邪恶地悄无声息」地实现中国特色的剪贴板应用,确实是个不小的挑战。</p><p>遗憾的是老外在 2006 年就帮我们做到了:使用 Flash。参考 <a href='http://www.jeffothy.com/weblog/clipboard-copy/'>Clipboard Copy</a>. 原版没有考虑不安装或禁止 Flash 的情况,我做了一个小改进:</p><pre class='js'><code class='js'>function copy(inElement) { var get = function (id) { return document.getElementById(id); }, elId = "flashcopier", embedId = "flashembed"; if (!get(elId)) { var divholder = Document.createElement("div"); divholder.setAttribute("id", elId); document.body.appendChild(divholder); } var divholder = get(elId); divholder.innerHTML = '<embed src="http://static.hainei.com/swf/cp.swf"\ FlashVars="clipboard=' + encodeURIComponent(inElement.value) + '"\ width="0" height="0" type="application/x-shockwave-flash"\ id="' + embedId + '"></embed>'; // 检测是否安装了 Flash var flashObj = window[embedId] || document[embedId] || {}; if (!flashObj.SetVariable) { // 没有 flash try { return window.clipboardData.setData("Text", inElement.value); } catch (ex) { return false; } } return true;}</code></pre><p>原版是 GPL 的,这个版本也请爱咋咋用……</p>%1Dfy%E5%81%9E%FFJ%80%14%C0跨浏览器使用剪贴板2008-09-01T10:00:00+08:00https://realazy.comRealazy<p><a href='http://www.whatwg.org/specs/web-forms/current-work/'>Web Forms 2.0</a> 是一个很有意思的东东,是 <a href='http://www.whatwg.org/specs/web-apps/current-work/'>HTML 5</a> 的组成部分。它的目标是提升表单的使用性 (usability),基本上就是为 <code>input</code> 元素的 <code>type</code> 属性增加一些值,如 <code>type="email"</code>;还有一些新属性,如 <code>required</code>。根据 <code>type</code> 由浏览器实现各种功能。比如,<code><input type="email" required="required" /></code>,从字面上即可看出,这是一个必须填写,且格式是电子邮件的输入框。如果你用的是 Opera 9+, 猛击<a href='http://shwetankdixit.com/testpages/webforms2demo.htm'>这个例子</a>看看效果。</p><p>注意,这不需要任何 JavaScript,是浏览器内部实现的功能。很遗憾的是到目前为止只有 Opera 9+ 有部分实现,作为前端开发者,每天都在为表单验证、自动完成等提升表单用户体验的事情上拼了老命,重复发明轮子。好消息是,基本上这些都可以通过 JavaScript 来模拟实现,项目当然有人在做了:<a href='http://code.google.com/p/webforms2/'>webforms2</a>,不妨下载一试。</p>k9%EBs%E2%15%C8%F6%FF:%E3%8Cr%84%03%60Web Forms 2.02008-07-22T10:00:00+08:00https://realazy.comRealazy<p>是不是很烦每次注册网站或填写相关资料时都要重来一遍?其实会有很多自动填写工具能代劳。比如使用 Mac, 在 Safari 的表单中,它可以足够聪明帮你从帐户资料中查找并填写一些相应的字段。Opera 也有相关功能,不过资料设置是在浏览器内。</p><p>当然,它们是根据表单的字段名称进行猜测与匹配的。如何给表单字段起个好名字,以方便自动填写工具的匹配,就变得有必要起来。</p><p>既然说「标准」,那名字肯定不是乱取的。<a href='http://www.ietf.org/rfc/rfc3106'>RFC3106</a> 据此而生。比方说,用户 ID 使用 Ecom_User_ID, 密码使用 Ecom_User_Password 等等,你可以访问该页面查看更多的对应实例。虽然商业气息比较浓重,但不妨碍我们的使用,以及给用户带来的便利。</p>'%85%5C%7BN%AF%F0%BC%3E%14%AE%9F%7C%01%BF_使用标准的表单字段名2008-06-28T10:00:00+08:00https://realazy.comRealazy<p>很多年以前,面对上古时代遗留的 HTML 发出的腐臭,我捂住鼻子唉声叹气。刚练熟 web 标准的我,恨不得寝其尸食其肉,把一切推翻重来。但经理说,没有时间浪费在清理这些垃圾上,快给我把新的页面切了!可想而知,我是郁闷的。你也是,别装了,我知道。我常常处于崩溃边缘,作为一个有深度爱心和追求完美(这应该是所有程序员追求的品质)的 web 前端开发者来说,为何不许我为残障者着想,改善一些无障碍性?为何不许我把这些鸟 <code>b</code>(读者最好不要连起来念),鸟 <code>i</code>, 还有鸟 <code>u</code> 送上刑场?看着 W3C 校验器显示出的一串又一串的 erorr, 我灰心丧气,横眉冷对经理指,好像丫欠了我几十万块冥币,哦不,人民币……</p><p>皆因我不知道"<strong>重构</strong>"(refactoring)。</p><p>或许你第一次看到这个词,会欢呼雀跃,耶,「重构」,网站重构,一切重新再来?很明显,不是。相反,它的过程是逐步的,有时甚至是很微小的。作为一门在编程中的高级技术,重构是指「在不改变代码外在行为的前提下,对代码作出修改,以改进程序的内部结构」(《重构:改善既有代码的设计》,2003,中国电力出版社)。对于 HTML 来说,就是不改变 HTML 所表达意义的基础上,对 HTML 作出修改,以改进 HTML 的内部结构。是不是很简单?</p><p>读者都知道 web 标准的意义所在,很明显,重构的目的是改善既有的 HTML,向 web 标准进军。重点在于<strong>改善</strong>,而不是取代。在很多情况下,取代的代价远比改善大得多。如果才能做到最大化的投入产出比,很明显,答案是重构。</p><p>啰嗦这么多,到底怎么重构?特别是 HTML, 怎么重构啊,看着堆垃圾我就头痛,是不?很多公司的网站,我们可以看到,比如腾讯,雅虎的新推出的页面,都是基于 web 标准的了,但还是存在大量的旧页面,就算访问量巨大,也是纹丝不动。我相信,这很大程度上不是由于不想改,而是除了全盘推翻重头再来之外,实在找不到好的解决办法。重构是你的解药,宝贝。</p><p>由世界知名的 XML 专家 Elliotte Rusty Harold 所著的 <a href='http://www.amazon.com/Refactoring-HTML-Improving-Applications-Addison-Wesley/dp/0321503635/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1211817642&sr=8-1'>_Refactoring HTML: Improving the Design of Existing Web Applications_</a> 是你解药的配方。书中大谈特谈的章节不多,就第一章说说 who, what, why, when, where,其他章节都是说 how. how, yeah, 就是到底怎么重构的锦囊妙计。书中从编程世界中「窃取」被证明是可行的、成熟的技术,很多可能是 web 标准实践者闻所未闻的技术,结合到 HTML 来,创造行之有效的新技术,解决 HTML 特有的难题。比方说,你关心过 GET 和 POST 是什么吗?知道为何 Google 爬虫爬一爬,很多页面消失殆尽吗?(嗯嗯,老油条们,我知道你知道(绕吧?),我在这里问的是「页面仔」)。关心过自动化测试吗?什么,你还刀耕火种,写一行就在浏览器刷一下看效果?知道怎么样批量校验你的页面吗?不要告诉我你一页又一页的把上千个页面轮番轰炸 W3C Validator...</p><p>不知道?那怎么还浪费时间看什么书评,赶紧的打开书来看啊!噢哦,没有书?google 一下,有得下。英文看不懂?很吃力?噢哦,明年中文版就会上市,嗯嗯,译者就是我……(终于露出狰狞面目)……</p><p>本书是探讨 HTML 领域的重构,是对重构这门技术的又一创造性运行。它从工具入手(我们知道,工具是重构的重要辅助),然后逐一分析良构(well-formness)、合法性、布局、无障碍、web 应用程序和内容等等需要重构的方面,提出问题,讨论理据(motivation)并提供行之有效的解决方案(mechanics)。对于接触 web 标准较少的人来说,提供了一个百科全书式的的参考,而对于我的同行们,sure, 亲爱的页面仔们,不仅可以查漏补缺,亦可学习程序工业中的先进经验,提升自己的「工业」素养。总之,这不是一本束之高阁的书。</p><p>编写 HTML 不仅是一门技术,更是一门艺术。你会了解的,在读了本书之后。在此我很严肃地说。仅以此献给中国所有的页面仔,共勉。</p>%00%FBq%AA%F5%D1f%DCi'%04%A4%9F%B4j%CARefactoring HTML 书评2008-05-27T10:00:00+08:00https://realazy.comRealazy<p>富文本编辑器,Rich Text Editor, 简称 RTE, 它提供类似于 Microsoft Word 的编辑功能,容易被不会编写 HTML 的用户并需要设置各种文本格式的用户所喜爱。它的应用也越来越广泛。最先只有 IE 浏览器支持,其它浏览器相继跟进,在功能的丰富性来说,还是 IE 强些。虽然没有一个统一的标准,但对于最基本的功能,各浏览器提供的 API 基本一致,从而使编写一个跨浏览器的富文本编辑器成为可能。</p><p>在很多开发者看来,富文本编辑器的编写是一件很神秘或者复杂的事情。神秘倒没有,复杂的话,确实如此。但是它的基本原理并不复杂,入门也不难。今天我们的主题是讲述基本原理,并逐步演示一个简单富文本编辑器的产生。这是我在 <a href='http://www.d2forum.cn/'>D2</a> 上的一个分享内容,在台上的演讲效果不佳,固写下来,希望能够对感兴趣的读者有所帮助。</p><h2 id="">富文本编辑器的基本原理</h2><p>这个原理实在是太简单了!对于支持富文本编辑的浏览器来说,其实就是设置 <code>document</code> 的 <code>designMode</code> 属性为 <code>on</code> 后,再通过执行 <code>document.execCommand('commandName'[, UIFlag[, value]]) </code> 即可。<code>commandName</code> 和 <code>value</code> 可以在 <a href='http://msdn.microsoft.com/en-us/library/ms533049(VS.85).aspx'>MSDN 上</a> 和 <a href='http://developer.mozilla.org/en/docs/Rich-Text_Editing_in_Mozilla'>MDC 上</a> 找到,它们就是我们创建各种格式的命令,比方说,我们要加粗字体,执行 <code>document.execCommand('bold', false)</code> 即可。很简单是吧?但是值得注意的是,通常是选中了文本后才执行命令,被选中的文本才被格式化。对于未选中的文本进行这个命令,各浏览器有不同的处理方式,比方 IE 可能是对位于光标中的标签内容进行格式化,而其它浏览器不做任何处理,这超出本文的内容,不细述。同时需要注意的是,<code>UIFlag</code> 这个参数设置为 <code>true</code> 表示 display any user interface triggered by the command (if any), 在我们今天的教程中都是 <code>false</code>, 而 <code>value</code> 也只在某些 <code>commandName</code> 中才有,具体参考以上刚给出的两个链接。</p><p>为了不影响当前 <code>document</code>, 通常的做法是在页面中嵌入一个 <code>iframe</code> 元素,然后对这个 <code>iframe</code> 内的 <code>document</code>(通过 <code>iframe.contentWindow.document</code> 获得)进行操作。</p><p>十分简单,是吧?下面我们来动手做一个。</p><h2 id="">编写一个简单的富文本编辑器</h2><p>这个例子使用了 <a href='http://developer.yahoo.com/yui/'>YUI</a>. 即使你对它不是很熟悉也没有关系,我在这里只使用了它的 DOM 和 Event 的一些跨平台基本方法。</p><h3 id="">搭架</h3><p>在此强调一下很久未曾提及的 unobtrusive. 我们的编辑器是对 <code>textarea</code> 元素的一个增强(enhencement),就是说,即使 JavaScript 被禁用了,用户还可以通过 <code>textarea</code> 编辑内容。</p><p>在这个例子中,我们将使用 <code>YAHOO.realazy</code> 的命名空间,在之下实现一个 <code>RTE</code> 的类。我们今天的编辑器很简单,因此构造器(constructor) 的参数也只有 <code>textarea</code> 一个。我们使用一个实例变量来保存工具条的各个项目。实例初始化放到一个叫 <code>render</code> 的方法中。这一步的页面和代码见<a href='http://realazy.com/lab/rte/1.html'>第 1 步</a>。</p><h3 id="iframe-textarea">创建 <code>iframe</code> 并替换 <code>textarea</code></h3><p>搭好架子,正如我在前面所说,建立一个 <code>iframe</code>, 编辑器的所有操作都在 <code>iframe</code> 的 <code>document</code> 内执行。并且把 textarea 隐藏起来。从<a href='http://realazy.com/lab/rte/2.html'>第 2 步</a>中可以看到,我们已经有了一个 <code>iframe</code>, 但不能输入任何东西,很正常,我们没有打开它的 <code>designMode</code> 嘛。</p><h3 id="designMode">开启 <code>designMode</code></h3><p>这一步涉及的东西挺多,也是关键。我们会创建获取 <code>iframe</code> 的 <code>document</code> 的方法,并通过程序的方式向 <code>iframe</code> 写入空页而非使用一个外接的 blank.html. 我们使用一个类属性 <code>YAHOO.realazy.RTE.htmlContent</code> 来保存空页的 <code>html</code>. 在准备好一切后,就可以开启 <code>designMode</code> 了。页面和代码详见<a href='http://realazy.com/lab/rte/3.html'>第 3 步</a>。看,我们已经可以在 <code>iframe</code> 里输入东西了。</p><h3 id="">构建工具条</h3><p>我们需要操作的工具条!这样才可以控制 <code>iframe</code> 里的内容,才能称之为编辑器。在此我并不打算实现太多的功能,只是选择字形、字号、加粗、斜体、下划线、居左、居中、居右、超链接和插图作为示例。对于跨平台,<a href='http://www.mozilla.org/editor/midas-spec.html'>Mozilla Midas Specification</a> 是不错的参考。ok, 请看<a href='http://realazy.com/lab/rte/4.html'>第 4 步</a>,我们的工具条出来了,虽然很丑。我同时用 CSS 对 <code>iframe</code> 的宽度做出了一些调整。</p><h3 id="">给工具条加上事件</h3><p>嗯,工具条出来了,编辑器看起来也「人模狗样」了,你兴奋的点啊点,没什么效果……意料中嘛。我们接着给工具条绑定一些事件,让编辑器内容能够响应工具条。在这一步,我们把 <code>execCommand</code> 再封一层,前面说过,我们用不上 <code>UIFlag</code>,让它永远是 <code>false</code> 好了。好,有代码就有真相,请看<a href='http://realazy.com/lab/rte/5.html'>第 5 步</a>。如果是正使用 IE, 请先暂时转移到其它浏览器。看到了吧,工具条生效了!</p><h3 id="IE">解决 IE 的问题</h3><p>well, 如果你没有听我的劝告,依然使用 IE, 你会发现除了字型和字号其它的都不能用。为什么呢?你观察一下,有没有发现,其它浏览器选择文本后,再点击工具条上的项目,被选中的文本是否依然选中的?而 IE 呢,在点击工具条时,选中的文本马上失去选中的状态,所以它们就失败了。所以,如果我们能够保证点击工具条文本保持选中状态,就可以解决 IE 的问题了。</p><p>Microsoft 给 HTML 标签一个很奇怪的属性 <code>unselectable</code>, 只要设置为 <code>on</code>, 焦点不会转移到点击的元素上,从而保证文本的选中状态。</p><p>请看<a href='http://realazy.com/lab/rte/6.html'>第 6 步</a>。这也是解决 IE 头痛问题的关键所在。我曾经在这上面费了很大脑筋。</p><h2 id="">高级主题展望</h2><p>good, 看看我们现在的代码,224 行。相比其它动辄上万行的编辑器,你可能会觉得不可思议。因为我们这个最基本的编辑器,连 <code>selection</code> 都没有用到。很多很酷的效果,比如 Google Doc 里能够动态改变链接文本,使用页内层而非弹出的 <code>prompt</code> 来操作等高级功能,基本上都要用到 <code>TextRange</code> (IE) 或者 <code>Range</code> (W3C). 要命的是这两个东西互不兼容,只是相似而已。入门推荐看 PPK 的 <a href='http://www.quirksmode.org/dom/range_intro.html'>Introduction to Range</a>.</p><p>在此我们就不深入了,等我有时间我会总结一些奇技淫巧(呜呼,前端开发需要的奇技淫巧太多了,这不是好事情)出来。</p>%01%AFN%F7%98%B0%ED%18%F8%BA2%B1N%88s%9A富文本编辑器的基本原理与实践2008-05-02T10:00:00+08:00https://realazy.comRealazy<p><a href='http://en.wikipedia.org/wiki/Memoization'>Memoization</a> 是一种将函数返回值缓存起来的方法,在 Lisp, Ruby, Perl, Python 等语言中使用非常广泛。随着 Ajax 的兴起,客户端对服务器的请求越来越密集(经典如 autocomplete),如果有一个良好的缓存机制,那么客户端 JavaScript 程序的效率的提升是显而易见的。</p><p>Memoization 原理非常简单,就是把函数的每次执行结果都放入一个散列表中,在接下来的执行中,在散列表中查找是否已经有相应执行过的值,如果有,直接返回该值,没有才真正执行函数体的求值部分。很明显,找值,尤其是在散列中找值,比执行函数快多了。现代 JavaScript 的开发也已经大量使用这种技术。</p><p>我通过 Google<a href='http://www.google.com/search?hl=en&hs=y9z&q=JavaScript+Memoization'>寻找</a>了好几种 JavaScript Memoization 的实现,都不太如人愿,有的实现不能缓存递归函数,有的需要修改函数的 <code>prototype</code>,于是自己实现一个:</p><pre class='js'><code class='js'>/** * JavaScript Momoization * @param {string} func name of function / method * @param {object} [obj] mothed's object or scope correction object * * MIT / BSD license */function Memoize(func, obj) { var obj = obj || window, func = obj[func], cache = {}; return function () { var key = Array.prototype.join.call(arguments, ""); var key = Array.prototype.join.call(arguments, "_"); if (!(key in cache)) cache[key] = func.apply(obj, arguments); return cache[key]; };}</code></pre><p>并写了一个测试案例,空口无凭,让大家亲自看看 Memoization 的威力。</p><p>见:<a href='http://realazy.com/lab/memoization.html'>http://realazy.com/lab/memoization.html</a></p><p>另,例子中的 fibonacci 函数有很多更有效率的实现方法,在此我使用最无效率的递归实现,只是为了更直达地演示 memoization.</p><p>又,longwosion 留言所提到的 key 唯一性问题,我略作修正,但应该还有更好的办法,欢迎您留言探讨。</p>%BFY%BA%0B%98%C0e%01R%12%A1%C2%B3%AE%CA%9CJavaScript Memoization2008-04-22T10:00:00+08:00https://realazy.comRealazy<p>一不小心从某 blog 中第一时间发现 Google App Engine 发布,立马注册一个。当天晚上抽空看了看文档,做了做 hello world,第二天晚上开始写一个聊天室程序,第三天晚上拿出 <a href='http://www.oreilly.com.cn/book.php?bn=7-5641-0576-3'>Python 技术手册</a>,捣腾到今天才把一个简单的东西弄出来。去年学了一段时间的 Python, 还用 <a href='http://webpy.org'>webpy</a> 写了一个不成型的 blog 系统。某段时间认识自己不足,苦读了两个月的 C,后来买了 MacBoook, 又一头扎进 Objective-C 和 Cocoa, 哈哈哈,总之,一事无成吧。现在 GAE 出来了,突然发现一直寻找的 Python hosting 就这么从天上掉下来了,而且还是馅饼……重新激起 Python 兴趣,却发现又忘得差不多了……</p><p>废话太多了……这个测试 demo 叫 chatlazy, 位于 chatlazy.appspot.com(注:已无法访问)。是一个简易聊天室,后台部分,就是 Python 了,具体一点,是 webpy 0.3 (开发版,未发布)。机制十分简单,就是前端使用 JavaScript 隔 5 秒去提取后台的最新消息。有几个小细节还是值得总结一下的:</p><p>由于 GAE 的数据 ID 使不能用在 Gql 中的,我只能通过时间戳来比对消息状态。把 <code>datetime</code> 和秒数 + 毫秒数的互转,还是比较繁琐的。Python 技术手册帮了我很大忙。解决方案大致如此:</p><pre class='python'><code class='python'>str(time.mktime(d.timetuple()))[:-1] + str(d.microsecond)</code></pre><p>反过来则是:</p><pre class='python'><code class='python'>p = str(t).split('.')tp = time.localtime(float(p[0]))dt = datetime(tp[0], tp[1], tp[2], tp[3], tp[4], tp[5]+1, int(p[1]))</code></pre><p>对于 <code>iterable</code> 的对象, 先要 <code>list</code> 它转成列表,才可以使用 <code>reversed</code> 等相关方法。</p><p>需要取最新的 n 条信息,即数据库末尾的 n 条,但是又要顺序,可以先按逆序取 n 条,再反向排序(由此引发上条启示)。</p><p>对于任何用户输入的东西都要做过滤,一开始我在用户名那块忽略了,结果马上有人 XSS 了。这应该是基本常识,应铭记于心。</p><p>一定要处理异常。</p><p>由于 GAE 这个天上掉的馅饼,我想我近期的精力会放到 Python 上了,有计划地把 blog 迁徙到 GAE 上,并开发一些有趣地程序。GAE rocks. 老实说,这是搜索、Gmail 后,对我而言可以排到第三的 Google 服务了。</p>?%7D%A3%85%1C'%96%8E,%95d%AC%88%16%C1X测试 Google App Engine2008-04-11T10:00:00+08:00https://realazy.comRealazy<p>由 John Resig 的 <a href='http://ejohn.org/blog/how-javascript-timers-work/'>How JavaScript Timers Work</a> 可以知道,现有的 JavaScript 引擎是单线程处理任务的。它把任务放到队列中,不会同步去执行,必须在完成一个任务后才开始另外一个任务。</p><p>让我们看看我之前的文章:<a href='/posts/2007-08-20-nine-javascript-gotchas.html'>JavaScript 的 9 个陷阱及评点</a>,在第 9 点 Focus Pocus 中提到的问题。原作者对这个认识有所偏差,其实不只是 IE 的问题,而是现有 JavaScript 引擎对于线程实现的问题(关于线程,我的概念其实不多,如果不对,希望读者多多指教)。我们通过一个例子来说明,请访问 <a href='http://realazy.com/lab/settimeout.html'>http://realazy.com/lab/settimeout.html</a>. 我们来看 1 和 2。如果你能看看源代码,会发现我们的任务很简单,就是给文档增加一个 <code>input</code> 文本框,并聚焦和选中。请现在分别点击一下,可以看到,1 并没有能够聚焦和选中,而 2 可以。它们之间的区别在于,在执行</p><pre class='js'><code class='js'>input.focus();input.select();</code></pre><p>时, 2 多了一个延迟时间为 0 的 <code>setTimeout</code> 的外围函数,即:</p><pre class='js'><code class='js'>setTimeout(function () { input.focus(); input.select();}, 0);</code></pre><p>按照 JavaScript: The Definitive Guide 5th 的 14.1 所说:</p><blockquote><p>在实践中,<code>setTimeout</code> 会在其完成当前任何延宕事件的事件处理器的执行,以及完成文档当前状态更新后,告诉浏览器去启用 <code>setTimeout</code> 内注册的函数。</p></blockquote><p>其实,这是一个把需要执行的任务从队列中跳脱的技巧。回到前面的例子,JavaScript 引擎在执行 <code>onkeypress</code> 时,由于没有多线程的同步执行,不可能同时去处理刚创建元素的 <code>focus</code> 和 <code>select</code> 事件,由于这两个事件都不在队列中,在完成 <code>onkeypress</code> 后,JavaScript 引擎已经丢弃了这两个事件,正如你看到的例子 1 的情况。而在例子 2 中,由于 <code>setTimeout</code> 可以把任务从某个队列中跳脱成为新队列,因而能够得到期望的结果。</p><p>这才是延迟事件为 0 的 <code>setTimeout</code> 的真正目的。在此,你可以看看例子 3,它的任务是实时更新输入的文本,现在请试试,你会发现预览区域总是落后一拍,比如你输 a, 预览区并没有出现 a, 在紧接输入 b 时, a 才不慌不忙地出现。其实我们是有办法让预览区跟输入框同步地,在此我没有给出答案,因为上面所说的,就是解决思路,try it yourself!</p>%C1%F1%1DN%0C%B2%B4%E3~%12%8EV4%E8%03j认识延迟时间为 0 的 setTimeout2008-03-29T10:00:00+08:00https://realazy.comRealazy<p>去年跟 <a href='http://blog.jjgod.org'>jjgod</a> 一起翻译的书,<strong><a href='http://realazy.com/jspro'>《精通 JavaScript》</a></strong> 终于上市了。此书原名为 <a href='http://jspro.org/'>Pro JavaScript Techniques</a>, 系 <a href='http://jquery.com'>jQuery</a> 之父 <a href='http://ejohn.org'>John Resig</a> 所著。</p><p>这是一本没有 “hello world” 的书,在未翻译前我已经<a href='/posts/2007-01-03-pro-javascript-techniques-review.html'>推荐</a>过。希望窥探 JavaScript 高级应用的读者可以一读。推荐大家从网上购买,这样折扣多些。</p><p>如果你有任何想法,可以致信 projsch@gmail.com, 欢迎交流。</p><p>p.s. 最近更新不勤,一则工作忙,二则积蓄待发。自学 C 语言几月有余,略有小成。新近在看 Objective-C 和 Cocoa, 希望能开发些客户端程序。数据结构和算法依然在门外,一步一步来吧。God bless you, also me.</p>%9E%09%7F%AE%F2T/%B3%C1%1C%03%95%B2%1F%B1%F3《精通 JavaScript》上市2008-03-18T10:00:00+08:00https://realazy.comRealazy<p>如题,这是一个在北京的工作机会。</p><p><strong>工作职责:</strong></p><ul><li>使用 HTML/CSS/Javascript 开发符合 W3C 标准的网站前端页面;</li><li>使用 AJAX,Flash 等技术丰富网站功能,增强用户体验;</li><li>和后台工程师一起研讨技术实现方案,制定服务接口等;</li><li>积累并完善自己的前端 WEB 开发框架,Javascript 开发框架;</li><li>积极探索并积累前端开发模式和规范。</li></ul><p><strong>职位要求:</strong></p><ul><li>本科以上学历,能熟练阅读英文技术文档;</li><li>具备良好的团队合作精神和积极主动的沟通意识;</li><li>具有强烈的进取心和求知欲,勇于挑战;</li><li>精通 HTML/CSS/Javascript 等前端技术,习惯于手写符合 W3C 标准、兼容多种浏览器的前端页面代码,而不是依赖 IDE;</li><li>有 Flash,ActionScript 开发经验者优先;</li><li>有设计经验,熟悉 Photoshop、Illustrator 者优先;</li><li>计算机相关专业毕业者优先。</li></ul><p>有意者请致信 job(a)hainei.com.</p><p>另外还招聘产品经理、研发工程师和高级研发工程师/架构师。详情见 <a href='http://hainei.com/job'>http://hainei.com/job</a>.</p>4%F2%97%AC%F1%95%E3%C2y%EE=%EB%1Fw;t海内网招聘前端开发工程师2008-03-01T10:00:00+08:00https://realazy.comRealazy<p>按照维基的定义,"A bookmarklet is an applet, a small computer application, stored as the URL of a bookmark in a web browser or as a hyperlink on a web page". 最近,它在一些新兴的网站中比较流行,比如 facebook, friendfeed. 从技术角度来看,它通常是一段来自提供方的可执行 JavaScript, 用以捕获当前网页的某些元素如标题、图片等以加强当前网页与提供方网站之间的交互。</p><p>那么,从技术角度来说,bookmarklet 有什么需要注意的呢?我个人意见如下:</p><p>首先,因为它是一段 JavaScript, 所以应该遵循普世的 JavaScript 编程原则。最重要的一点是,不要污染当前网页的命名空间,否则可能会破坏当前网页的 JavaScript. 通常,可以使用<a href='http://www.jibbering.com/faq/faq_notes/closures.html'>闭包</a>来隐藏你所有的变量。同样,如果您的 bookmarklet 的 CSS 可能会入侵当前网页(很遗憾,CSS 没有命名空间,也没有类似闭包的东西,很容易就会冲突),那么请考虑将 bookmarklet 的内容放到 <code>iframe</code> 中去。</p><p>其次,防止函数执行后不经意的副作用,一个比较好用的贴士是,使用不返回值始终返回 <code>undefined</code> 的 <a href='http://developer.mozilla.org/en/docs/Core_JavaScript_1.5_Reference:Operators:Special_Operators:void_Operator'>`void`</a>, 它<strong>可以接受任何参数</strong>,因此,把你的闭包放到 <code>void</code> 中是个不错的主意:</p><pre class='js'><code class='js'>javascript:void((function(){...})());</code></pre><p>最后,有一个比较恼火的问题也需要加以注意。目前世界上最流行的浏览器,IE6, 它对 bookmarklet 所能容忍的长度仅为 <strong>508</strong>!</p>%8F%88%8E%FC%7Ba%85%98thyDI%CA%95%DEBookmarklet2008-02-25T10:00:00+08:00https://realazy.comRealazy<p>最近入手了一台 MacBook 061.</p><p>由于 Mac OS 身上淌着 Unix 的血液,要找到一些不用花钱的生产力工具还是很容易的。最近习惯了 GNU Emacs (以下简称 Emacs), 所以不管三七二十一,找一个来安装上再说。</p><p>才发现 Mac OS 下的 Emacs 版本如此之多。我分别尝试了 <a href='http://emacs-app.sourceforge.net/'>Emacs.app</a>, <a href='http://homepage.mac.com/zenitani/emacs-e.html'>Carbon Emacs</a> 和 <a href='http://aquamacs.org/'>Aquamacs</a>.</p><p>首先,Emacs.app 基于还在开发中的 Emacs 23,这个版本最大的改动之一就是字体的处理方式,无疑,在 Mac OS 下,三者中也是它的字体支持最好。Carbon Emacs 和 Aquamacs 都是基于 Emacs 22 的,对字体的支持不足(比如中英混排,或者对字型的选择都比较奇怪)。我对 Carbon Emacs 没有什么印象,倒是因为 Aquamacs 解决一个至关重要的问题,我才不得不选择它。</p><p>如果你用过 Emacs, 你就知道它是多么依赖于各种快捷键。问题在于,MacBook 键盘右边没有 control 键。当然可以通过设置来让 command 或者 option 来充当 control 键来解决问题,但是为了一个 Emacs 而改变整个系统的键盘布局方式,有点得不偿失。而 Aquamacs 可以使得这样的设置只在 Emacs 内生效而不影响系统:</p><pre class='lisp'><code class='lisp'>(setq mac-command-modifier 'control)(setq mac-control-modifier 'alt)(setq mac-option-modifier 'meta)</code></pre><p>p.s. 至于 Textmate, 先慢慢尝试一下吧。我的工作环境不可能不处理中文,Textmate 不支持中文严重降低了我去使用它的频度。</p><p>更新:后来又折腾一下 Emacs.app, 发现它其实也可以 remap 键位的。无疑,我马上把 Aquamacs 抛弃了。</p>%BB%9F%84%E9%1D%DFX%CB%E7+0%19%94H%95kMac OS 和 Emacs2008-01-27T10:00:00+08:00https://realazy.comRealazy<p>由于某些原因需要把函数直接放到 <code>img</code> 标签上的 <code>onload</code> 属性执行,比如:</p><p>For some reasons we have to execute the JavaScript function in the <code>img</code> tag's attribute <code>onload</code>, e.g.</p><pre class='html'><code class='html'><img onload="javascript:jsFunction();" ...</code></pre><p>开启 Opera, CPU 狂窜到 100%……</p><p>When using Opera browser, the CPU usage is up to 100%...</p><p>原来丫会重复执行 <code>jsFunction</code>……只好加个变量来记录是否已经执行。</p><p>Because Opera execute <code>jsFunction</code> repeatedly. The solution is adding an flag variable to track the function be executed or not.</p><p>Google 一下并没有发现这方面的资料,所以记下来,希望能帮助碰到这个问题的人。为了表现我的国际主义精神,特翻译了一下(至于这句,就不翻了……)。</p>%EE%15%B9%F3%1C%A0G%04%D3%B7%15%EF%89%07%E7%ECopera img onload 重复执行2008-01-09T10:00:00+08:00https://realazy.comRealazy<p>不经意,又到了一年一度的总结和展望时间了。</p><p>2006 末,在不懂 MySQL, Ruby 的情况下,用 RoR 折腾出一个十分简单的 jobz board, 向世界宣布我「会」编程了。侥幸进入中国雅虎(已在 8 月份离职),虽大部分时间还是折腾 HTML 和 CSS, 但也有了练习 JavaScript 的机会,怎么说,以前玩 linux 积累一些非编程但有助于了解编程的经验,在练习过程中得以入门编程,还写得真像那么回事了。</p><p>记得 2007 的展望是精通 JavaScript,这个完成得有 60% 吧,对 PHP 和 MySQL 也有一定的了解。至于 Ruby,没地方用,所以顶多掌握了一些概念和语法,倒是 Python,用得还多点,年末用 <a href='http://webpy.org'>webpy</a> 和 SQLite3 写了一个 blog 程序,算是对 Python 入了点门。</p><p>还好,不经意间踏上了编程之路。</p><p>但一提到「编程」二字,常常让我觉得汗颜。程序者,经典公式为<strong>程序=算法+数据结构</strong>。但是我既不懂算法,也不懂数据结构。我说我在编程(programming),实在是跟全天下的程序员(programmer)过不去,我顶多是个编码者(coder)而已。</p><p>因此,2008, 北京要举办奥运会,我要学习算法和数据结构。从而需要自补高等数学(不好意思,我是文科生 :D),也从而需要学好 C 语言。高数、C 语言、算法和数据结构,用我党的话说,这是 2008 的主旋律。</p><p>如果时间充足的话,还计划使用 Python 做些 GUI 应用。计算机的精彩决不仅仅局限于 web.</p><p>回顾一下年初,有些话说过了头,但基本上都履行了。虽然没法攒够钱给支持家庭,但也算是最有力度的一年了。</p><p>2007 年 10 月,我奶奶安详辞世,享 86 岁。愿往者安息,生者健康,以奋斗来慰藉往者的关怀与爱护!</p>A%B7%E9I%B8W%E7%86%B6%B7%D5%D7%CF%01%DFp20082007-12-30T10:00:00+08:00https://realazy.comRealazy<p>来自某个 nb 招聘的题目:</p><blockquote><p>请给 Array 本地对象增加一个原型方法,它的用途是删除数组条目中重复的条目(可能有多个),返回值是一个包含被删除的重复条目的新数组。这是我的答案:</p></blockquote><h2 id="">新解</h2><pre class='js'><code class='js'>Array.prototype.uniq = function () { var resultArr = [], returnArr = [], i = 1, origLen = this.length, resultLen; function include(arr, value) { for (var i = 0, n = arr.length; i < n; ++i) { if (arr[i] === value) { return true; } } return false; } resultArr.push(this[0]); for (i; i < origLen; ++i) { if (include(resultArr, this[i])) { returnArr.push(this[i]); } else { resultArr.push(this[i]); } } resultLen = resultArr.length; this.length = resultLen; for (i = 0; i < resultLen; ++i) { this[i] = resultArr[i]; } return returnArr;};</code></pre><p>这种解法在整个过程对原有数组的改变只有两次,效率比其他两种高了 2 个数量级左右!可<a href='http://realazy.com/lab/uniq.html'>在此测试</a>三种解法的性能。</p><h2 id="">旧解</h2><p>以下至「关于测试案例」之间皆为旧文,若阅读不顺,忽略之。</p><pre class='js'><code class='js'>Array.prototype.uniq_slow = function () { var ret = [], i = 0, j = 0; while (undefined !== this[i]) { j = i + 1; while (undefined !== this[j]) { if (this[i] === this[j]) { ret.push(this.splice(j, 1)[0]); } else { ++j; } } ++i; } return ret;};</code></pre><p>感谢猫仔提示,这道题目很容易让人产生误读。看清了题目后更新了。</p><p>为何用 <code>while</code> 而不是 <code>for</code>? 因为这个数组总是在变化,每次循环都得重新计算 <code>length</code>. 按理说,使用 <code>while</code> 效率会更高,尤其数组很大的时候。</p><p>欢迎大家交流讨论。</p><p>感谢 fdcn 提示,更新之。这里确实是容易犯错。</p><p>猜想由于强类型判断导致性能不高(可<a href='http://realazy.com/lab/uniq.html'>在此测试</a>),因此此种做法未见有性能的提升(还稍微慢了一些),而且还不能传递类似 <code>[1,,,2,,]</code> 这样的数组。所以还是<a href='http://ued.taobao.com/blog/2007/11/20/job_test_explanation/'>淘宝 UED 上的解法</a>比较科学(当然不是没有改进之处,比如不应该在 <code>for</code> 循环中声明变量)。</p><p>其实,这篇 blog 的意义在探讨如何避免无意义的消耗(比如计算 <code>length</code>)。但是鱼和熊掌不能兼得是自古之理,顾此失彼。当然,办法不是没有,比如数组的 <code>forEach</code>, <code>map</code> 方法等,可惜只有 <code>gecko</code> 浏览器才支持。</p><h2 id="">关于测试案例</h2><p>数组是随机产生的 1-100 之间的整数,长度为 5000,每个相同的大约重复 5 次。三个测试数组的元素构成是一致的。</p><h2 id="">总结</h2><p>对数组的改变开销巨大,如果可能,尽量在不改变原有数组的情况下进行操作,如最终需要改变数组自身,可将结果赋予原有数组来操作。另外,对于 <code>length</code> 的计算,似乎效率并未受其影响。</p><p>啥时候我也该进补算法了,唉。软肋啊。</p><p><strong>推荐阅读:</strong> 王元涛同学的 <a href='http://www.pkblogs.com/todwang/2007/12/javascript-uniq.html'>JavaScript 数组的 uniq 方法</a>。</p>%3EF%A5%00%8D%08%A0%EB%17%8D%AB%A3%F7u*%F5JavaScript 数组的 uniq 方法2007-12-07T10:00:00+08:00https://realazy.comRealazy<p>Opera, 作为 <a href='http://developer.yahoo.com/yui/articles/gbs/#gbschart'>A-Grade</a> 浏览器,在现在的前端开发中务必支持。它很优秀,很不幸,bug 是每个浏览器都不可避免的问题,Opera 亦难免。说说我发现的一个关于 <code>cloneNode</code> 的问题。</p><h2 id="">问题</h2><p>假设我们有一个 Form 节点(node)的引用,姑且名之为 <code>elForm</code>,现在需要克隆一份,可以这么做:<code>var elFormClone = elForm.cloneNode(true)</code>.</p><p>在插入这份克隆到 DOM 树中后,IE, Firefox 均未发现问题。Opera 会产生这样的问题:表单内的字段无法引用。比如,假设刚才我们的 <code>elForm</code> 有一个 <code><input name="title" ... /></code>, 此时你无法通过 <code>elFormClone.title</code> 或者 <code>elFormClone['title']</code> 获取它。</p><h2 id="">解决方案</h2><p>使用 <code>document.createElement</code> 创建 form 元素,然后设置该元素的 <code>innerHTML</code>(感谢 MS 发明了它) 为 <code>elForm</code> 的 <code>innerHTML</code> 即可:</p><pre class='js'><code class='js'>var elFormClone = document.createElement('form');// 设置一些 elForm 的原属性,有必要的话...elFormClone.innerHTML = elForm.innerHTML;// 处理这个 clone, 该咋办就咋办了...</code></pre>F%99%B1%094l%F8%1C%F9%B0%8F%1F%5Ex%C7%CBOpera 下 cloneNode 的 bug2007-11-22T10:00:00+08:00https://realazy.comRealazy<p>在某些情况下,比如自动补全(auto complete)的输入框中,需要使用 <code>keyup</code> 事件来监听键盘的输入以迅速作出回应。</p><p>关键在于 <code>keyup</code>, 如果世界是美好的,那么就不会有这篇 blog. 可是……</p><p>世界是不美好的。我们活在中文世界,我们要用输入法。在输入法开启的情况下,您会碰到不美好的事情:<code>keyup</code> 失效。对于您绑定到 <code>keyup</code> 的任何回调函数,除非您把输入法切换回英文状态,否则它会无动于衷。如果能称之为 bug,我会很高兴,因为 bug 会有修复的可能,如果是特征(feature),那么,我只好叹息一下。</p><h2 id="">问题</h2><p>在开启输入法的情况下,三个浏览器的具体问题如下:</p><ul><li><strong>IE:</strong>触发 keydown 和 keyup, 不触发 keypress. 能够获得输入值。</li><li><strong>Firefox:</strong>触发 keydown 和 keypress, 不触发 keyup. 输入值未能获得。在回车后会触发 keyup, 可获得输入值。</li><li><strong>Opera:</strong>keydown, keypress 和 keyup 都不触发,输入值也未能获。</li></ul><p>(如果您能帮我试用一下 Safari,我会很高兴并谢谢您。这里有一个测试页面:<a href='http://tonextone.com/test/eventTest.html'>http://tonextone.com/test/eventTest.html</a>)</p><h2 id="">解决方案</h2><p>总结出以上问题,没有兴奋反而陷入绝望,因为没有 google 出解决方案(是的,对于拉丁语系的老外来说,不会存在输入法)。但是,wait, 谷歌搜索的自动补全不是工作得好好的吗?于是研究一下这个 <a href='http://www.google.cn/ac.js'>http://www.google.cn/ac.js</a>。嘿嘿,虽然混淆得还可以,但还是可以发现蛛丝马脚的。它使用一个计时器,当输入框处于聚焦(focus)状态时,每 10 毫秒执行一次回调函数。</p><p>虽然挺耗资源(所以建议在输入框失焦(blur)时,一定要清除这个计时器),但也只能如此了。作前端开发的,不仅要与语言(JavaScript, CSS, HTML) 斗,还要与浏览器斗,其乐无穷也。</p><p><strong>更新:</strong>IE 有一个 <code>onpropertychange</code> 事件处理器可以随时检测变化,对应之,Firefox 和 Opera 有一个 <code>oninput</code> 事件处理器也可以达到目的。遗憾的是,在输入法开启的情况下,Opera 对于 <code>oninput</code> 也没法随时监听(表现在:输入英文字母可以监听到,而中文则失效)。如果不考虑 Opera, IE 使用 <code>onpropertychange</code> 而 Firefox 使用 <code>oninput</code>, 也可解决我们上述问题。</p><p>另外需要注意的是, <code>oninput</code> 比较诡异,您可以 <code><input oninput="jsFunc()" ... /></code> 来绑定,也可以用 <code>addEventListener</code> 来绑定,就是不能使用 DOM 0 的 <code>element.oninput = function(){}</code> 的形式来绑定,否则不生效。</p><p>未曾测试 Safari, 有心者可自行测试之。</p>%06%E5%13%BA%E0%A3(c%C0QT%12%A3%15%01%7B输入法下 keyup 失效的解决方案2007-10-31T10:00:00+08:00https://realazy.comRealazy<p>CSS Sprites 技术不新鲜,早在 2005 年 <a href='http://csszengarden.com/'>CSS Zengarden</a> 的园主 <a href='http://www.mezzoblue.com/'>Dave Shea</a> 就在 <a href='http://www.alistapart.com/'>ALA</a> 发表对该技术的<a href='http://www.alistapart.com/articles/sprites'>详细阐述</a>。原先只在 CSS 玩家之间作为一种制作方法流传,后来出来个 <a href='http://stevesouders.com/examples/rules.php'>14 Rules for Faster-Loading Web Sites</a>, 技术人员之间竞相传阅,其中第一条规则 Make Fewer HTTP Requests 就提到 CSS Sprites。于是这个小妖精就火了起来,甚至出现了<a href='http://www.csssprites.com/'>在线生成工具</a>,势不可挡也。近来国内很多 blog 都提到 CSS Sprites,最著名的例子莫过于 <a href='http://www.google.co.kr/'>http://www.google.co.kr/</a> 下方的那几个动画。最新发布的 YUI 中,也是使用到 CSS Sprites,几乎都有的 CSS 装饰图都被一个 <a href='http://developer.yahoo.com/yui/build/assets/skins/sam/sprite.png'>40x2000 的图</a> 包办。社交大站 Facebook 最近也使用了一个 <a href='http://static.ak.facebook.com/images/sprite/icons.png?db3'>22x1150 的图片</a> 承担了所有 icon. 一时间,CSS Sprites 无处不在。</p><h2 id="">原理</h2><p>我们知道,自 CSS 革命以降,HTML 倾向于语义化,在一般情况下不再在标记里写装饰性的内容而是把呈现的任务交给了 CSS。GUI 是缤纷多彩的,少不了各种漂亮的图来装点。新时代的生产方式是,在 HTML 布满各种各样的钩子(hook),然后交由 CSS 来处理。在需要用到图片的时候,现阶段是通过 CSS 属性 <code>background-image</code> 组合 <code>background-repeat</code>, <code>background-position</code> 等来实现(题外话:为何我提现阶段,因为未来浏览器若支持 <code>content</code> 则又新增另外的实现方法)。我们的主角是,你一定猜到了,就是 <code>background-position</code>。通过调整 <code>background-position</code> 的数值,背景图片就能以不同的面貌出现在你眼前。其实图片整体面貌没有变,由于图片位置的改变,你看到只该看到的而已。就好比手表上的日期,你今天看到是 21,明天看到是 22,是因为它的<strong>position</strong>往上跳了一格。所以你也大概了解到,CSS Sprites 一般只能使用到固定大小的盒子(box)里,这样才能够遮挡住不应该看到的部分。</p><p>我们使用 YUI 的 sprite.png 举个例子,假如我们有这么一段代码,<code>max</code> 代表最大化,<code>min</code> 代表最小化,我们需要给它们配上相应的漂亮图片(这样我们的网站才能够吸引人,才可以卖钱,才可以到佛罗里达晒太阳:D):</p><pre class='html'><code class='html'><div class="max">最大化</div><div class="min">最小化</div></code></pre><p>这两个 <code>class</code> 都使用同一个图片:</p><pre class='css'><code class='css'>.min,.max { width: 16px; height: 16px; background-image: url(http://developer.yahoo.com/yui/build/assets/skins/sam/sprite.png); background-repeat: no-repeat; /*我们并不想让它平铺*/ text-indent: -999em; /*隐藏文本的一种方法*/}</code></pre><p>效果如下:</p><p><img src='/assets/missing.png' alt='' /></p><p>我们看到一团灰,没错,因为我们还没有指定 <code>background-position</code>,默认为 <code>0 0</code>,可以看下 <a href='http://developer.yahoo.com/yui/build/assets/skins/sam/sprite.png'>sprite.png</a>, 处于这个位置正是灰块。好了,我们要找到代表最大化的加号和代表最小化的减号的位置找出来。经过测量,最大化按钮位于 Y 轴的 350px 处,最小化按钮位于 Y 轴 400px 处。想一想我们如何才能让它们能够显示出来呢,明显,要向上提升 sprite.png,得到代码如下:</p><pre class='css'><code class='css'>.max { background-position: 0 -350px;}.min { background-position: 0 -400px;}</code></pre><p>耶,我们成功了:</p><p><img src='/assets/missing.png' alt='' /></p><p>(注意:为了举例的方便,本例子直接在 HTML 内置样式,切勿在实践中的非特殊情况使用这种方式)。</p><h2 id="">优点</h2><p>我们从前面了解到,CSS Sprites 为什么突然跑火,跟能够提升网站性能有关。显而易见,这是它的巨大优点之一。普通制作方式下的大量图片,现在合并成一个图片,大大减少了 HTTP 的连接数。HTTP 连接数对网站的加载性能有重要影响。</p><h2 id="">缺点</h2><p>至于可维护性,这是一般双刃剑。可能有人喜欢,有人不喜欢,因为每次的图片改动都得往这个图片删除或添加内容,显得稍微繁琐。而且算图片的位置(尤其是这种上千 px 的图)也是一件颇为不爽的事情。当然,在性能的口号下,这些都是可以克服的。</p><p>由于图片的位置需要固定为某个绝对数值,这就失去了诸如 <code>center</code> 之类的灵活性。</p><p>前面我们也提到了,必须限制盒子的大小才能使用 CSS Sprites,否则可能会出现出现干扰图片的情况。这就是说,在一些需要非单向的平铺背景和需要网页缩放的情况下,CSS Sprites 并不合适。YUI 的解决方式是,加大图片之间的距离,这样可以保持有限度的缩放。</p><h2 id="">总结</h2><p>性能压倒一切。CSS Sprites 是值得推广的一种技术。尤其适宜用于 <a href='http://www.alistapart.com/articles/fir/'>FIR</a>,比如固定大小的 icon 替换。为保持兼容性,图片中的各个部分保持一定的距离是一种不错的做法。</p><h3 id="">推荐阅读:</h3><ul><li><a href='http://www.alistapart.com/articles/sprites'>CSS Sprites: Image Slicing's Kiss of Death</a></li><li><a href='http://stevesouders.com/examples/rules.php'>14 Rules for Faster-Loading Web Sites</a></li><li><a href='http://www.oreilly.com/catalog/9780596529307/index.html'>High Performance Web Sites</a></li></ul><p><strong>更新:</strong>有网友问到 IE6 不支持 png 的问题。其实真相是,IE6 不支持的是半透明(alpha transparency)的 png,对于全透明的 png, IE6 并不存在问题。因此,在实践中,不涉及到半透明而需要透明背景的图片,其实都可以使用 png, 这是很安全的。</p>9%C7%0C%A7%EE%BC%AE%DE%9D%C7%021%88%C8%EE%8ECSS Sprites2007-10-08T10:00:00+08:00https://realazy.comRealazy<p>关于英文的写作有一本十分著名的书,<a href='http://crockford.com/wrrrld/style.html'>TheElements of Style</a>(风格要素),编写程序也有一本 <a href='http://www.amazon.com/exec/obidos/ASIN/0070342075/wrrrldwideweb'>The Elements of Programming Style</a>(编程风格要素)。证明了在某种程度上,编写程序其实就是语文写作,清晰的风格对程序的质量有着重要的影响。草率含混的风格会隐蔽程序真性。</p><p>作为 JavaScript Guru, <a href='http://www.crockford.com/'>Douglas Crockford</a> 提出了自己对 JavaScript 风格的创见(<a href='http://javascript.crockford.com/style1.html'>第一部分</a>,<a href='http://javascript.crockford.com/style2.html'>第二部分</a>)。</p><p>第一部分主要讨论 JavaScript 语言本身,包括:</p><ul><li><strong>淘汰过时的构建</strong>:讨论了在 HTML 页面引入 JavaScript 的方式。经典的写法是: <code><script language="javascript"><!-- --></script></code>。<code>language</code> 并不是 W3C 所认同的标准,建议使用的是 <code>type</code>, 但作为 <code>type</code> 值的 MIME type 并没有标准化(有时是 <code>text/javascript</code>, 有时是 <code>application/ecmascript</code>),但目前所有的浏览器都是使用 JavaScript 作为默认的脚本语言,因此仅仅写<code><script></code> 就是安全的。随着 NetScape 3 的淘汰,<code><-- --></code> 也不是必要的了。 (注:这个东西 Web 标准社区也有自己的看法,并认为应该写 <code>type="text/javascript"</code>,我也赞成。)</li><li><strong>在结构中始终使用区块</strong>:莫偷懒,省略区块(即 <code>{ }</code>)会引发不经意的错误和麻烦。即使只有一句,也老老实实写上:<code>if (expression) { ... }</code></li><li><strong>避免在表达式中进行赋值</strong>:虽然可以使代码紧凑,但会让控制流程难于理解。</li><li><strong>使用对象扩充</strong>:在不需要构造函数的对象中,不如先创建一个空对象,然后扩充它。</li><li><strong>使用通用库</strong>:注意提高代码的重用。</li></ul><p>第二部分主要讲的是一些使程序更清晰和简洁的习惯用法:</p><ul><li><strong>使用 <code>==</code> 得当心强类</strong>:<code>1 == true</code> 是真,但 <code>1 === true</code> 却为假。</li><li><strong>使用 <code>?:</code> 运算符选择两值之一</strong>:在这种操作中,三元运算符为此而生。</li><li><strong>永远不要使用隐含的全局变量</strong>:请记得声明变量时加上 <code>var</code>.</li><li><strong>不要使用 <code>?:</code> 来选择两种行为的其一</strong>:写作 <code>p.style.backgroundColor = z ? '#fff' : '#989898';</code> 而不是 <code>(z == 0) ? p.style.backgroundColor = '#fff' : p.style.backgroundColor = '#989898';</code>。</li><li><strong>使用 <code>||</code> 来指定一个默认值</strong>:最典型的莫过于处理事件参数了:<code>var e = e || event;</code>。</li><li><strong>全局变量是邪恶的</strong></li><li><strong>使用内部函数来避免全局变量</strong></li></ul><p>这些风格是 Crock 在 2005 年提出的,现在有些已经深入人心(如不要使用全局变量),因而也没有必要多做解释了。如果你有所疑惑,不如详细看看这两篇风格文章,里面有大量的代码来举证这些风格的重要性。</p><p><strong>更新:</strong><a href='http://dojotoolkit.org/developer/StyleGuide'>Dojo 的风格指南</a> 也不错。</p>-P%DB%16%90W%C5a%DC%BE%A7#%E2%03%B4%0DJavaScript 风格要素2007-09-17T10:00:00+08:00https://realazy.comRealazy<p>去听了牛人 <a href='http://dbaron.org/'>dbaron</a> 的一个 <a href='http://groups.google.com/group/firefoxer-china/browse_thread/thread/1ad0de208c3da9dd'>Web Page Layout/Display in Mozilla</a> 讲座(<a href='http://blog.mozilla.com/ligong/2007/09/04/tech-talk-at-mozilla-online-this-saturday-3pm-09082007/'>via</a>)。讲的东西对我一个只会 HTML, CSS 和 JavaScript 的人来说很底层,所以效果也比较「和谐」,只是大致了解了 mozilla 的 CSS 渲染源码分布位置和渲染流程而已。</p><p>讲座提到了 reflow(如何翻译呢?又是一个问题)这个东东。之前对 reflow 有所闻,能经常从某些大牛的幻灯中提到,提高页面渲染的性能,需尽量避免 reflow. 那么 reflow 是什么东西呢?它又是如何影响页面性能的?事后去问了一下 dbaron(呵呵,我口语彻底不行,加上心理素质,最后是把问题写下来给他看),豁然开朗也。</p><p>在 CSS 规范中有一个渲染对象的概念,通常用一个盒子(box, rectangle)来表示。mozilla 通过一个叫 frame 的对象对盒子进行操作。frame 主要的动作有三个:</p><ul><li>构造 frame, 以建立对象树(DOM 树)</li><li>reflow, 以确定对象位置,或者是调用 mozilla 的 Layout(这里是指源码的实现)</li><li>绘制,以便对象能显示在屏幕上</li></ul><p>总的来说,reflow 就是载入内容树(在 HTML 中就是 DOM 树)和创建或更新 frame 结构的响应的一种过程。</p><p>要提高页面性能,其实就是避免 reflow 的开销。那么,有哪些方面是需要 reflow 的呢?比如,未指定图片宽高的话,图片的载入会使页面 reflow, 因为要根据图片宽高来更新 frame。这里就有一个提高页面性能的小技巧:如果事先能够确定图片宽高的话,最好在 HTML 里写上。</p><p>在编写一些常见的动态效果时,一般使用 CSS 的 <code>display</code> 来切换可见性。很不幸,这也会产生 reflow. 把元素置为 <code>display:none</code>,相当于把这个元素的 frame 销毁了,再置回非 <code>none</code> 时,需要重新构造 frame,这就产生了 reflow. 而另外一个切换可见性的属性 <code>visibility</code> 则不存在 reflow 问题,置为 <code>visibility:hidden</code> 的元素的 frame 并没有销毁,需要显示的时候其实就是一个绘制(上面提到的动作第三步)过程而已,没有 reflow,因此效率会更高。如果你看过一些 JavaScript 库/框架的源码,会发现它们大量使用 <code>visibility</code> 而不是 <code>display</code>,道理应该如此。</p>&9-%FB%5C%9F%EF%9D4%F0%1C%D7%7C%81%1CVreflow2007-09-09T10:00:00+08:00https://realazy.comRealazy<p>如果你看过 YUI 的 RAW 源码,会发现很多跟 javadoc 语法类似的注释。据说 (<a href='http://www.phpied.com/running-jsdoc-on-windows/#comment-762'>via</a>) 是使用 <a href='http://jsdoc.sourceforge.net/'>JSDoc</a> 这个工具。但我探索了一遍,发现 YUI 多出很多 tag, 比如 <code>@namespace</code>, <code>@static</code> 之类,那么我只好怀疑 YUI 做了改良。一开始,我也试着使用了一下 JSDoc,遗憾的是,除了缺少一些 tag 外,它还不支持未匿名函数内的文档生成。比如,有时候为了保证不产生额外的全局变量会这么写:</p><pre class='js'><code class='js'>(function(){/** * 注释 * @tag */....})();</code></pre><p>或者我所喜欢的 “<a href='http://yuiblog.com/blog/2007/06/12/module-pattern/'>module pattern</a>”:</p><pre class='js'><code class='js'>var module = function(){ var private; return { /** * 注释 * @tag */ pub1: function(){}, pub2: function(){}, ... }}();</code></pre><p>在这种情况下,在匿名函数内,和在 return 区块内的注释,JSDoc 就,用北京话说,「葛屁」(音)了,就是不起作用了。遗憾的是我的水平尚未上升到改良 perl 脚本以能按我所需的程度,因此,我寻找的是下一个目标。</p><p><a href='http://jsdoctoolkit.org/'>JsDoc Toolkit</a> 是一个不错的选择,虽然名称跟 JSDoc 不太容易区分。相比之下,它是使用 JavaScript 来处理文档的(当然,得通过 rhino),而且支持我前面所列举的两种形式(单是这点,我就只能选择它了)。</p><p>两者在 tag 的处理方面有所不同,感觉 JSDoc 的全面一些,但缺乏某些关键的 tag,比如 JsDoc Toolkit 中的 <code>@scope</code> 就很好用。</p><p>至于如何安装使用,直接上官网看吧 :)。欢迎同我交流。</p><p>粗粗看了一下 jQuery,似乎它也是通过 rhino 来处理文档生成的,嗯,我也得钻研一下,hack 出符合我要求的工具了。</p>%B44%E8%11@%7B%06%13%DF%25%22%A6aM%F0iJavaScript 文档生成工具2007-09-02T10:00:00+08:00https://realazy.comRealazy<p>来自 <a href='http://www.fitzblog.com/tabid/17782/bid/2127/Nine-Javascript-Gotchas.aspx'>Nine Javascript Gotchas</a>, 以下是 JavaScript 容易犯错的九个陷阱。虽然不是什么很高深的技术问题,但注意一下,会使您的编程轻松些,即所谓 make life easier. 笔者对某些陷阱会混杂一些评点。</p><h2 id="">最后一个逗号</h2><p>如这段代码,注意最后一个逗号,按语言学角度来说应该是不错的(python 的类似数据类型辞典 dictionary 就允许如此)。IE 会报语法错误,但语焉不详,你只能用人眼从几千行代码中扫描。</p><pre class='js'><code class='js'>var theObj = { city: "Boston", state: "MA",};</code></pre><h2 id="this"><code>this</code> 的引用会改变</h2><p>如这段代码:</p><pre class='html'><code class='html'><input type="button" value="Gotcha!" id="MyButton" /></code></pre><pre class='js'><code class='js'>var MyObject = (function () { this.alertMessage = "Javascript rules"; this.ClickHandler = function () { alert(this.alertMessage); };})();document.getElementById("theText").onclick = MyObject.ClickHandler;</code></pre><p>并不如你所愿,答案并不是"JavaScript rules"。在执行 <code>MyObject.ClickHandler</code> 时,代码中红色这行,<code>this</code> 的引用实际上指向的是 <code>document.getElementById("theText")</code> 的引用。可以这么解决:</p><pre class='html'><code class='html'><input type="button" value="Gotcha!" id="theText" /></code></pre><pre class='js'><code class='js'>var MyObject = (function () { var self = this; this.alertMessage = "Javascript rules"; this.OnClick = function () { alert(self.value); };})();document.getElementById("theText").onclick = MyObject.OnClick;</code></pre><p>实质上,这就是 <a href='/posts/2007-07-18-scope-in-javascript.html'>JavaScript 作用域的问题</a>。如果你看过,你会发现解决方案不止一种。</p><h2 id="">标识盗贼</h2><p>在 JavaScript 中不要使用跟 HTML 的 id 一样的变量名。如下代码:</p><pre class='html'><code class='html'><input type="button" id="TheButton" /><script> TheButton = get("TheButton");</script></code></pre><p>IE 会报对象未定义的错误。我只能说:IE sucks.</p><h2 id="">字符串只替换第一个匹配</h2><p>如下代码:</p><pre class='js'><code class='js'>var fileName = "This is a title".replace(" ", "_");</code></pre><p>而实际上,结果是"<code>This_is a title</code>". 在 JavaScript 中,<code>String.replace</code> 的第一个参数应该是正则表达式。所以,正确的做法是这样:</p><pre class='js'><code class='js'>var fileName = "This is a title".replace(/ /g, "_");</code></pre><h2 id="mouseout-mousein">mouseout 意味着 mousein</h2><p>事实上,这是由于事件冒泡导致的。IE 中有 <code>mouseenter</code> 和 <code>mouseleave</code>,但不是标准的。作者在此建议大家使用库比如 YUI 来解决问题。</p><h2 id="parseInt"><code>parseInt</code> 是基于进制体系的</h2><p>这个是常识,可是很多人给忽略了 <code>parseInt</code> 还有第二个参数,用以指明进制。比如,<code>parseInt("09")</code>,如果你认为答案是 9,那就错了。因为,在此,字符串以 0 开头,<code>parseInt</code> 以八进制来处理它,在八进制中,<code>09</code> 是非法,返回 <code>false</code>,布尔值 <code>false</code> 转化成数值就是 0. 因此,正确的做法是 <code>parseInt("09", 10)</code>.</p><h2 id="for-in"><code>for...in...</code> 会遍历所有的东西</h2><p>有一段这样的代码:</p><pre class='js'><code class='js'>var arr = [5, 10, 15];var total = 1;for (var x in arr) { total = total * arr[x];}</code></pre><p>运行得好好的,不是吗?但是有一天它不干了,给我返回的值变成了 <code>NaN</code>, 晕。我只不过引入了一个库而已啊。原来是这个库改写了 <code>Array</code> 的 <code>prototype</code>,这样,我们的 <code>arr</code> 平白无过多出了一个属性(方法),而 <code>for...in...</code> 会把它给遍历出来。所以这样做才是比较安全的:</p><pre class='js'><code class='js'>for (var x = 0; x < arr.length; x++) { total = total * arr[x];}</code></pre><p>其实,这也是污染基本类的 <code>prototype</code> 会带来危害的一个例证。</p><h2 id="">事件处理器的陷阱</h2><p>这其实只会存在使用作为对象属性的事件处理器才会存在的问题。比如 <code>window.onclick = MyOnClickMethod</code> 这样的代码,这会复写掉之前的 <code>window.onclick</code> 事件,还可能导致 IE 的内容泄露(sucks again)。在 IE 还没有支持 DOM 2 的事件注册之前,作者建议使用库来解决问题,比如使用 YUI:</p><pre class='js'><code class='js'>YAHOO.util.Event.addListener(window, "click", MyOnClickMethod);</code></pre><p>这应该也属于常识问题,但新手可能容易犯错。</p><h2 id="Focus-Pocus">Focus Pocus</h2><p>新建一个 <code>input</code> 文本元素,然后把焦点挪到它上面,按理说,这样的代码应该很自然:</p><pre class='js'><code class='js'>var newInput = document.createElement("input");document.body.appendChild(newInput);newInput.focus();newInput.select();</code></pre><p>但是 IE 会报错(sucks again and again)。理由可能是当你执行 <code>fouce()</code> 的时候,元素尚未可用。因此,我们可以延迟执行:</p><pre class='js'><code class='js'>var newInput = document.createElement("input");newInput.id = "TheNewInput";document.body.appendChild(newInput);setTimeout(function () { //这里我使用闭包改写过,若有兴趣可以对比原文 document.getElementById("TheNewInput").focus(); document.getElementById("TheNewInput").select();}, 10);</code></pre><p>在实践中,JavaScript 的陷阱还有很多很多,大多是由于解析器的实现不到位而引起。这些东西一般都不会在教科书中出现,只能靠开发者之间的经验分享。谢天谢地,我们生活在网络时代,很多碰到的问题,一般都可以在 Google 中找到答案。</p>%D8%BF%B2%A4%A8%3E%10Z%A0%80%CE%DE%BF%AB%5C%CFJavaScript 的 9 个陷阱及评点2007-08-20T10:00:00+08:00https://realazy.comRealazy<p>这篇文章阐述的是一种函数式编程(functional-programming)设计模式,我称之为惰性函数定义(Lazy Function Definition)。我不止一次发现这种模式在 JavaScript 中大有用处,尤其是编写跨浏览器的、高效运行的库之时。</p><h2 id="">热身问题</h2><p>编写一个函数 <code>foo</code>,它返回的是 <code>Date</code> 对象,这个对象保存的是 <code>foo</code> 首次调用的时间。</p><h3 id="">方法一:上古时代的技术</h3><p>这个最简陋的解决方案使用了全局变量 <code>t</code> 来保存 <code>Date</code> 对象。<code>foo</code> 首次调用时会把时间保存到 <code>t</code> 中。接下来的再次调用,<code>foo</code> 只会返回保存在 <code>t</code> 中的值。</p><pre class='js'><code class='js'>var t;function foo() { if (t) { return t; } t = new Date(); return t;}</code></pre><p>但是这样的代码有两个问题。第一,变量 <code>t</code> 是一个多余的全局变量,并且在 <code>foo</code> 调用的间隔期间有可能被更改。第二,在调用时这些代码的效率并没有得到优化因为每次调用 <code>foo</code> 都必须去求值条件。虽然在这个例子中,求值条件并不显得低效,但在现实世界的实践例子中常常会有极为昂贵的条件求值,比如在 if-else-else-...的结构中。</p><h3 id="">方法二:模块模式</h3><p>我们可以通过被认为归功于 <a href='http://www.jibbering.com/faq/faq_notes/closures.html#clEncap'>Cornford</a> 和 <a href='http://www.crockford.com/javascript/private.html'>Crockford</a> 的<a href='http://yuiblog.com/blog/2007/06/12/module-pattern/'>模块模式</a>来弥补第一种方法的缺陷。使用闭包可以隐藏全局变量 <code>t</code>,只有在 <code>foo</code> 内的代码才可以访问它。</p><pre class='js'><code class='js'>var foo = (function () { var t; return function () { if (t) { return t; } t = new Date(); return t; };})();</code></pre><p>但这仍然没有优化调用时的效率,因为每次调用 <code>foo</code> 依然需要求值条件。</p><p>虽然模块模式是一个强大的工具,但我坚信在这种情形下它用错了地方。</p><h3 id="">方法三:函数作为对象</h3><p>由于 JavaScript 的函数也是对象,所以它可以带有属性,我们可以据此实现一种跟模块模式质量差不多的解决方案。</p><pre class='js'><code class='js'>function foo() { if (foo.t) { return foo.t; } foo.t = new Date(); return foo.t;}</code></pre><p>在一些情形中,带有属性的函数对象可以产生比较清晰的解决方案。我认为,这个方法在理念上要比模式模块方法更为简单。</p><p>这个解决方案避免了第一种方法中的全局变量 <code>t</code>,但仍然解决不了 <code>foo</code> 每次调用所带来的条件求值。</p><h3 id="">方法四:惰性函数定义</h3><p>现在,这是你阅读这篇文章的理由:</p><pre><code>var foo = function() { var t = new Date(); foo = function() { return t; }; return foo();};</code></pre><p>当 <code>foo</code> 首次调用,我们实例化一个新的 <code>Date</code> 对象并重置 <code>foo</code> 到一个新的函数上,它在其闭包内包含 <code>Date</code> 对象。在首次调用结束之前,<code>foo</code> 的新函数值也已调用并提供返回值。</p><p>接下来的 <code>foo</code> 调用都只会简单地返回 <code>t</code> 保留在其闭包内的值。这是非常快的查找,尤其是,如果之前那些例子的条件非常多和复杂的话,就会显得很高效。</p><p>弄清这种模式的另一种途径是,外围(outer)函数对 <code>foo</code> 的首次调用是一个保证(promise)。它保证了首次调用会重定义 <code>foo</code> 为一个非常有用的函数。笼统地说,术语「保证」 来自于 Scheme 的惰性求值机制(lazy evaluation mechanism)。每一位 JavaScript 程序员真的都应该<a href='http://www.amazon.com/Scheme-Programming-Language-3rd/dp/0262541483/ref=pd_bbs_sr_1/102-4214146-5559331?ie=UTF8&s=books&qid=1186852441&sr=8-1'>学习 Scheme</a>,因为它有很多函数式编程相关的东西,而这些东西会出现在 JavaScript 中。</p><h2 id="">确定页面滚动距离</h2><p>编写跨浏览器的 JavaScript, 经常会把不同的浏览器特定的算法包裹在一个独立的 JavaScript 函数中。这就可以通过隐藏浏览器差异来标准化浏览器 API,并让构建和维护复杂的页面特性的 JavaScript 更容易。当包裹函数被调用,就会执行恰当的浏览器特定的算法。</p><p>在拖放库中,经常需要使用由鼠标事件提供的光标位置信息。鼠标事件给予的光标坐标相对于浏览器窗口而不是页面。加上页面滚动距离鼠标的窗口坐标的距离即可得到鼠标相对于页面的坐标。所以我们需要一个反馈页面滚动的函数。演示起见,这个例子定义了一个函数 <code>getScrollY</code>。因为拖放库在拖拽期间会持续运行,我们的 <code>getScrollY</code> 必须尽可能高效。</p><p>不过却有四种不同的浏览器特定的页面滚动反馈算法。Richard Cornford 在他的 <a href='http://www.jibbering.com/faq/faq_notes/not_browser_detect.html#bdScroll'>feature detection article</a> 文章中提到这些算法。最大的陷阱在于这四种页面滚动反馈算法其中之一使用了 <code>document.body</code>. JavaScript 库通常会在 HTML 文档的 <code><head></code> 加载,与此同时 <code>docment.body</code> 并不存在。所以在库载入的时候,我们并不能使用特性检查(feature detection)来确定使用哪种算法。</p><p>考虑到这些问题,大部分 JavaScript 库会选择以下两种方法中的一种。第一个选择是使用浏览器嗅探 <code>navigator.userAgent</code>,为该浏览器创建高效、简洁的 <code>getScrollY</code>. 第二个更好些的选择是 <code>getScrollY</code> 在每一次调用时都使用特性检查来决定合适的算法。但是第二个选择并不高效。</p><p>好消息是拖放库中的 <code>getScrollY</code> 只会在用户与页面的元素交互时才会用到。如果元素业已出现在页面中,那么 <code>document.body</code> 也会同时存在。<code>getScrollY</code> 的首次调用,我们可以使用惰性函数定义模式结合特性检查来创建高效的 <code>getScrollY</code>.</p><pre class='js'><code class='js'>var getScrollY = function () { if (typeof window.pageYOffset == "number") { getScrollY = function () { return window.pageYOffset; }; } else if ( typeof document.compatMode == "string" && document.compatMode.indexOf("CSS") >= 0 && document.documentElement && typeof document.documentElement.scrollTop == "number" ) { getScrollY = function () { return document.documentElement.scrollTop; }; } else if (document.body && typeof document.body.scrollTop == "number") { getScrollY = function () { return document.body.scrollTop; }; } else { getScrollY = function () { return NaN; }; } return getScrollY();};</code></pre><h2 id="">总结</h2><p>惰性函数定义模式让我可以编写一些紧凑、健壮、高效的代码。用到这个模式的每一次,我都会抽空赞叹 JavaScript 的函数式编程能力。</p><p>JavaScript 同时支持函数式和面向对象便程。市面上有很多重点着墨于面向对象设计模式的书都可以应用到 JavaScript 编程中。不过却没有多少书涉及函数式设计模式的例子。对于 JavaScript 社区来说,还需要很长时间来积累良好的函数式模式。</p><p>原文:<a href='http://peter.michaux.ca/article/3556'>Lazy Function Definition Pattern</a>. 转载没有我的信息没有关系,但你一定得写上原文信息,谢谢。</p><p><strong>更新</strong>:</p><p>这个模式虽然有趣,但由于大量使用闭包,可能会由于内存管理的不善而导致性能问题。来自 <a href='http://www.fckeditor.net/'>FCKeditor</a> 的 FredCK 改进了 <code>getScrollY</code>,既使用了这种模式,也避免了闭包:</p><pre class='js'><code class='js'>var getScrollY = function () { if (typeof window.pageYOffset == "number") return (getScrollY = getScrollY.case1)(); var compatMode = document.compatMode; var documentElement = document.documentElement; if ( typeof compatMode == "string" && compatMode.indexOf("CSS") >= 0 && documentElement && typeof documentElement.scrollTop == "number" ) return (getScrollY = getScrollY.case2)(); var body = document.body; if (body && typeof body.scrollTop == "number") return (getScrollY = getScrollY.case3)(); return (getScrollY = getScrollY.case4)();};getScrollY.case1 = function () { return window.pageYOffset;};getScrollY.case2 = function () { return documentElement.scrollTop;};getScrollY.case3 = function () { return body.scrollTop;};getScrollY.case4 = function () { return NaN;};</code></pre><p>请看具体的<a href='http://peter.michaux.ca/article/3556#comment-3661'>评论</a>。</p>Y%A1%106%E8%C2%3C%BA%5C%F7%D8~%D0n%D2%60惰性函数定义模式2007-08-16T10:00:00+08:00https://realazy.comRealazy<p>在本人看来,HTML 5 是一个妥协方案,虽不激进,但更能推动技术的继续进步。没有命名空间,元素也不要求闭合(当然这并不是优点),浏览器也可以宽大处理一些错误。一切沿袭上个世纪 HTML 4 的做法。对于 HTML 的渲染,浏览器一直停留在 1999 年的水平。为此,HTML 5 是一个实用主义方案,这样不仅可以继续处理这么多年来散落在世界各个角落的 HTML,也可以让浏览器厂商更容易添加新特性。这就叫 degrade gracefully(优雅降级)。让我们来看看 HTML 5 增加的一些新元素。</p><h2 id="">结构元素</h2><p>这真是大快人心。目前,我们定义结构只能通过一个「万能」的 <code>div</code>, 试图通过设置它的特性 <code>id</code> 的值如 header, footer, sidebar 等来分别表达头部,底部或者侧栏等。有了它们,代码编写者不再需要为 <code>id</code> 的命名费尽心思,对于手机、阅读器等设备更有语义的好处。HTML 5 增加了新的结构元素来表达这些最常用的结构:</p><ul><li><strong><code>section</code></strong>: 这可以表达书本的一部分或一章,或者一章内的一节</li><li><strong><code>header</code></strong>: 页面主体上的头部。并非 <code>head</code> 元素</li><li><strong><code>footer</code></strong>: 页面的底部(页脚),可以是一封邮件签名的所在</li><li><strong><code>nav</code></strong>: 到其他页面的链接集合</li><li><strong><code>article</code></strong>: 诸如 blog, 杂志,纲要等之中的一条独立记录。</li></ul><p>举个例子,一个 blog 的首页,用 HTML 5 写的话,可以是这样(有省略):</p><pre class='html'><code class='html'><!DOCTYPE html><html> <head> <title>realazy</title> </head> <body> <header> <h1>realazy</h1> </header> <section> <article> <h2><a href="/posts">标题</a></h2> <p>内容在此...(省略 n 字)</p> </article> <article> <h2><a href="/posts">标题2</a></h2> <p>内容2在此...(省略 n 字)</p> </article> ... </section> <footer> <nav> <ul> <li><a href="/posts">导航1</a></li> <li><a href="/posts">导航2</a></li> ... </ul> </nav> <p>© 2007 realazy</p> </footer> </body></html></code></pre><h2 id="block">块级 <code>block</code> 的语义元素</h2><p>HTML 还增加以下三个块级元素:</p><ul><li><strong><code>aside</code></strong></li><li><strong><code>figure</code></strong></li><li><strong><code>dialog</code></strong></li></ul><p><code>aside</code> 可以用以表达注记、贴士、侧栏、摘要、插入的引用等诸如作为补充主体的内容。比如这样表达 blog 的侧栏:</p><pre class='html'><code class='html'><aside> <h3>最新文章</h3> <ul> <li><a href="/blog">文章标题</a></li> ... </ul></aside></code></pre><p><code>figure</code> 元素表示一个有说明的块级图片。比如:</p><pre class='html'><code class='html'><figure> <legend>这是图片的说明</legend> <img alt="图片可替换文本" src="/path/to/img.png" /></figure></code></pre><p><code>dialog</code> 元素用于表达人们之间的对话。在 HTML 5 中,<code>dt</code> 用于表示说话者,而 <code>dd</code> 则用来表示说话者的内容。如:</p><pre class='html'><code class='html'><dialog> <dl> <dt>佛</dt> <dd>色即是空</dd> <dt>悟空</dt> <dd>我现在需要点空……</dd> </dl></dialog></code></pre><h2 id="inline">行内(inline)的语义元素</h2><p><code>m</code> 元素用来标记一些不是那么需要着重强调的文本。现在尚有争议,可能最终会改为 <code>mark</code>.</p><p><code>time</code> 元素如其名,用来表达时间。它需要一个 <code>datetime</code> 的特性来标明机器能够认识的时间,如:</p><pre class='html'><code class='html'><time datetime="2008-08-08T20:08:08">2008年8月8日晚上8时8分8秒</tiem></code></pre><p><code>meter</code> 元素表达特定范围内的数值。可用于薪水、百分比、分数等。比如:</p><pre class='html'><code class='html'>很遗憾地告诉你,我只有<meter>150cm</meter></code></pre><p>它还有 6 个特性来表达各方面的含义,比如:</p><pre class='html'><code class='html'><p> 您的分数是:<meter value="88.7" min="0" max="100" low="65" high="96" optimum="100" > B+</meter >.</p></code></pre><p>还有一个 <code>progress</code>,也是义如其名,用以表达进度:</p><pre class='html'><code class='html'>目标完成度:<progress value="40" max="100">40%</progress></code></pre><h2 id="">嵌入多媒体</h2><p>新增 <code>video</code> 和 <code>audio</code> 元素。顾名思义,分别是用来插入视频和声音的。至于格式,交由浏览器实现,HTML 再也不需要特别的代码去播放特定的格式。就像 <code>img</code> 一样,不管是 png, jpg 还是 gif 都可以显示。值得注意的是,它们可以包含内容。比如,可以把歌词放到某段歌曲中去:</p><pre class='html'><code class='html'><audio src="谁人伴你睡.mp3"> <p>泪枯干</p> <p>难忍怎么委屈自已</p> <p>曾经有一刻悲与喜</p> ...</audio></code></pre><h2 id="">交互性</h2><p>HTML 5 同时也叫 Web Applications 1.0, 因此也进一步发展交互能力。这些标签就是为提高页面的交互体验而生:</p><ul><li><strong><code>details</code></strong></li><li><strong><code>datagrid</code></strong></li><li><strong><code>menu</code></strong></li><li><strong><code>command</code></strong></li></ul><p><code>details</code> 用来表示一段具体的内容,但是内容默认可能不显示,通过某种手段(如点击)与 <code>legend</code> 交互才显示出来。这跟现在各种通过 JavaScript 隐藏一段内容,在点击后才显示出来的做法有些类似。比如:</p><pre class='html'><code class='html'>一句话记录生活中的点点滴滴,<details> <legend>更多</legend> <p>交流与分享,拉近你和朋友,支持 MSN/GTalk/QQ、短信、手机 WAP</p></details></code></pre><p>它可以有一个 <code>open</code> 的特性,用来显示细节与否。</p><p><code>datagrid</code> 用来控制数据,可以由用户或者脚本来更新。</p><p><code>menu</code> HTML 2 就存在了,不过 HTML 4 把它废弃了。HTML 5 废物利用,并在期内加上 <code>command</code> 元素。</p><p>参考:<a href='http://www.ibm.com/developerworks/web/library/x-html5/index.html'>New elements in HTML 5</a></p>%BF%91A%19%F2%A51X;%FA%88%20%AA/%03%25HTML 5 新增的元素2007-08-10T10:00:00+08:00https://realazy.comRealazy<p>原文:<a href='http://www.digital-web.com/articles/scope_in_javascript/'>http://www.digital-web.com/articles/scope_in_javascript/</a></p><p>作用域(<a href='http://en.wikipedia.org/wiki/Scope_%28programming%29'>scope</a>)是 JavaScript 语言的基石之一,在构建复杂程序时也可能是最令我头痛的东西。记不清多少次在函数之间传递控制后忘记 <code>this</code> 关键字引用的究竟是哪个对象,甚至,我经常以各种不同的混乱方式来曲线救国,试图伪装成正常的代码,以我自己的理解方式来找到所需要访问的变量。</p><p>这篇文章将正面解决这个问题:简述上下文(context)和作用域的定义,分析可以让我们掌控上下文的两种方法,最后深入一种<em>高效的</em>方案,它能有效解决我所碰到的 90%的问题。</p><h2 id="">我在哪儿?你又是谁</h2><p>JavaScript 程序的每一个字节都是在这个或那个运行上下文(execution context)中执行的。你可以把这些上下文想象为代码的邻居,它们可以给每一行代码指明:从何处来,朋友和邻居又是谁。没错,这是很重要的信息,因为 JavaScript 社会有相当严格的规则,规定谁可以跟谁交往。运行上下文则是有大门把守的社区而非其内开放的小门。</p><p>我们通常可以把这些社会边界称为<strong>作用域</strong>,并且有充足的重要性在每一位邻居的宪章里立法,而这个宪章就是我们要说的上下文的<strong>作用域链</strong>(scope chain)。在特定的邻里关系内,代码只能访问它的作用域链内的变量。与超出它邻里的变量比起来,代码更喜欢跟本地(local,即局部)的打交道。</p><p>具体地说,执行一个函数会创建一个不同的运行上下文,它会将局部作用域增加到它所定义的作用域链内。JavaScript 通过作用域链的局部向全局攀升方式,在特定的上下文中解析标识符。这表示,本级变量会优先于作用域链内上一级拥有相同名字的变量。显而易见,当我的好友们一起谈论"Mike West"(本文原作者)时,他们说的就是我,而非 <a href='http://www.mikewest.net/'>bluegrass singer</a> 或是 <a href='http://www.isds.duke.edu/%7Emw/'>Duke professor</a>, 尽管(按理说)后两者著名多了。</p><p>让我们看些例子来探索这些含义:</p><pre class='js'><code class='js'>var ima_celebrity = "Everyone can see me! I'm famous!", the_president = "I'm the decider!";function pleasantville() { var the_mayor = "I rule Pleasantville with an iron fist!", ima_celebrity = "All my neighbors know who I am!"; function lonely_house() { var agoraphobic = "I fear the day star!", a_cat = "Meow."; }}</code></pre><p>我们的全明星,<code>ima_celebrity</code>, 家喻户晓(所有人都认识她)。她在政治上积极活跃,敢于在一个相当频繁的基层上叫嚣总统(即 <code>the_president</code>)。她会为碰到的每一个人签名和回答问题。就是说,她不会跟她的粉丝有私下的联系。她相当清楚粉丝们的*存在 *并有他们自己某种程度上的个人生活,但也可以肯定的是,她并不知道粉丝们在干嘛,甚至连粉丝的名字都不知道。</p><p>而在欢乐市(<code>pleasantville</code>)内,市长(<code>the_mayor</code>)是众所周知的。她经常在她的城镇内散步,跟她的选民聊天、握手并亲吻小孩。因为欢乐市(<code>pleasantville</code>)还算比较大且重要的邻居,市长在她办公室内放置一台红色电话,它是一条可以直通总统的 7x24 热线。她还可以看到市郊外山上的孤屋(<code>lonely_house</code>),但从不在意里面住着的是谁。</p><p>而孤屋(<code>lonely_house</code>)是一个自我的世界。旷恐患者时常在里面囔囔自语,玩纸牌和喂养一个小猫(<code>a_cat</code>)。他偶尔会给市长(<code>the_mayor</code>)打电话咨询一些本地的噪音管制,甚至在本地新闻看到 <code>ima_celebrity</code> 后会写些粉丝言语给她(当然,这是 <code>pleasantville</code> 内的 <code>ima_celebrity</code>)。</p><h2 id="this"><code>this</code>? 那是虾米?</h2><p>每一个运行上下文除了建立一个作用域链外,还提供一个名为 <code>this</code> 的关键字。它的普遍用法是,<code>this</code> 作为一个独特的功能,为邻里们提供一个可访问到它的途径。但总是依赖于这个行为并不可靠:取决于我们如何进入一个特定邻居的具体情况,<code>this</code> 表示的完全可能是其他东西。事实上,<em>我们如何进去邻居家</em>本身,通常恰恰就是 <code>this</code> 所指。有四种情形值得特别注意:</p><h3 id="">呼叫对象的方法</h3><p>在经典的面向对象编程中,我们需要识别和引用当前对象。<code>this</code> 极好地扮演了这个角色,为我们的对象提供了自我查找的能力,并指向它们本身的属性。</p><pre class='js'><code class='js'>var deep_thought = { the_answer: 42, ask_question: function () { return this.the_answer; },};var the_meaning = deep_thought.ask_question();</code></pre><p>这个例子建立了一个名为 <code>deep_thought</code> 的对象,设置其属性 <code>the_answer</code> 为 42,并创建了一个名为 <code>ask_question</code> 的方法(method)。当 <code>deep_thought.ask_question()</code> 执行时, JavaScript 为函数的呼叫建立了一个运行上下文,通过"<code>.</code>"运算符把 <code>this</code> 指向被引用的对象,在此是 <code>deep_thought</code> 这个对象。之后这个方法就可以通过 <code>this</code> 在镜子中找到它自身的属性,返回保存在 <code>this.the_answer</code> 中的值:42。</p><h3 id="">构造函数</h3><p>类似地,当定义一个作为构造器的使用 <code>new</code> 关键字的函数时,<code>this</code> 可以用来引用刚创建的对象。让我们重写一个能反映这个情形的例子:</p><pre class='js'><code class='js'>function BigComputer(answer) { this.the_answer = answer; this.ask_question = function () { return this.the_answer; };}var deep_thought = new BigComputer(42);var the_meaning = deep_thought.ask_question();</code></pre><p>我们编写一个函数来创建 <code>BigComputer</code> 对象,而不是直白地创建 <code>deep_thought</code> 对象,并通过 <code>new</code> 关键字实例化 <code>deep_thought</code> 为一个实例变量。当 <code>new BigComputer()</code> 被执行,后台透明地创建了一个崭新的对象。呼叫 <code>BigComputer</code> 后,它的 <code>this</code> 关键字被设置为指向新对象的引用。这个函数可以在 <code>this</code> 上设置属性和方法,最终它会在 <code>BigComputer</code> 执行后透明地返回。</p><p>尽管如此,需要注意的是,那个 <code>deep_thought.the_question()</code> 依然可以像从前一样执行。那这里发生了什么事?为何 <code>this</code> 在 <code>the_question</code> 内与 BigComputer 内会有所不同?简单地说,我们是通过 <code>new</code><em> 进入 _<code>BigComputer</code> 的,所以 <code>this</code> 表示「新(new)的对象」。在另一方面,我们通过 <code>deep_thought</code></em> 进入 _<code>the_question</code>,所以当我们执行该方法时,<code>this</code> 表示 "<code>deep_thought</code> 所引用的对象"。<code>this</code> 并不像其他的变量一样从作用域链中读取,而是在上下文的基础上,在上下文中<em>重置</em>。</p><h3 id="">函数呼叫</h3><p>假如没有任何相关对象的奇幻东西,我们只是呼叫一个普通的、常见的函数,在这种情形下 <code>this</code> 表示的又是什么呢?</p><pre class='js'><code class='js'>function test_this() { return this;}var i_wonder_what_this_is = test_this();</code></pre><p>在这样的场合,我们并不通过 <code>new</code> 来提供上下文,也不会以某种对象形式在背后偷偷提供上下文。在此, <code>this</code> 默认下尽可能引用最全局的东西:对于网页来说,这就是 <code>window</code> 对象。</p><h3 id="">事件处理函数</h3><p>比普通函数的呼叫更复杂的状况,先假设我们使用函数去处理的是一个 <code>onclick</code> 事件。当事件触发我们的函数运行,此处的 <code>this</code> 表示的是什么呢?不凑巧,这个问题不会有简单的答案。</p><p>如果我们写的是行内(inline)事件处理函数,<code>this</code> 引用的是全局 <code>window</code> 对象:</p><pre class='js'><code class='js'>function click_handler() { alert(this); // 弹出 window 对象}</code></pre><pre class='html'><code class='html'><button id="thebutton" onclick="click_handler()">Click me!</button></code></pre><p>但是,如果我们通过 JavaScript 来添加事件处理函数,<code>this</code> 引用的是生成该事件的 DOM 元素。(注意:此处的事件处理非常简洁和易于阅读,但其他的就别有洞天了。请使 <a href='http://dean.edwards.name/weblog/2005/10/add-event2/'>真正的 addEvent 函数</a>取而代之):</p><pre class='js'><code class='js'>function click_handler() { alert(this); // 弹出按钮的 DOM 节点}function addhandler() { document.getElementById('thebutton').onclick = click_handler;}window.onload = addhandler;```html<button id='thebutton'>Click me!</button></code></pre><h2 id="">复杂情况</h2><p>让我们来短暂地运行一下这个最后的例子。我们需要询问 <code>deep_thought</code> 一个问题,如果不是直接运行 <code>click_handler</code> 而是通过点击按钮的话,那会发生什么事情?解决此问题的代码貌似十分直接,我们可能会这样做:</p><pre class='js'><code class='js'>function BigComputer(answer) { this.the_answer = answer; this.ask_question = function () { alert(this.the_answer); };}function addhandler() { var deep_thought = new BigComputer(42), the_button = document.getElementById("thebutton"); the_button.onclick = deep_thought.ask_question;}window.onload = addhandler;</code></pre><p>很完美吧?想象一下,我们点击按钮,<code>deep_thought.ask_question</code> 被执行,我们也得到了"42"。但是为什么浏览器却给我们一个 <code>undefined</code>? 我们错在何处?</p><p>其实问题显而易见:我们给 <code>ask_question</code> 传递一个引用,它作为一个事件处理函数来执行,与作为对象方法来运行的上下文并不一样。简而言之,<code>ask_question</code> 中的 <code>this</code> 关键字指向了产生事件的 DOM 元素,而不是在 <code>BigComputer</code> 的对象中。DOM 元素并不存在一个 <code>the_answer</code> 属性,所以我们得到的是 <code>undefined</code> 而不是"42". <code>setTimeout</code> 也有类似的行为,它在延迟函数执行的同时跑到了一个全局的上下文中去了。</p><p>这个问题会在程序的所有角落时不时突然冒出,如果不细致地追踪程序的每一个角落的话,还是一个非常难以排错的问题,尤其在你的对象有跟 DOM 元素或者 <code>window</code> 对象同名属性的时候。</p><h2 id="apply-call">使用 <code>.apply()</code> 和 <code>.call()</code> 掌控上下文</h2><p>在点击按钮的时候,我们真正需要的是能够咨询 <code>deep_thought</code> 一个问题,更进一步说,我们真正需要的是,在应答事件和 <code>setTimeout</code> 的呼叫时,能够在自身的本原上下文中呼叫对象的方法。有两个鲜为人知的 JavaScript 方法,<code>apply</code> 和 <code>call</code>,在我们执行函数呼叫时,可以曲线救国帮我们达到目的,允许我们手工覆盖 <code>this</code> 的默认值。我们先来看 <code>call</code>:</p><pre class='js'><code class='js'>var first_object = { num: 42,};var second_object = { num: 24,};function multiply(mult) { return this.num * mult;}multiply.call(first_object, 5); // 返回 42 * 5multiply.call(second_object, 5); // 返回 24 * 5</code></pre><p>在这个例子中,我们首先定义了两个对象,<code>first_object</code> 和 <code>second_object</code>,它们分别有自己的 <code>num</code> 属性。然后定义了一个 <code>multiply</code> 函数,它只接受一个参数,并返回该参数与 <code>this</code> 所指对象的 <code>num</code> 属性的乘积。如果我们呼叫函数自身,返回的答案极大可能是 <code>undefined</code>,因为全局 <code>window</code> 对象并没有一个 <code>num</code> 属性除非有明确的指定。我们需要一些途径来告诉 <code>multiply</code> 里面的 <code>this</code> 关键字应该引用什么。而 <code>multiply</code> 的 <code>call</code> 方法正是我们所需要的。</p><p><code>call</code> 的第一个参数定义了在业已执行的函数内 <code>this</code> 的所指对象。其余的参数则传入业已执行的函数内,如同函数的自身呼叫一般。所以,当执行 <code>multiply.call(first_object, 5)</code> 时,<code>multiply</code> 被呼叫,<code>5</code> 传入作为第一个参数,而 <code>this</code> 关键字被设置为 <code>first_object</code> 的引用。同样,当执行 <code>multiply.call(second_object, 5)</code> 时,<code>5</code> 传入作为第一个参数,而 <code>this</code> 关键字被设置为 <code>second_object</code> 的引用。</p><p><code>apply</code> 以 <code>call</code> 一样的方式工作,但可以让你把参数包裹进一个数组再传递给呼叫函数,在程序性生成函数呼叫时尤为有用。使用 <code>apply</code> 重现上一段代码,其实区别并不大:</p><pre class='js'><code class='js'>...multiply.apply(first_object, [5]); // 返回 42 * 5multiply.apply(second_object, [5]); // 返回 24 * 5</code></pre><p><code>apply</code> 和 <code>call</code> 本身都非常有用,并值得贮藏于你的工具箱内,但对于事件处理函数所改变的上下文问题,也只是送佛到西天的中途而已,剩下的还是得我们来解决。在搭建处理函数时,我们自然而然地认为,只需简单地通过使用 <code>call</code> 来改变 <code>this</code> 的含义即可:</p><pre class='js'><code class='js'>function addhandler() { var deep_thought = new BigComputer(42), the_button = document.getElementById("thebutton"); the_button.onclick = deep_thought.ask_question.call(deep_thought);}</code></pre><p>代码之所以有问题的理由很简单:<code>call</code><em>立即</em>执行了函数(译注:其实可以用一个匿名函数封装,例如 <code>the_button.onclick = function(){deep_thought.ask_question.call(deep_thought);}</code>,但比起即将讨论的 <code>bind</code> 来,依然不够优雅)。我们给 <code>onclcik</code> 处理函数一个函数执行后的结果而非函数的引用。所以我们需要利用另一个 JavaScript 特色,以解决这个问题。</p><h2 id="bind"><code>.bind()</code> 之美</h2><p>我并不是 <a href='http://prototypejs.org/'>Prototype JavaScript framework</a> 的忠实粉丝,但我对它的总体代码质量印象深刻。具体而言,它为 <code>Function</code> 对象增加一个简洁的补充,对我管理函数呼叫执行后的上下文产生了极大的正面影响:<code>bind</code> 跟 <code>call</code> 一样执行相同的常见任务,改变函数执行的上下文。不同之处在于 <code>bind</code> 返回的是函数引用可以备用,而不是 <code>call</code> 的立即执行而产生的最终结果。</p><p>如果需要简化一下 <code>bind</code> 函数以抓住概念的重点,我们可以先把它插进前面讨论的乘积例子中去,看它究竟是如何工作的。这是一个相当优雅的解决方案:</p><pre class='js'><code class='js'>var first_object = { num: 42,};var second_object = { num: 24,};function multiply(mult) { return this.num * mult;}Function.prototype.bind = function (obj) { var method = this, temp = function () { return method.apply(obj, arguments); }; return temp;};var first_multiply = multiply.bind(first_object);first_multiply(5); // 返回 42 * 5var second_multiply = multiply.bind(second_object);second_multiply(5); // 返回 24 * 5</code></pre><p>首先,我们定义了 <code>first_object</code>, <code>second_object</code> 和 <code>multiply</code> 函数,一如既往。细心处理这些后,我们继续为 <code>Function</code> 对象的 <a href='http://en.wikipedia.org/wiki/Prototype-based_programming'>`prototype`</a> 定义一个 <code>bind</code> 方法,这样的话,我们程序里的函数都有一个 <code>bind</code> 方法可用。当执行 <code>multiply.bind(first_object)</code> 时,JavaScript 为 <code>bind</code> 方法创建一个运行上下文,把 <code>this</code> 置为 <code>multiply</code> 函数的引用,并把第一个参数 <code>obj</code> 置为 <code>first_object</code> 的引用。目前为止,一切皆顺。</p><p>这个解决方案的真正天才之处在于 <code>method</code> 的创建,置为 <code>this</code> 的引用所指(即 <code>multiply</code> 函数自身)。当下一行的匿名函数被创建,<code>method</code> 通过它的作用域链访问,obj 亦然(不要在此使用 <code>this</code>, 因为新创建的函数执行后,<code>this</code> 会被新的、局部的上下文覆盖)。这个 <code>this</code> 的别名让 <code>apply</code> 执行 <code>multiply</code> 函数成为可能,而传递 <code>obj</code> 则确保上下文的正确。用计算机科学的话说,<code>temp</code> 是一个闭包(<a href='http://www.jibbering.com/faq/faq_notes/closures.html'>_closure_</a>),它可以保证,需要在 <code>first_object</code> 的上下文中执行 <code>multiply</code>,<code>bind</code> 呼叫的最终返回可以用在任何的上下文中。</p><p>这才是前面说到的事件处理函数和 <code>setTimeout</code> 情形所真正需要的。以下代码完全解决了这些问题,绑定 <code>deep_thought.ask_question</code> 方法到 <code>deep_thought</code> 的上下文中,因此能在任何事件触发时都能正确运行:</p><pre class='js'><code class='js'>function addhandler() { var deep_thought = new BigComputer(42), the_button = document.getElementById("thebutton"); the_button.onclick = deep_thought.ask_question.bind(deep_thought);}</code></pre><p>漂亮。</p>%16%02%C5%04%AF%F1%C6%F8%5C%5C%D9%1A:%92%FE%12JavaScript 中的作用域2007-07-18T10:00:00+08:00https://realazy.comRealazy<p>这两条是关于 IE 环境中的 CSS 的。</p><ol><li>不要使用 <code>import</code> 引入 CSS,可以避免内容的无样式瞬间([FOUC][0])问题。</li><li>不要把样式的 <code>link</code> 放到页面后(</body>之前),以防止页面 6-10 秒的空白。</li></ol><p>这条是关于 Firefox 的。</p><p>在 Firefox 里,嵌入 flash 影片的 HTML 代码存在 <code>wmode</code> 特性时,在某些 <code>position:relative</code> 的元素内,会产生不可点击的现象(很遗憾,我总结不出具体条件,只知道必备条件是这个)。解决方法,只能依赖于 JavaScript。思路是,暂时改变的了的元素的 <code>position</code> 为 <code>static</code>, 然后恢复。JavaScript 代码大致如下:</p><pre class='js'><code class='js'>function fixSWFUnclickable(wrapper){ wrapper.style.position = 'static'; setTimeout(function(){wrapper.style.position = 'relative';}, 1);}``以上。[0]: http://bluerobot.com/web/css/fouc.asp/</code></pre>%C5~%8A=%8D%C0=%CC1t%EC%88%980*#小巧三条2007-07-13T10:00:00+08:00https://realazy.comRealazy<p>之前看过 <a href='http://twittervision.com'>TwitterVison</a>,便萌生使用<a href='http://fanfou.com'>饭否</a>的数据搞一个类似的应用。遗憾的是谷歌地图不能使用地理译码(知道我为何不说 Google Maps 了吗),之前我想自己去搜集全国城市的经纬度,但觉得太浪费时间了。于是又研究了一下<a href='http://51ditu.com/'>我要地图网</a>,他们能够提供地理译码,但是地图观感和动画效果却比不上谷歌地图。</p><p>于是来个偷梁换柱法,使用我要地图网的 API 获取地理译码,然后交给谷歌地图来显示。从而诞生了我的我的第一个地图 mashup——口号是「<strong>海内存知己,天涯共饭否</strong>」的共饭:<a href='http://realazy.com/projects/gongfan/'>http://realazy.com/projects/gongfan/</a>。欢迎赏光 :) 动画效果和数据的抓取算法还比不上 TwitterVison,有空会逐步改进的。</p><p>以上。</p><p><strong>更新</strong>:整理出一份<a href='http://realazy.com/projects/gongfan/js/geocode.js'>地理译码索引</a> 出来,这样就不用每次查询,效率提升不少。</p><p>JavaScript 源文件<a href='http://realazy.com/projects/gongfan/js/gongfan.js'>在此</a>,欢迎交流与指正。</p>ht%14%B4%7D%A4%01%BA%C9%EC%5C%1Bd(%EAe第一个地图 mashup——共饭2007-06-15T10:00:00+08:00https://realazy.comRealazy<p>莫非这是 WWDC 2007 的礼物之一?请访问 <a href='http://www.apple.com/safari/'>http://www.apple.com/safari/</a>。</p><p>经测试,这个 beta 版对中文支持严重不足。只有满足以下条件才可以正确显示:</p><ol><li>你的系统装有微软正黑体(microsoft jhenghei)</li><li>正在浏览的网站字体优先指定为微软正黑体</li></ol><p>很凑巧,我的 blog 符合第二个条件。请穿墙看截图:<a href='http://www.flickr.com/photos/realazy/541766405/'>http://www.flickr.com/photos/realazy/541766405/</a>.</p><p>尝鲜 Safari 3 on Windows,那么装上微软正黑体后,可以访问我的网站来测试(可能是世界上唯一能够在当前版本的 Safari 3 on Windows 正确显示的中文网站?)。</p><p>以上。</p><p>更新:Safari 3.0.2 已经支持中文的显示。</p>%E8%A7%AC%08%7Ba%01%01S/%E8%BC%E5%C2%F3%E9Safari 3 Windows 版2007-06-12T10:00:00+08:00https://realazy.comRealazy<p>如果你曾经试图使用 Flickr JSON 格式的 API,第一眼一定会感到奇怪,这真的是 JSON 吗?不信请看 <a href='http://api.flickr.com/services/feeds/photos_public.gne?format=json&tags=flower'>http://api.flickr.com/services/feeds/photos_public.gne?format=json&tags=flower</a>.</p><p>诸如 <code>jsonFlickrFeed({...})</code> 看起来更像一个函数的调用。细心一看,确实不错,它把 JSON 作为 <code>jsonFlickrFeed</code> 函数的参数输出了。本人猜测这是安全方面的原因。不过这样一来,我们如何才能把 JSON 抽取出来为我所用?Flickr 能提供这种形式的 API,那也肯定能用。</p><p>其实,前面我已经一语道破天机,jsonFlickrFeed 是一个函数,那么我们先定义好它,然后通过 <code>script</code> 的 <code>src</code> 引入 API 地址执行即可。</p><p>请参考:<a href='http://realazy.com/lab/flake/'>http://realazy.com/lab/flake/</a></p>%92%99%C4%A7%11%C7X;+%17/j%5C%7D%AD%87如何使用 Flickr JSON 格式的 API2007-05-22T10:00:00+08:00https://realazy.comRealazy<p>写一个插件,用于处理 tag 的复制,类似 del.icio.us 插件处理 tag 的形式。</p><p><a href='http://realazy.com/projects/tagto/index-zh.html'>http://realazy.com/projects/tagto/index-zh.html</a></p>%B0%A4%C3%99%A2%A7,0p%3CkU%07%01Sy我的第一个 jQuery 插件——tagTo2007-05-18T10:00:00+08:00https://realazy.comRealazy<p>不要使用 <code>white-space:nowrap</code>.</p><p>见:<a href='http://realazy.com/lab/avoid_half_character/'>http://realazy.com/lab/avoid_half_character/</a></p>R%BB%25%1A%13%CB%A1%03~?%19%CA%EE%3C%C9%A4防止截掉半个汉字2007-05-17T10:00:00+08:00https://realazy.comRealazy<p>XHTML 已死,请准备迎接新的 <a href='http://www.whatwg.org/specs/web-apps/current-work/'>HTML 5</a>.</p><p>在未来,<a href='http://www.456bereastreet.com/archive/200705/browsers_will_treat_all_versions_of_html_as_html_5/'>所有的伺服为 `text/html` 的标记都将被视为 HTML 5 处理</a>。</p><p>XHTML,尽管 Firefox 和 Opera 有所支持,但是已名存实亡。它并未见得不先进,但是历史的现实(试想想如果转换到真正的 XHTML,有多少网页会由于不良够而无法显示)无情地把它丢进了历史的垃圾桶中。尽管如此,Web 标准观念已经深入人心,尽管核心概念跟 XHTML 并未有根本的联系,但是 XHTML 曾作为一个推广标准的急先锋并成为某些人忽悠、炫耀的名词功不可没。</p><p>尽管如此,当今运行在世界各个角落的 web 网站或者 web 应用,除了一些热心的个人追随者,基本上所有的都是 HTML, CSS 和 JavaScript 纠缠不清,分离的理念有所推进但也不是灵丹妙药。</p><p>除了可维护性那么一点点可怜的理由,为见得分离就有什么强大的好处。accessibility? 开发另外一个版本的成本未见得维护一个「万能」的版本要高。</p><p>web 标准的价值有限。HTML 和 CSS 充其量是构建简单界面的标记语言,把界面表示出来就是他们的最大用处。同样,JavaScript 能把动态效果运行起来,给用户最佳体验,只要以一种可维护性较高的方式编写即可。当然,就目前来说,可维护性较好的方式,还是 web 标准的分离方式。</p><p>xhtml 已死,web 标准的含义亦函待改进。否则说不定明天就会有 web 标准的可替代方案(XAML? XUL?)。</p><p>当然,我在这里并没有号召大家不要管 web 标准了,在当前来说,它是最先进的,你还是需要学习并理解运用它。当然,不要受到条条框框的束缚,我其实想表达的是:在 web 中,实用主义是最高指导思想。你不仅需要理解,还需要变通。</p><p>期望 HTML 更好的未来。</p>%B0%C15%8F%C9p%A7%F4%F7N!%B4D%F1%C9#HTML 新变数2007-05-04T10:00:00+08:00https://realazy.comRealazy<p>那么请看 demo: <a href='http://realazy.com/lab/bubble/'>http://realazy.com/lab/bubble/</a> .</p><p>唧唧歪歪无益,还会浪费读者宝贵时间,以后我倾向于写少于 100 字但能说明问题的 blog,欢迎大家监督 :)</p><p>以后我的 blog 将是 demo 的世界,呵呵。</p>%B6%1A%DB%10%F8%B7%8A(kA)%DD%C0%A5%FF%C4如果你还不理解 JavaScript 的事件冒泡2007-04-09T10:00:00+08:00https://realazy.comRealazy<p>有时候,你会发现,在一些 JS 应用中,涉及到 CSS 的重新渲染(即页面样式需要更新)的话,IE 死活不变。</p><p>此时你需要让 IE 重新渲染一下:</p><pre class='js'><code class='js'>function handleIEhasLayout() { //trigger re-rendering document.body.style.zoom = 1.1; //restore it document.body.style.zoom = "";}</code></pre><p>有问题,记得执行一下 handleIEhasLayout,万事 OK。</p><p>bug 重现以及延伸阅读:<a href='http://old9.blogsome.com/2006/07/26/ie6-reflow-bug/'>http://old9.blogsome.com/2006/07/26/ie6-reflow-bug/</a></p>(%06%91%7D%7DQ%D9%B4%C2%89%0F%E0%DEz%0E?解决 IE 在 JS 下不渲染的 bug2007-04-02T10:00:00+08:00https://realazy.comRealazy<p>遥想当年没有刻录机,硬是将 50 多个不同的 linux distros 从硬盘或者 vmware 安装到了实际硬盘上,练就了一手不需光驱/软驱安装 linux 的好功夫。</p><p>但是,我昨天碰到麻烦了,同样,我从硬盘给我的本本安装了 vista 之后,无福消受,本本罢机。更不幸的是,此时我才发现本本的光驱挂了,无法读盘。这下麻烦大了,即使不能装 windows,至少得整个 linux 上去。</p><p>万幸万幸,在我的本本的 BIOS 里发现它可以 boot from removable device,呵呵,就是说,它能从 U 盘启动。时代进步了,我玩 linux 那时 U 盘洛阳纸贵,加上那时的主板也没有支持,所以,我决定今天好好补补,这样,我的绝活——安装 linux 得以百尺竿头,更进一步。</p><p>我的安装目标是 <a href='http://archlinux.org/'>Arch linux</a>. 当然,Google 是最好的老师,很容易找到了 <a href='http://wiki.archlinux.org/index.php/Usb_Drive_Arch_Install'>Usb Drive Arch Install</a>。它下面都是一些 linux 下的命令,虽然另一台我弟用的台式机上没有 linux,但基本上都是拷贝文件的操作,所以对我没有影响。把 arch base 的光盘 iso 下载过来,解压出来,按照其步骤:</p><ol><li>把 U 盘(我的是读卡器+我手机的 miniSD 卡)格式为 FAT16</li><li>把光盘文件拷贝到 U 盘上</li><li>把光盘 <code>isolinux</code> 内的 <code>boot.*</code>, <code>initrd.img</code>,<code>isolinux.cfg</code> 拷贝到 U 盘根目录下,并将 <code>isolinux.cfg</code> 更名为 <code>syslinux.cfg</code></li></ol><p>ok, 最后一步需要 <code>syslinux</code>,还好,这个由 windows 版,请到 <a href='http://syslinux.zytor.com/download.php'>http://syslinux.zytor.com/download.php</a> 下载。打开 <code>cmd</code>,假如你的 U 盘盘符是 <code>F:</code>, 则使用 <code>syslinux.exe F:</code>. (收获不小,发现大部分 linux 发行版其实都是用 syslinux 来做启动盘的)。</p><p>至此,你的可启动 U 盘已经完毕,记得设置 BIOS 从 USB 设备启动,你就可以像光盘启动一样安装 linux 了,比硬盘安装方便多了。</p><p>如果确认你的机器是可以从 USB 设备启动的,而你按照我上述步骤做了之后发现 U 盘无法启动,那么你可能需要一个叫 <a href='http://www.google.cn/search?complete=1&hl=zh-CN&q=USBoot&btnG=Google+%E6%90%9C%E7%B4%A2&meta='>USBOOT</a> 的工具帮助你把 U 盘重置参数(使用 0 或 1):</p><p><img src='http://farm1.static.flickr.com/199/441700591_5f54a32216_o.png' alt='usboot' /></p><p>我的 miniSD 卡就是经过使用 1 重置参数的洗礼后才被本本的启动认识……</p>%13%C7.%01%5C%91u%BF'V%AB2%B5%25%15%04从 usb 启动安装 linux2007-04-01T10:00:00+08:00https://realazy.comRealazy<p>如果说 ppk 是浏览器的测试专业户,那么 Dean Edwards 就是浏览器的技术解决专业户。自从出了一批成为众多框架/库的参考,如 IE7, cssQuery, Base 等,沉寂了一段时间,终于,有新料放出了:<a href='http://dean.edwards.name/weblog/2007/03/yet-another/'>base2.DOM</a>。</p><p>他还挺诙谐,标题叫做 Yet Another JavaScript Library Without Documentation,对,世界上又一个没有文档的 JavaScript 库诞生了……</p><p>没有文档难道是光荣的?非也,因为 base2.DOM 遵循 W3 的标准(甚至包括草案),文档就散落在这些标准中。另外,遵循标准,让您的代码 <strong>future-proof</strong>! 这是最大的意义所在。现在满世界的框架/库,每种都有自己的特色 API,命名方式千奇百怪,未见得对开发者友好。base2.DOM 使用标准的 API 命名,不仅友好,而且,现在你就可以进入未来,难道你不想在 IE 上能使用 <code>forEach</code>, <code>DOMContentLoaded</code>?Just have a try...</p><p>Dean 的原文浅显易懂,我也不掺和什么了,特色请看原文的 Highlights。总而言之,这些标准的 API 不用怕找不着文档,一般都可以在 <a href='http://developer.mozilla.org/en/docs/Main_Page'>Mozilla Developer Center</a> 上找到答案,大部分还有例子。</p><p>使用它,你不必像其他库一样必须按照它的方式来写 JS,你可以写出最 raw 的 JS。</p><p>使用它写了个拖放的例子作为练习,标准的方式让人心情愉快:<a href='http://realazy.com/lab/base2/dragsort.html'>http://realazy.com/lab/base2/dragsort.html</a>。</p>%7D%E7%60%E5%60%3C%13,%CBF%D1%A4%15%88k%C1base2.DOM2007-03-25T10:00:00+08:00https://realazy.comRealazy<p>非已知高度的垂直对齐的条件:</p><ul><li>表格单元格</li><li>行内块(<code>inline-block</code>)</li></ul><p>第一个比较好理解,第二个很多人可能会忽略。比如,很多人在图片与文本混排的时候想让图片相对于文本垂直居中除了用 IE 的私有特性 <code>valign="absmiddle"</code> 别无他法。记住,默认下图片就是属于 <code>inline-block</code>,你只需简单的 <code>img {vertical-align:middle;}</code> 即可。</p><p>那么,我们进入主题。现在我们由于某些特殊需要两个并排的 <code>div</code> 想实现垂直居中。如前所述,<code>div</code> 非表格,但是当代的浏览器中除了 IE 都支持 <code>display:table-cell</code>。恰好,IE 支持 <code>dispaly:inline-block</code>,那么我们就用两种方式为当代浏览器实现非表格的垂直居中,殊途同归。</p><p>HTML 如下:</p><pre class='html'><code class='html'><div id="div1">blah blah...看见我居中了吗?</div><div id="div2"> <p>blah blah...</p> ....</div></code></pre><p>CSS 如下:</p><pre class='css'><code class='css'>#div1,#div2 { display: table-cell; *display: inline; zoom: 1; vertical-align: middle;}</code></pre><p>我们来重点分析 CSS。如您所知,<code>*property</code> 是一个只有 IE(包括 IE7)才能解析的 hack. 那么为何是 <code>inline</code> 而不是 <code>inline-block</code> 呢?这跟 IE 的变态工作方式有关,具体不深究。在此你只需知道加上 <code>zoom:1</code> 后,就等价于 <code>inline-block</code>。另外,如果是 <code>a</code>, <code>span</code> 等非 <code>block</code> 的元素,则按正常方式 <code>display:inline-block</code>。</p><p>OK, 既然是 tip, 废话不宜多,自己看例子:<a href='http://realazy.com/lab/div-valign/'>http://realazy.com/lab/div-valign/</a>.</p><p><strong>Update:</strong>严格地说,IE 确实不支持 <code>inline-block</code>,这就是为什么直接赋予 <code>div</code> 会不生效的问题。准确地说,在 IE 中,为 <code>inline</code> 赋予 <code>inline-block</code> 会使 IE 触发 hasLayout,从而获得部分 <code>inline-block</code> 的表现。请参考:<a href='http://cn.autos.yahoo.com/as2007/sub1/index.html'>http://cn.autos.yahoo.com/as2007/sub1/index.html</a>.</p>%18%F5%9B%FE%C8%9Dj%F4%BB%F9%FA%20%19%A4%8Ex未知高度的非表格垂直对齐2007-03-10T10:00:00+08:00https://realazy.comRealazy<p>本人非计算机,亦非心理学,或者交互设计,更非设计专业出身,因此什么都是半桶水。即使如此,依然靠着兴趣寻找乐趣。对于设计,爱之,但没有受过系统培训,更多时候只是从用户角度来考虑问题。今偶读一文,叫 <a href='http://bokardo.com/archives/five-principles-to-design-by/'>Five Principles to Design By</a>,跟本人观点颇投缘,虽然有些观点不是很认同(如:设计不是艺术,考究起来,现在啥都可以冠上艺术二字,何况设计呢),但道理挺得我心,遂花两小时,冒着上班迟到的危险,翻译出来,与众分享之。水平有限,若有不通或歧义,请多多指出指正指教,不胜感激。以下是正文:</p><h2 id="">技术为人民服务</h2><p>人们经常埋怨自己的技术短处。当电脑当机,他们会说:「我一定是干了某些愚蠢的事情」。面对一个设计糟糕的网站,他们会说:「我一定是太蠢了,我竟找不着北」。他们很有可能去买一本傻瓜指南。</p><p>很不可思议!人们在使用技术时不应该感到挫折。就像客户,用户永远是对的。如果软件崩溃了,那是软件设计师的错;如果在某个网站上找不着北,那是 web 设计师的错。但这也不表示设计师要羞愧低头……他们更应该把这看作是一个学习的机会!好设计师与坏设计师最大的差别在于,如何处理他们的让用户正折腾着的设计。</p><p>技术为人民服务。人民不会为技术服务。</p><h2 id="">设计非艺术</h2><p>艺术是关于个人表达的。它关于生命,关于情感——艺术家的想法和主意。它不太关注第三者干什么,活动也不是必须的,仅是他们的鉴赏。艺术<strong>实践</strong>不需要这些。鉴赏是艺术家的必需品,同时他们也是孤独的。</p><p>设计,从另一方面来说,是关于<strong>使用</strong>的。设计师需要别人来<strong>使用</strong>(而不仅仅是鉴赏)他们的作品。设计不存在不让人们使用它的目的。设计辅助解决人类问题。我们能授予一个设计的最高荣誉不是它多漂亮,多艺术,而是它用途多广。</p><p>不像艺术,设计<strong>总是</strong>存在于情景中的。情景对一个设计至关重要:它要解决什么问题?为谁设计?在什么时期?这就是为何设计与技术如此紧密的缘故。因为技术变化太快,所以设计必须步随其后。十年前的设计在今天可能不值一提。充满精彩设计的历史没有必要继续存在。</p><p>伟大的艺术,从另一角度说,永远风行。就算今天我们可以复制一百万个大卫像,我们依然欣赏米开朗基罗的原作,因为它是一个人的辛劳和表达。艺术永远不会凋谢。伟大的设计则依赖于它产生的年代和要解决的问题。但艺术不这样,艺术是永恒的。</p><p>做个测试。当人们享受艺术时,说:「我喜欢」;当人们享用设计时,说:「做得不错」。这并非偶然。好的设计就是能干活的料。</p><h2 id="">体验属于用户</h2><p>设计师不生产体验,他们制作的是体验用品。看似细微的差别却有很大不同,因为它把设计师放在服务用户的位置上,而非其他方式。这并不是要扼杀创新,它并不会妨碍设计师跳过上述关于艺术的阐述。它只意味着,设计的体验并不会简单地因为设计师说这般这般就会这般这般地发生,当用户提交反馈时,真实的体验就确确实实发生了。</p><p>最终的体验发生在用户那里,体验是属于他们的。</p><h2 id="">伟大的设计是无形的</h2><p>伟大的设计一个有趣的属性是,它来源于认可。用得爽的时候我们忘了创造它时的种种艰辛。有时,就像普通的汤匙,显而易见,这东西太简单了,但我们会忽视了在历史的某一时期它并不这样。还有,就像汽车,这东西尽管很复杂但依然容易使用,但是我们却对于它背后数以万计的工时视而不见。这是一种羞耻……每一个伟大的设计都有丰富的历史。每一设计背后都是一个或一群设计师,他们想通过解决某些问题来让世界更美好。</p><p>烂的设计显然是因为它伤害了使用。它拙劣,困难和复杂。最为讽刺的是,这个世界上烂的设计比好的设计更容易碰着。它像暴徒一样强奸着我们的头脑。它像恶徒一样在我们面前逞威风。因为有用,伟大的设计通常都是无形的。</p><h2 id="">简洁是终极哲学</h2><p>如 Saint Exupery 所说:「设计师知道自己要达到完美,不在于无物可增,而在于无物可减」。简洁自成一体:知道什么要加和什么要扔……当它作用时,就像魔术般实现预期效果,因为没有任何复杂的东西传递给用户……只有简洁。这是设计师的最高成就。</p>%9Eg%9E%0BI%E7=%DE%C9%1F%AD9%7D%EB?p设计五原则2007-03-07T10:00:00+08:00https://realazy.comRealazy<p>开通一个「每周一巧」的新分类,每周都会发布一条有关 XHTML, CSS 和 JavaScript 等的小技巧。一来分享经验,二来防止本人越来越懒,保证每周至少有一篇 blog,也好让本人的 feed 被刷得勤快点。另外宣布一个消息,ppk on javascript 已经中止笔记啦(虽然我已经读完了),因为这本书的中文版已经在翻译了(不是我),避免重复劳动,就决定停止了,总的来说,我认为精华都在我所做笔记的前两章里了 :)</p><p>那么,开篇第一巧是:<strong>防止文本选择</strong>。</p><p>当然啦,我没说禁止,因为我们的目的绝不是说防止读者从我们的网站拷贝东西(你想用于此目的也是可以的,但只能防菜鸟而已啦),我们的目的是防止一些默认的选择行为,以让我们的 JavaScript 程序运行起来更平滑。举个例子吧,现在的拖放程序是满天飞啊,你知道,在浏览器里,按着鼠标拖动的默认行为是:鼠标经过的文本会被选中,反白高亮,这就导致不好的用户体验(凡事说到这个<strong>大词</strong>,绝对有搞头,我也不能免俗,sorry)了。好吧,为避滥增字数的嫌疑,我就把本周的即时贴公布如下:</p><pre class='js'><code class='js'>function disableMouseSelection(element) { if (element.onselectstart !== undefined) { element.onselectstart = returnFalse; } else if (element.style.MozUserSelect !== undefined) { element.style.MozUserSelect = "none"; } else if (element.style.KhtmlUserSelect !== undefined) { element.style.KhtmlUserSelect = "none"; } else { element.onmousedown = returnFalse; } if (element.ondrag !== undefined) { element.ondrag = returnFalse; }}disableMouseSelection(document.documentElement); //把 document 的根传入,就可以防止整个页面被选择的可能了</code></pre><p>既然是巧,一切都是自我解释,就没有什么好阐述的了。彪悍的人生不需要 XX,强悍的代码也不需要注释。其实无形中我也公布第二巧了,就是,在基于 Gecko 和 KHTML 的浏览器中,防止选择文本是可以通过样式设置来实现了,即</p><pre class='css'><code class='css'>selector { -moz-user-select: none; -khtml-user-select: none; user-select: none;}</code></pre><p>废话既然已经一箩筐,我就继续展开吧。其实这是 CSS3 中 User Interface 的一个性质:<code>user-select</code>,Gecko 和 KHTML 让我们超前体验。更多详细的,你问 Google 或者你觉得<em> 百度一下,你就知道</em>,那就百度吧 XD。</p>&)%3C%CAf%02%7B%1FP%A8%F2%B3%F2%FB%CA%5B每周一巧开通暨第一巧发布2007-02-27T10:00:00+08:00https://realazy.comRealazy<h2 id="">页签的流行</h2><p>自从 Yahoo!的首页引进页签(tab, 见下图)之后,这种可用性极佳的方式越来越受欢迎,用户也逐步习惯和喜欢上它,因为它可以在原有的空间上增加更多的可用信息,而且只需切换,不需刷新整个页面,浏览更舒畅。很多网站接受并使用,如新浪等。</p><p><img src='/assets/posts/2007_02_22/tab_example.png' alt='页签样例' /></p><h2 id="">页签的标记结构</h2><p>那么,让我们来看看这些页签后的代码。</p><p>新浪完全不考虑什么标准,就是表格嵌套,我们略过不提。Yahoo!的 XHTML 形式是这样的:</p><pre class='html'><code class='html'><div> <ul> <li>页签1</li> <li>页签2</li> ... </ul></div><div> <div>内容1</div> <!--它们可能由 Ajax 载入--> <div>内容1</div> ...</div>...</code></pre><p>符合标准,但却没有语义。页签和相应内容没有任何关联。也就是说,在没有 CSS 展现的情况下,用户并不晓得页签究竟对应哪一块内容。而且就 JavaScript 实现来说,必须对元素定义更多的 id 或者 class 作为调用钩子(hook),容易造成代码冗余。有人对此作出改良,使用连接元素的 hash(即 #号后的字段)跟内容进行关联,即下面这种形式:</p><pre class='html'><code class='html'><div> <ul> <li><a href="#content1">页签1</a></li> <li><a href="#content2">页签2</a></li> ... </ul></div><div> <div id="content1">内容1</div> <!--它们可能由 Ajax 载入--> <div id="content2">内容1</div> ...</div>...</code></pre><p>这种方式对于机器来说,确实找到了关联点,而且用户点击的时候,也能在 hash 的作用下(传统说法中的「锚点」)调到相应的内容区块。有进步,但还是不够语义。</p><h2 id="">语义,语义,语义!</h2><p>理想中标准的、语义的 tab 代码应该是怎么样的呢?在我看来,应该是这样:</p><pre class='html'><code class='html'><dl> <dt>页签1</dt> <dd>内容1</dd> <dt>页签2</dd> <dd>内容2</dd></dl></code></pre><p>当然,我认为并不是 Yahoo!的设计师/开发者并不了解语义,可能是由于某些特殊的需求在这种代码下可能会实现不了,只好采取折衷方案。是的,在这种代码形式下,语义虽能充分体现,但是要实现页签的表现形式,确实是一个难题。</p><h2 id="">解决之道</h2><p>首先,请打开我们的 <a href='http://realazy.com/lab/s2utab/'>Demo 页面</a>,先自行分析一下。如果您使用 Firefox,可以尝试把 CSS 样式禁用进行「欣赏」(如果您装了 Web developer toolbar,您可以 <code>CTRL</code> + <code>SHIFT</code> + <code>S</code>)。继续。</p><h3 id="dt">解决 <code>dt</code> 的横排</h3><p><code>dt</code> 与 <code>dd</code> 交错,如何能够使得 <code>dt</code> 排在一行上?well,理论不分析太多,要使它们在一起,我们假设 <code>dd</code> 不存在。这样的话,使用 float 就能排在一起。既然 <code>dd</code> 不能不存在,ok,那么让它们脱离文档流,如何做?<code>position:absolute;</code> 就可以了。但是 IE6 有问题,wtf . 我的解决方法是,使用 JavaScript 把所有的 <code>dt</code> 凑一块,这样严重伤害了语义,但这只是一个浏览器问题,而且是在有 JavaScript 的时候才产生语义问题,阿弥陀佛,辩证法认为事物都具两面性。</p><h3 id="dd">解决 <code>dd</code> 的自适用高度</h3><p>对于已经 <code>position:absolute;</code> 了的 <code>dd</code>,无论是理论还是实践,使用纯 CSS 都没有解决方法。同样,我使用了 JavaScript 来动态计算它的每一次高度,然后赋予整个 <code>dl</code>。</p><h3 id="">局限性与缺点</h3><p>这样做保证了标准、语义、Unobtrusive。但对于少部分拥有能解析 CSS 的先进浏览器但却关闭了 JavaScript 的用户来说,极有可能会被不能自适用高度的页签下的内容区块挡住了跟随在后的信息。</p><h2 id="s2uTab">s2uTab</h2><p>很高兴我能写出一些实用的 JavaScript,以上所提到的,我将之命名为 s2uTab -- 偏要解释的话,它就是 Standard, Semantic, Unobtrusive Tab 的缩写。首先,它很小,不依赖于任何库,在 IE6+, opera 9+, Firefox 2+均通过测试(若您有 Safari,务必帮忙测试一下,谢谢)。其次,灵活,除了 <code>dl</code> 外无须任何钩子,且页签数目灵活没有限制。再次,您可以指定页签切换的事件形式,可以指定初始的页签是哪个。</p><h3 id="">用法</h3><p>您可以为 <code>window.onload</code> 添加如下函数:</p><p><code>s2uTab(页签, 事件类型, 初始页签);</code></p><p>其中,页签指 <code>dl</code> 元素的引用,如果您传入的是字符串,则返回 <code>id</code> 是改字符串的 <code>dl</code> 引用;事件类型是指,页签的激活是点击还是鼠标悬停,传入 <code>click</code> 或者 <code>mouseover</code>(注意:没有"on"!)即可;初始页签是指您在初始化页面时需要激活的页签,注意,为符合编程习惯,请从 0 算起。</p><p>请多多参考我们的<a href='http://realazy.com/lab/s2utab/'>例子</a>。</p>%BCP%D2%01/%E3%FA%7Cn%05n%3E%C6%FB%AF%1E标准的、语义的、Unobtrusive 的页签切换2007-02-22T10:00:00+08:00https://realazy.comRealazy<p>抱歉还没有找到合适的词来翻译这三个单词的组合。它出自 <a href='http://en.wikipedia.org/wiki/Edward_Tufte'>Edward Tufte</a>,意思是:"Make all visual distinctions as subtle as possible, but still clear and effective",翻译过来就是说,让元素视觉差别尽可能细微,但依然保证清晰和效率。一个典型的例子是:</p><p><img src='/assets/posts/2007_02_20/difffont.png' alt='nytimes navigation' /></p><p>(图片来自 <a href='http://garrettdimon.com/archives/2007/2/19/typographical_contrast/'>Typographical Contrast</a>)</p><p>这是 <a href='http://nytimes.com/'>The New York Times</a> 的导航系统。看上去有什么特别的吗?没有。但是可以很明确地知道哪些是主导航,哪些是次级导航吗?答案是明确的,但是从视觉上说,并不见得是显而易见的,但你却可以明确区分主次,何也?难道真只可意会而不可言传?让我们来分析一下吧。</p><p>最重大的视觉差别在哪?颜色?确实次级导航颜色稍微浅一点。等等,我们来做个实验。以下是我处理过的图片:</p><p><img src='/assets/posts/2007_02_20/samefont.png' alt='nyt navigation with same font' /></p><p>这下,仔细看,依然可以区分主次,但是得费更大的劲。注意,我只是把次级导航的字体族(font family)改了一下,从视觉上说,这是很细微的变化,如果不细心根本感觉不出来。但是给你的感觉发生了很大的变化,很明显,主次的清晰性大打折扣,即使是细微到很难觉察(当然,如果你是一个称职的视觉设计师,你一眼就能看出来)的变化。</p><p>这就是<strong>Smallest effective difference</strong>。如果感兴趣,推荐你继续这篇:<a href='http://www.37signals.com/svn/posts/137-design-decisions-basecamp-help'>Design Decisions: Basecamp help</a>.</p><p>让我们看看百度之前的首页(由于找不到相关截图,这是我自己加工的,估计就这么个样,如有不对或者你有之前的真实截图,请指出或者提供,谢谢):</p><p><img src='/assets/posts/2007_02_20/baidu_prev.png' alt='百度推行空间前的首页' /></p><p>有一天李老板说,我们要强势推广空间,设计师给我想个方案,在首页上重点突出空间。相信,设计师也准备了大量的方案,套红,加粗,加大等等,这些都是突出的手段。但是,太过突兀会不会伤害用户体验?有没有可能保证空间这一条目跟其他条目之间的差异尽可能小,但是能够重点突出它,吸引用户的注意而又不会让用户产生惊怵?</p><p>现在的首页,把空间拎出来,放到了搜索条的下方:</p><p><img src='/assets/posts/2007_02_20/baidu_now.png' alt='百度现在的首页' /></p><p>空间两字没有套红,也没有加粗加大,只是挪动了一下位置。但是效果就出来了,搜索条产生的非平衡感(不知道这算优点还是缺点,看久了会产生不安,还好用户在这停留的时间很短)让用户迅速把注意力集中到了空间上。Bingo! 目的达到。</p><p>尽管我不知道最终决定了这个方案的设计师是否知道 Smallest effective difference,但是这确实是一个 Smallest effective difference 的经典案例。</p><p>更新:根据 Seven 的留言,我的加工图是错误的,而且说是空间跟前面内容逻辑不符而另拎出来(小疑问:那怎么更多就可以跟空间在一块呢),当并妨碍我们的 Smallest effective difference 的理论,即使百度设计师真的没有过这样的想法。为我的错误图片感到抱歉,但还是不更换了,留着对比能有说服力。</p><p>自然,如果没有参与,永远不要去猜测设计师背后的动机或者支持理论是什么,就像机遇只可遇不可求一样。很多事情往往是无心插柳而成,百度如此设计无意中支持 Smallest effective difference 了,从而也加大了空间的举重。</p>%00%1Ec%E1z%CE%F0i!%CCaR%9D%B5i%98Smallest effective difference2007-02-20T10:00:00+08:00https://realazy.comRealazy<p>忘了从哪看到的一句话,印象挺深:Frontend engineering rocks right now. 没错,随着用户体验等课题的不断发展,如何能够把需求更完美在前端中实现的重担,已经由过去兼职的后端工程师转向了专职的前端工程师。作为一名前端工程师,除了在开发上,必须比后端工程师更关注用户体验,因为用户最终使用中,第一接触 UI 界面,然后操作,进行交互。能否保证用户友好的界面和交互,除了产品相关人员的功能策划,还有前端工程师对功能在前端中的具体表现的深刻理解,对技术壁垒的一一攻破。前端开发到了 rocks 的时代了。</p><p>但是或许我们会在用户友好中迷失自己。一件产品的诞生,凝聚的是整个团队的努力,所以,前端开发必须注意到自己代码应该是清晰紧凑的,这样,无论是市场的广告投放,后端的数据输送等开发,都可以最佳的方式来配合前端,即需做到开发者友好;我们的代码,不管是由浏览器来处理,还是搜索引擎的检索,都必须尽量做到符合语义,使用当代的开发思想(如 web 标准)来要求前端的代码,即做到机器友好。</p><p>前端开发的任务就是做到:<strong>用户友好,开发者友好和机器友好</strong>。</p><p>用户友好毋须多言,这是我们的首要任务。如果可能,我建议所有的前端工程师都能够把自己定位为用户体验设计师的一员,而且是不可或缺的重要一员。只要是用户体验设计师,无论是谁,都想最佳的体验呈现在用户面前,这个愿望是好的,但是,在这个不是那么美好的时代,我们会受到许多客观条件的制约,如浏览器的支持不足,或者某个重要特性的缺乏,让我们本来预计的美好愿望编程肥皂泡。如果前端工程师在产品设计初期未参与进去,原有的完美设计,在进行到开发的阶段才发现某个不可实现的功能变得四不像。我们可以利用我们的专业技能明确判断哪些可以实现哪些不能实现,那么就能避免很多问题。</p><p>开发者友好是前端开发的第二个任务。在讲究流水作业的今天,保证开发者友好的代码、接口等是提高产品开发速度和质量的一个有效途径。开发者友好,意味着降低沟通的成本,意味着合作的顺利进行。不要认为世界上的人都像您这么厉害,即使是最简单的 HTML,写上最详尽的注释,这样其他开发者在接手的时候就不必满天查找开始与闭合的 <code>div</code>。就算您是多面手,您也必须注意您的前端开发必须开发者友好,否则可能过个个把月您连自己的代码都不认识。</p><p>至于机器友好,其实也是保证用户友好的一个前提。如何能保障我们中的特殊群体——残障人士的友好性,机器友好是最重要的一条途径。他们使用的设备,受限于他们的生理条件,同时就受限于处理信息的灵活度。如果不能保证机器友好,那么这些设备就不能保证能正确处理我们提供的信息。即使是普通用户,机器友好的代码也能降低他客户端的资源消耗,提升使用的速度,从而提升用户友好度。我们的用户中也有机器,比如搜索引擎,它们往往是用户找到我们提供的服务的关键。机器友好的一大意义还在于,搜索引擎能更好得处理信息。</p><p>用比较商业的术语说,前端开发的客户是:用户,开发者以及机器。我们的价值观的第一条:<strong>客户第一</strong>。既然如此,我们前端开发的任务,非用户友好、开发者友好、机器友好莫属。</p>%97)%60%F8%7C%B1%19%DFF%D8J%D0/%EDlK前端开发:用户友好、开发者友好、机器友好2007-02-03T10:00:00+08:00https://realazy.comRealazy<h2 id="2007">2007 的计划</h2><p>虽然有点迟,但还是来了 :)</p><ol><li>省钱,攒钱,2008 年到来前能达 3w 给家里</li><li>精通 JavaScript</li><li>适应公司需要,优先学习 PHP + MySQL, 要求到达熟悉</li><li>掌握 Ruby 语言,熟悉 Ruby on Rails 框架</li><li>初步学习一些设计理论</li></ol><h2 id="Wishlist">Wishlist</h2><p>没啥,希望<strong>今年能有一台 MacBook</strong>.</p><p>我其实在 2006 年的某段时间预计在春节时能够买到一台 MacBook, 当时把希望寄托于年终的双薪上。后不爽跳槽,几近一月无收入,年终奖更是水中花。手机被扒购置新机又花一笔(莫非是本命年我没穿过红色内裤的缘故 orz)因此这个计划在 2006 是不能实现的了。因为上面计划第一条的缘故,3w 已经是我的极限,而且必须全数归家。我不能再攒多 1w 来在 2007 实现 MacBook 的愿望了。那么为什么我 2007 的 wishlist 还是 MacBook?</p><p>受 <a href='http://www.dbanotes.net/geek/wish_list_version_2007.html'>DBA Notes</a> 的启发,我也打算依靠出售本 blog 的广告位来实现。当然,比起 DBA notes 来,我的 blog 微不足道,所以人家要 MacBook Pro, 我要 MacBook 就好了 :)</p><p>如果您觉得本 blog 上唯一的广告位连续两年不断的投放还值得一台 MacBook 的话,热切盼望您联系我:xianan.chen AT gmail.com 商讨具体事宜。</p><p>注:广告形式由您定,当然得合法健康,跟本 blog 的内容相关的最好啦,连续两年是指除非不可避免的因素如地震或者我身亡等否则不会中断。具体详谈。</p><p>P.S. 虽然我比较低调,但能在一个还算小有名气的国外网站上投稿成功,国际化的第一小步迈出成功了,倍高兴。所以在这里 P.S. 炫耀一下 :D 详情请看:<a href='http://bitesizestandards.com/bites/coloring-text-decoration'>Coloring text-decoration</a>. 这个 tips 其实是我老大 kejun 传授的,在此感谢。</p>%C0%8Dv%C2E%258%B7%EF%BC%FEs%C6%B4p%CD杂七杂八2007-01-28T10:00:00+08:00https://realazy.comRealazy<p>半年前,当我对 JavaScript 还停留在只认识这几个字母的时候(again, XD),我在寻找学习 JavaScript 的学习途经,<a href='http://jquery.com/'>jQuery</a> 无意闯入我的眼帘,我惊叹于它的小巧和易理解性,在我还不懂 JavaScript 基本语法的时候,我已经能用它做很多对我来说是不可思议的事情。由此也认识了它的作者 <a href='http://ejohn.org/'>John Resig</a>, 并关注其第一本书 <a href='http://jspro.org/'>Pro JavaScript Techniques</a>(projs).</p><p>其实把这篇文章归类于书评让我心戚戚焉,因为 projs 里面的某些高级主题作为入门者的我还不能理解,实属「无法评论」,若夸夸其谈恐会贻笑大方,虽如此,元旦放假期间还是硬着头皮翻完,虽不能一一理顺,但收获也不少。</p><p>比起 <a href='http://www.quirksmode.org/book/'>ppk on JavaScript</a>(ppk)来,我的第一感受是,projs 太「高级」了。看完 ppk 后,你可能还不能理解现在流行的那些框架是如何工作的,因为 ppk 压根就不讲 JavaScript 的特性比如对象的构建,继承等,最重要的是,很多框架的根基——闭包。虽然我还不能完全理解这些特性,但有一种豁然开朗的感觉,并开始了解,创建可复用的,复杂的 JavaScript 应用的基础,不能不了解这些特性。遗憾的是,这些特性的介绍一直都只在网上零散流行(比如牛人的 <a href='http://www.crockford.com/'>Douglas Crockford</a>, <a href='http://dean.edwards.name/'>Dean Edwards</a> 之类的个人网站),甚至是权威的<a href='http://oreilly.com.cn/book.php?bn=7-111-11091-9'>「犀牛书」</a>也就是一笔带过,并没有一个系统的概述。如今,<em>Pro JavaScript Techniques</em> 就是一本应势而生的书,系统概述这些特性,看过后给您的感觉,我再说一次,<strong>豁然开朗</strong>。最重要的是,它能改变你的看法,如果你还认为 JavaScript 只是一门小打小闹的玩具语言。JavaScript 实在还有很多「隐藏」的特性需要去挖掘,并使用它们来构建工业级的应用。</p><p>projs 不厚,但含金量实在不低。它的核心就三部分:专业 JavaScript 开发、Unobtrusive JavaScript 和 Ajax。其实我个人看法,精华应该是第一部分。有人说 JavaScript 是基于对象但不是面向对象的语言,因为它没有传统面向对象语言的一些概念比如类。但不是说就不能不能在 JavaScript 里面向对象编程,因为 JavaScript 提供的高级特性更灵活,因为灵活所以也可能更容易出错和不易掌握。projs 系统总结 JavaScript 如何面向对象编程,虽然大部分例子或者方法都来源于网上的牛人,但能够以系统的成文书籍来向读者介绍,我想这应该是世界上第一本。或许是第二、三部分个人有所了解并在一定程度生熟悉的缘故,个人觉得应该是 John 的经验分享了,概述十分入理,他的例子,尤其是在 JavaScript and CSS 的章节里(其实也是我比较能看懂的一章,呵呵),写得十分优雅和巧妙,其功力可见一斑。</p><p>因此 projs 的第一部分实属 JavaScript 进阶必看,而第二第三部分可以偷学很多有用的技巧,总的来说,是一本可以打四星半的好书。在 JavaScript 进入工业时代,您需要这 <strong>Pro JavaScript Techniques</strong> 来引导您开发更「工业级」的 JavaScript。</p><p>更新:此书中文版<a href='http://realazy.com/jspro'>《精通 JavaScript》</a>已经上市。</p>%A06%25%18%ED%C3%3C%A5%FB%F8z%EF%AD1%DC%F3JavaScript 工业时代的来临2007-01-03T10:00:00+08:00https://realazy.comRealazy<h2 id="">无障碍规则</h2><p>尽管无法预见可能损害一个有脚本网站的可用性的所有情形,但我已经总结出一些可以帮助您在基础上不犯错的规则。不要把它们当作 JavaScript 和无障碍的终极规则,这只是能防止一些常见低级错误的规则集,别怀疑它们可能会增增删改一旦某些规则在某些场合下不凑效。</p><p>在检验这些规则时别忘了加上您的思考。</p><h3 id="HTML">逻辑上的 HTML</h3><p>在一个有脚本的环境中保持无障碍的最明显的方式莫过于确保平白的 HTML 页面包含所有必须的骨架,来保证成功的浏览。</p><p>内容、导航和重要的表单应该硬编码(hard-coded, 表示非脚本生成的代码)进您的 HTML 中,用户将能访问和使用它们。</p><p>合用表单(书中例子)就是一个好例子。当浏览器不支持 JavaScript,表单依然可访问因为所有的表单字段和标签都硬编码进 HTML 中去了。尽管更少可用性,但如我们所见,这是不可避免的。</p><h3 id="href">硬编码连接和 <code>href</code></h3><p>您的 HTML 中所有硬编码连接都应该有一个 <code>href</code> 属性,并指向一个有用的页面或者其他文件。所以,这是错误的:</p><pre class='html'><code class='html'><a href="#" onclick="showPopup('niceimage.jpg')">Nice image!</a></code></pre><p>当一个无脚本用户点击连接,什么也没发生,因此这页面是有障碍的。此外,我们前面也讨论过,不应该再使用内联事件句柄。</p><p>相反,unobtrusive JavaScript 编程者会这样做:</p><pre class='html'><code class='html'><a href="niceimage.jpg" id="nice">Nice image!</a></code></pre><pre class='js'><code class='js'>document.getElementById("nice").onclick = function () { showPopup(this.href);};</code></pre><p>现在,无脚本用户能够访问到硬编码的 <code>href</code> 属性了,同时脚本用户打开一个新窗口。网站能够保持无障碍,行为也从结构中分离出来了。</p><h2 id="">生成内容意味着只有脚本用户才能访问</h2><p>某些场合下,由 JavaScript 生成内容(generating content)让一个站更有障碍。</p><h3 id="">触发先进脚本的连接</h3><p>假设您有一个连接,用于触发时髦的 Ajax 脚本来获取内容并大大增强可用性,但没有一个 HTML 页面可供连接。我们刚刚看到的,这是错误的:</p><pre class='html'><code class='html'><a href="#" onclick="startUpAjaxStuff()">Commence coolness!</a></code></pre><p>但我们不能应用上一条规则了。我们该把哪个页面的连接放进 <code>href</code> 里呢,如果我们压根就没有一个跟 Ajax 脚本等价的无脚本页面?</p><p>如果要为连接增加一个没啥道理的 <code>href</code>,那么交给 JavaScript 来生成吧:</p><pre class='js'><code class='js'>var link = document.createElement("a");link.href = "#";link.onclick = startUpAjaxStuff;var linkText = document.createTextNode("Commence coolness!");link.appendChild(linkText);document.body.appendChild(link);</code></pre><p>现在,无脚本用户根本看不到这个连接了。很好,因为它不会干任何事情如果被点击,还会制造困扰。</p><p>注意该例子脚本为 <code>link.href</code> 设置了"#",尽管我们看到了使用"#"并不是什么好主意。但我们需要它:大部分浏览器把连接定义成一个 <code>a</code> 加上一个 <code>href</code> 属性。</p><p>幸运的是,前一条规则不会在这种情况下应用,因为该连接没有硬编码进 HTML,只是由 JavaScript 生成的。我们可以确保只有有脚本的用户才能邂逅这个连接,同时也可以运行事件句柄。因此, <code>href="#"</code> 在此是被允许的。</p><h3 id="JavaScript">在 JavaScript 中隐藏内容</h3><p>隐藏内容是危险的。一般上,您隐藏内容是因为您不想把所有东西一下子展示给用户以提高可用性。您等待用户点击连接然后通过运行脚本来展示内容。</p><p>没有 JavaScript 的话,内容将永远不会展示出来,页面就变得有障碍起来。如果您创建一个必须由用户激活脚本才能展示内容的页面,您应该把「隐藏内容」的命令交给 JavaScript 而不是 CSS。</p><p>比如,合用表单初始隐藏所有的带 <code>rel</code> 属性的 <code>tr</code>。这可用 CSS 来达到,但这完全是错误的:</p><pre class='css'><code class='css'>tr[rel] { display: none;}</code></pre><p>如果一个无脚本用户访问到您的页面,他看不到这些 <code>tr</code>,而且没有任何方式让它们展示出来。因此页面也是有障碍的。</p><p>相反,合用表单使用 JavaScript 来隐藏 <code>tr</code>(事实上,从文档中完全删除了)。如果 JavaScript 没有启用,它们不会隐藏,因此保持了无障碍。</p><h2 id="">重定向</h2><p>偶尔,处理无障碍问题的最佳方式是为网站同时创建脚本和无脚本版本。尽管我不喜欢这个解决方案并试着避免,但是无论如何在实践中有它的价值。</p><p>如果您使用这种方法,您应该遵循两条规则。首先,站点入口页应该使用无脚本页面,因此,所有的浏览器,就算只支持 HTML 而已,也能获取它们能用的页面。</p><p>然后,一旦浏览器载入了页面,运行脚本来检测浏览器是否支持您的先进脚本,如果支持,重定向一个脚本页面,使用 <code>replace()</code> 方法。</p><pre class='html'><code class='html'><head> <title>Noscript page</title> <script type="text/javascript"> var isSupported = [check JavaScript support]; if (isSupported) location.replace('scriptpage.html'); </script></head></code></pre><p>永远不要在这种情形下使用 <code>location.href</code>,因为它将创建新的浏览器历史记录。如果用户载入了无脚本页面,她被重定向到有脚本页面。一旦她按「后退」键就会回退到无脚本页面,但是脚本又会触发把她带回到有脚本页面。「后退」键在种情形下成了可用性的罪魁祸首。</p><p><code>location.replace()</code> 也会载入新页,但它不会历史记录中留下痕迹。当用户按「后退」键,她被带回到载入无脚本页面之前的页面。从用关注的角度来说,「后退」键功能依然正常。</p><h2 id="">键盘用户</h2><p>我们已经了解到键盘用户没有鼠标事件(除非他们使用屏幕阅读器)。因此,您应该定义鼠标事件的可替换方案。有时这很简单,比如,使用聚焦事件匹配鼠标悬停事件;有时是很困难的,比如,拖拽脚本,您必须写更多的额外功能来特殊照顾键盘用户。</p><h3 id="">可点击项目</h3><p>就算您创建了键盘可访问的脚本,如果用户不能聚焦于您定义事件句柄的元素上,也是无用的。</p><p>比如下拉菜单(书中例子)。它使用鼠标悬停来触发下拉,根据前面的规则,我为键盘用户增加聚焦事件。但是,为了能触发聚焦事件,键盘用户必须能够聚焦下拉菜单。如果这不可能,那么脚本依然有障碍。</p><p>所有浏览器中可信赖的能够获取焦点的元素是连接,表单字段和按钮。因此,任何键盘友好的事件或者脚本都应该在这些 HTML 元素上设置。</p><p>下拉菜单也这么做,聚焦事件都被赋予连接上。因为键盘用户可以聚焦连接,脚本依然对他们无障碍。</p><h2 id="noscript"><code>noscript</code> 标签</h2><p>浏览器厂商知道 web 开发者可能需要为无脚本的用户提供特别的内容,所以发明了 <code>noscript</code> 标签。</p><p>它如此工作:</p><ul><li>不支持任何 JavaScript 的浏览器不支持这个标签。因为非支持的标签会被 HTML 解析器忽略,这些浏览器显示这个标签的内容:无脚本内容。</li><li>支持 JavaScript 的浏览器检查浏览是否启用了 JavaScript。如果是,它们隐藏 <code>noscript</code> 标签和其内容;如果不,浏览器显示该标签内容。</li></ul><p>不幸的是,在当代无障碍开发中 <code>noscript</code> 标签并不会扮演很重要的角色。大量的浏览器支持过时版本的没有 W3C DOM, XMLHttpRequest 或者其他的当代特点的 JavaScript。这些浏览器不会显示 <code>noscript</code> 内容因为他们支持部分的 JavaScript。因此其用户就像无脚本用户,不会看到脚本化的界面,但同时也看不到额外的无脚本内容。</p><p>所以,能不用 <code>noscript</code> 就不用。</p><p>更新:ppk on js 的读书笔记暂告一段落,有兴趣的读者请读原版或者等待中文版的上市。另,推荐 <a href='http://ihower.idv.tw/blog/'>{|ihower.idv.tw| blog }</a> 上的 <a href='http://ihower.idv.tw/blog/archives/category/javascript/'>javascript 资源</a>,他那里也有 ppk 相关的读书笔记。</p>%AC%D4*%13r%B8%F7%D0%C4%7B%00%D9%B6%DEppk on JavaScript 第二章:背景(完结篇)2007-01-01T10:00:00+08:00https://realazy.comRealazy<p>半年前,当我对 JavaScript 还停留在只认识这几个字母的时候,有一天我突然心血来潮,在网上下了 <a href='http://domscripting.com/book/'>DOM Scripting</a> 的 <a href='http://www.friendsofed.com/samples/1590595335.pdf'>样章</a>,照着里面的例子写了我平生第一个能让我知所以然 JavaScript,在浏览器运行成功,兴奋不已,从此能把学习编程的热情持续半年以上,破了过去只能热一两个星期的记录,它带给我的影响不只是 JavaScript 本身,我同时已经初步入门了 <a href='http://rubyonrails.org/'>Ruby on Rails</a>。</p><p>为什么之前我之前也拷贝粘贴过 JavaScript, 也曾试图学习过,但都无疾而终?除了这种 JavaScript 的经典学习方式不适合作为非程序员的我外,更重要的,网上或者市面上根本没有合适的教程,您所看到的大部分教程,除了让您一头雾水外,还可能把您引入岐途。随着 web 标准的发展,JavaScript 的开发方式已经发生了质的变化,急需一本结合 web 标准理念来教学 JavaScript 的入门教程来革新旧开发者的观念,引导初学者一开始就走在正确的路上,这方面,我想没有谁能够比领导 <a href='http://www.webstandards.org/'>WaSP</a> DOM Scripting Task Force 的 <a href='http://adactio.com/'>Jeremy Keith</a> 更权威。</p><p>最近,由人民邮电引进出版的中文版 <a href='http://www.douban.com/subject/1921890/'>《JavaScript DOM 编程艺术》</a> 终于面市,我第一时间购买了并阅读完毕,因此将我读后感发表出来与大家分享,如有什么高见,请留言不吝赐教,谢谢。</p><p>首先要说的本书的书名,原书名是: Dom Scripting: Web Design with JavaScript and the Document Object Method, 显而易见,本书是面向初学者的,而且对象比较明确,web 设计师, 并且表明了本书的内容主要就是 DOM, 因此想从本书里看到 JavaScript 奇技淫巧,想看到 Ajax 的高级应用什么的,肯定会大失所望。中文版的译名在我本人看来没能表达出原书名要表达的意思,当然基于市场考虑,加上「艺术」之流的字眼还是可以原谅的,最重要的,还是书本的内容质量。翻译来说,虽然有个术语比较别扭(如 hook 翻译成「挂钩」),但有些也相当精确(如 graceful degradation 翻译为「预留退路」)。比起 CSS Mastery 的翻译来,相当不错了。</p><p>为何面对设计师?(当然不是说不是设计师就不能看)当 web 标准越来越普及,使用 XHTML 和 CSS 来构建兼容标准的网页的设计师越来越多,接触了 web 标准的观念和大量使用 CSS 之后,对于 DOM 其实已经有了非常感性的认识,只需高人来点破即可迅速掌握 DOM 的基本操作,而在网页里,对 DOM 的操作,绝大部分是 JavaScript. 所以,我觉得,如果您现在已经了解 web 标准并积累有一定的项目经验,那么,这本书对您来说,阅读应该很轻松,即使,您没有任何的编程经验。所以,书中对于怎么入门编程,其实就只是简介一下 JavaScript 的语法,而也不会使用复杂的语句来构建例子程序,绝大部分只停留在 <code>if</code>, <code>for</code> 等简单的逻辑上。</p><p>至于书的内容,我觉得您直接去看网上提供的目录就可以一目了然。在我看来,它只不过就是获取 DOM 节点及其类型和值,如何改变 DOM 节点的类型和值,如何插入和删除 DOM 节点,如果您 CSS 基础好,那么您简直就是在看一本 CSS 书籍中的选择器介绍,只不过 DOM 更强大和灵活,并活起来罢了。并简要介绍了 JavaScript 的动画原理,让您明白,动的背后其实很简单,还提供了一个制作整站实例的过程,对于一些非设计师来说可以一窥网站诞生的流程。最后一章展望,其实就是入门书都提供的,就是对 JavaScript 的一些高级应用的概述,来吸引您继续学习的兴趣。不过连展望都不忘提醒贯穿整书的理念:</p><p>JavaScript 是用来<strong>充实</strong>网页而不是<strong>构建</strong>网页的,并基于 web 标准的结构,表现,行为分离原则。任何时候不要忘了无障碍,网页的核心内容在 JavaScript 缺席的时候不能受影响。</p><p>确实,这是本不折不扣的入门书,对于初学者更合适。但是,是不是说对于老鸟就不适合了呢?非也,前面已经提到,尽管本书的技术浅显,但始终贯穿书本的开发理念与原则,或许是作为老鸟的您从来没有听过或者一知半解的,我建议您可以把它当作一本小说来看,不必像初学者一样拘泥于技术细节。为何这么说?</p><p>Ajax 引爆 JavaScript 的流行,流行展望当今 JavaScript,大量框架的涌现,您已经可以不费吹灰之力迅速搭建一个 JavaScript 应用,这样让大家的应用看起来都一样,最终结果看起来似乎都一样:解决了问题。但是您有否想过,这相同结果可能在只有 JavaScript 的时候?您是否想过能在禁止 JavaScript 的情况下,人家的能用您的不能用?为何?书本不会直接给您答案,但我相信您能间接找到。没错,该刷新您的开发观念了。而且书不厚,您只需花一个下午。</p><p>通往终点的过程与终点本身同样重要。</p><p>如果非要找点瑕疵,那么就是,本书作者作为 WaSP 的成员,在里面的例子中竟然同时使用两次 <code>h1</code>,有违 W3C <code>h1</code> 每页只出现一次的建议。另外,翻译中把 “you” 翻译成「你们」,让人感觉作者高高在上的样子,还不如翻译成「您」或「你」来得亲切。</p>k%3C%FF%95%B6)%91%D06%CD%1DV%9Ej%5C%97通往终点的过程与终点本身同样重要2006-12-30T10:00:00+08:00https://realazy.comRealazy<p>这一章讲的太多是开发观念,为其他书所不或很少提及的。所以我基本上全译而不是精简笔记。以后的技术章节我可能只记录一些需要注意的问题,否则我就是在翻译这本书而不是作读书笔记了。因为是全译,所以有点长,故这一章还未完结,请耐心等待第三部分,如果您真的在等的话。以下是正文:</p><h2 id="">分离行为和表现</h2><p>第三层分离,行为和表现,恐怕要比前两种分离更复杂。别期待我会给您清晰的答案,该分离还属于当代 JavaScript 编程领域中尚未能够明确化到容易学习和理解规则的范围内。现在我们的问题比答案还多。</p><p>最基本的问题是:哪种效果您应该定义到 CSS 中,哪种应该定义到 JavaScript 中?尽管表现应定义在 CSS 中而行为应定义在 JavaScript 中的问题很明显,但是它们存在一个交错的灰色地带,某些效果是属于表现还是行为并不清晰。</p><h3 id="hover-mouseover-out">下拉菜单: <code>hover</code> 还是 <code>mouseover/out</code>?</h3><p>下拉菜单的目的是,当用户鼠标悬停时显示次级菜单,移开时隐藏,从远古时代(精确地算的话,1997 年)起我们就用 JavaScript 获得这种效果。那时我们没有其他选择,如果您要下拉菜单,您只能用 JavaScript。</p><p>尽管如此,时代的进步让您现在可以使用 CSS 来实现,无须任何 JavaScript:</p><pre class='html'><code class='html'><li> <a href="#">News</a> <ul> <li><a href="#">Press Releases</a></li> <li><a href="#">News Articles</a></li> ... ... </ul></li></code></pre><pre class='css'><code class='css'>// in .css fileli ul { display: none;}li:hover ul { display: block;}</code></pre><p>在 <code>li</code> 内的 <code>ul</code> 次级菜单,初始隐藏(<code>display: none</code>),当鼠标悬停 <code>li</code> 上,通过 <code>li:hover</code>,次级菜单即可展示出来(<code>display:block</code>)。</p><p>哪种方案更可取?大部分人将选择 JavaScript 因为 IE6 及更早版本并不支持 <code>li:hover</code>。</p><p>但是,这个现实的答案并未能回答到根本。我们来假设下当所有浏览器都完美支持 <code>:hover</code> 时(别怀疑,IE7 已经实现),CSS 和 JavaScript 方案都兼容浏览器了。我们选择谁?</p><p>CSS 代码比 JavaScript 简单多了,理由当然有其优越性。我们会在后面的章节看到,JavaScript 鼠标移开事件,特别令人憎恶,要玩得起它,必须有一手过硬的技术。相比之下,CSS <code>:hover</code> 选择器……真的跑起来了。</p><p>此外,CSS 可以在 JavaScript「残掉」的时候继续工作。不过,这并不意味着 CSS 必定比 JavaScript 更具无障碍。</p><p>有些人用键盘代替鼠标。他们使用按键(一般是 Tab 键)捕获 HTML 元素(通常是连接),然后按回车来激活已捕获元素。键盘用户没法使用 CSS 下拉菜单,因为 <code>li:hover</code> 仅仅是一个鼠标选择器,没有对应的键盘。此外,没法捕获 <code>li</code> 因为键盘仅能捕获连接,按钮和表单域。</p><p>反之,JavaScript 能通融键盘用户。因此 CSS 下拉菜单未必本来就比 JavaScript 版更无障碍。键盘用户更喜欢 JavaScript 版本下拉菜单。</p><p>实际上,它们都有各自的问题。下拉菜单是表现还是行为?该效果只在用户行为下才触发,这表明它是行为。另一方面,该效果是关于展现次级菜单的,这表明它是表现。</p><p>尽管个人倾向于行为答案,但问题允许有多个答案,每个 web 开发者都应招到自己的解决方案,忽略这些理论假设吧。</p><p>总之,CSS <code>:hover</code> 方案比起 JavaScript <code>mouseover/out</code> 的来明显的得分点只在于更少的代码和维护。但这是 CSS 方案比 JavaScript 方案好的充分理由吗?还是要取决于个人的选择。</p><h3 id="vs">相同效果 vs. 相似效果</h3><p>在下拉菜单中,您需要给每个被选中的 <code>li</code> 部署相同的效果。「如果 <code>li</code> 内有一个嵌套的 <code>ul</code>,当鼠标悬停 <code>li</code> 时显示它」。如我们所见,这很容易折合归纳成以下两行 CSS:</p><pre class='css'><code class='css'>li ul { display: none;}li:hover ul { display: block;}</code></pre><p>太简单了,因为所有的 li 效果都一样。</p><p>现在我们来个不一样的例子:<code>a</code> 鼠标悬停。当用户鼠标停在图片上,图片改变,鼠标移开,图片变回原来的。</p><p>之前,所有的鼠标悬停效果都用 JavaScript 来实现,这是传统。JavaScript 鼠标悬停效果是最受欢迎的宠物自从 1996 年诞生以来,并且是 WWW 中被拷贝最多的脚本。因为已经存在银河沙数的脚本例子,没人移植到 CSS 上。</p><p>即使如此,理论上,创建 CSS 鼠标悬停效果是可以的:</p><pre class='html'><code class='html'><a href="somewhere.html" id="somewhere">Somewhere</a><a href="somewhere_else.html" id="somewhere_else">Somewhere else</a></code></pre><pre class='css'><code class='css'>a#somewhere { background-image: url(pix/somewhere.gif);}a#somewhere_else { background-image: url(pix/somewhere_else.gif);}a:hover#somewhere,a:focus#somewhere,a:active#somewhere { background-image: url(pix/somewhere_hover.gif);}a:hover#somewhere_else,a:focus#somewhere_else,a:active#somewhere_else { background-image: url(pix/somewhere_else_hover.gif);}</code></pre><p>您会注意到每个鼠标悬停效果都需要两条 CSS 语句,一条定义普通状态,另一条定义鼠标悬停状态。原因很简单:每个连接都有起独自的普通状态和鼠标悬停状态图片,这些不同的图片需要在 CSS 中定义。</p><p>在这个案例中我们不能为所有的连接部署相同的效果,但它们效果很相似。所有的连接都会在鼠标悬停时改变图片,但每个连接都需要属于其自身的图片集合。每添加一条新连接我们都必须增加两条 CSS 语句,而且必须是手工的。无法改变的事实是,一个单纯的 CSS 鼠标悬停很快就会变成维护地狱,尤其是在几打连接上使用时。</p><p>这只不过是一条常规罢了。当您需要给一些元素部署相同的效果时 CSS 特别高效,就像上面的下拉菜单例子,然而在一些类似的效果上,CSS 显得低效,就像刚才那个鼠标悬停例子。</p><p>而 JavaScript 允许您写一个管理您不愿意计算有多少的连接的脚本:</p><pre class='html'><code class='html'><a href="somewhere.html" id="somewhere"><img src="pix/somewhere.gif" /></a><a href="somewhere_else.html" id="somewhere_else" ><img src="pix/somewhere_else.gif"/></a></code></pre><pre class='js'><code class='js'>function initMouseOvers() { var links = document.getElementsByTagName("img"); for (var i = 0; i < links.length; i++) { var moSrc = links[i].src.substring(0, links[i].src.lastIndexOf(".")); moSrc += "_hover.gif"; links[i].moSrc = moSrc; links[i].origSrc = links[i].src; links[i].onmouseover = function () { this.src = this.moSrc; }; links[i].onmouseout = function () { this.src = this.origSrc; }; }}</code></pre><p>起初,这个方案需要比 CSS 更多的代码行,但它的强大远远抵消这个坏处。如果您需要另一个悬停,您只需把连接加上,ok,它自己自动工作了。</p><p>因此,当创建这些类似但不相同的效果时,JavaScript 方案更高效。当您面临选择 CSS 还是 JavaScript 时请记得这点。</p><p>现在还不可能得出一个关于表现和行为分离绝对的结论,有待更多的调研,现在我更希望您花些时间考虑一下这个问题,无论您是否遭遇类似问题,一旦您发现有更好的规则,分享出来吧!说不定您就是第三种分离的规则领导者。</p><h2 id="">无障碍概览</h2><p>我已经不止一次提及无障碍了,而且是通常伴随着讽评普遍缺乏它的 JavaScript 开发中。是时候来个概览了,同时给我们的例子来个无障碍测试。</p><h3 id="">什么是无障碍</h3><p>无障碍是指在大部分情况下,尤其在用户不可改变的某些条件,比如视力衰减,或者使用一个不支持(足够)JavaScript 的浏览器,您的网页依然可以使用。</p><p>在 JavaScript 情景中「依然可以使用」是什么意思?它表示,用户必须可读站点内容,使用导航,并且能进行一般的操作比如提交表单。少不了,也不多。</p><p>在任何情况下都完美无障碍是一宗大案,我们可以看到,在著书的这段时期,屏幕阅读器某些繁杂的技术问题更是让完美是难以企及。但是,我们应该从现在就开始,因为我们能够,所以我们就应该解决某些无障碍问题。</p><h3 id="">无脚本</h3><p>最明显不过的无障碍问题,任何人都可指控的,就是某些浏览器不支持(足够)JavaScript. 您小心翼翼打造的脚本这些浏览器并不买帐,用户看到的是未经脚本处理过的页面。</p><p>编写无障碍脚本的天字第一号准则是,理所当然,是确保没有 JavaScript 页面功能依然。稍后我们会继续讨论这些骇人听闻的细节,但现在我更想先讨论两条不太为人所知但很严重的无障碍问题。</p><h3 id="">无鼠标</h3><p>某些用户不用鼠标,使用击键来操纵页面。有的用键盘,有的用小物件比如屏幕触摸键盘,或者是其他模拟键盘的设备。</p><p>使用击键代替鼠标的原因可能有多种。我自己偶尔情况下会使用键盘来实现某些快速操作,显然这是我自己的选择。我可以用鼠标,但有时我很懒。</p><p>但,其他用户,可能只能一直使用键盘,有些条件是他们改变不了的。最可能的场景是,这些用户的手(部分)残障或者不能精确地移动鼠标。击键给他们提供了另外更好的选择,除非 JavaScript 开发者忽视了他们。</p><p>通常这些用户的浏览器能够执行先进的脚本,并且他们能够看到屏幕上的反馈结果。所以并没有什么大问题,除非脚本对键盘输入无反应。</p><p>为了让您的脚本兼容键盘,除了鼠标事件之外,您应该增加定义额外的事件。比如,如果您使用鼠标悬停事件就应该使用聚焦(focus)事件,因为没有鼠标就不会有鼠标悬停事件的发生。</p><h2 id="">屏幕阅读器</h2><p>有些人不能使用普通的浏览器。比较典型的是盲人或者视力严重损害得阅读不了电脑屏幕的任何东西。反而,他们需要一个能够大声朗读页面内容的程序。这些程序就是屏幕阅读器。</p><p>JavaScript 开发者通常认为屏幕阅读器根本就是不支持脚本的浏览器,因此屏幕阅读器的无障碍只不过是无脚本用户的扩展而已。无 JavaScript 的页面只要工作,盲人用户就不会遭遇有关 JavaScript 的问题(尽管大量的其他问题像缺少 <code>alt</code> 依然存在)。</p><p>不幸的是,这是个神话。大部分屏幕阅读器都运行在已有的有时是 IE 有时是 Mozilla 的浏览器上。因为它们使用普通的浏览器来获取数据,它们也支持 JavaScript。当基础的浏览器碰到脚本,它一般会试着去执行。</p><p>似乎是个好消息。如果屏幕阅读器也支持 JavaScript,那就没有问题了,对不?很不幸,有两大严重的问题:屏幕阅读起的线性性质,及其混乱的事件支持。</p><p>屏幕阅读器只支持线性处理页面。当一个视力明朗的用户使用一个图形浏览器访问网站,她简单一瞥就能获得网站的总览。左边那些古怪颜色可能就是主导航,中间的文本就是主要内容,等等。因此用户能够迅速地决定哪些是她需要的,并于这些交互。此外,一旦脚本改变了文档结构或者表现,她的眼睛能被吸引过去并能揣测其含义。</p><p>但屏幕阅读器用户不会如此。屏幕阅读器从头到脚把页面读个遍,一般是按照源代码的顺序。这是一个严重的问题,对当代 JavaScript 来说。就算所有屏幕阅读器完美支持 JavaScript,我们怎样才能提示其用户页面部分已改变,尤其是屏幕阅读器已经读过这些部分?</p><p>拿表单验证(本书有几个例子贯穿始终,这是其中一个)的例子来说,假设它运行在一个完美支持 JavaScript 的屏幕阅读器中,允许用户填写和提交表单。因为 JavaScript 工作完美,一旦用户激活提交按钮,表单验证程序就会运行。如果有错,脚本就会提示「出错」,并停止提交。</p><p>问题在于,屏幕阅读器可能(但不总是)会听到提示并得到问题的某些暗示,但是阅读器已经读过表单域,他不会听得到错误。</p><p>为了帮助屏幕阅读器用户,表演验证回滚到表单的开头:</p><pre class='html'><code class='html'><form id="startOfForm"></form></code></pre><pre class='js'><code class='js'>if (!validForm) { alert("Errors have been found"); location.hash = "#startOfForm";}</code></pre><p>这对视力未损或者已损的用户都有好处,他们都会觉察到返回表单的开头,所以能够以其方式修正错误。但是,需要注意的是,这对视力良好的用户来说是一个额外的特色,但对视力有损的用户来说是一个绝对的必须!</p><h3 id="">屏幕阅读器和事件</h3><p>不幸的是,屏幕阅读器的事件支持非常混乱与无序。理论上您希望它们能支持界面事件如聚焦和模糊,但不支持鼠标悬停与离开,因为屏幕阅读器用户使用键盘(或者等价的设备)来输入。</p><p>更不幸的是,有些屏幕阅读器厂商不管怎么还是把鼠标事件引入。理由是,大部分网站只使用鼠标事件因为制作者永远不考虑键盘的可访问性。厂商们需要他们的程序能够正确处理这些页面,因此增加鼠标事件。当然这会产生严重的问题,当一个关注无障碍的 web 开发者需要小心区分屏幕阅读器和图形浏览器来给予不同的处理。</p><p>我不想继续讨论细节了,它们让您迷惑(我也是),屏幕阅读器的事件支持在新版本发布时可能会改变。假如您不能预测屏幕阅读器的事件支持,那就罢了吧。</p><p>现实情形太差了,我担心目前屏幕阅读器的无障碍并不会有更新换代的革命性发展。<em>Derek Featherstone</em>无情但正确的结论是:「我们可以处理 JavaScript 的开或关,但不能处理中间状态」,因此他感到旧屏幕阅读器让用户选择完全禁止 JavaScript 的方式更好。如果这样,他们会回到一个没有脚本的页面,这个问题我们可以处理的。</p><p>此时,根据我们对屏幕阅读器的 JavaScript 支持的认知程度,我只能同意 Derek Featherstone,尽管是勉强的。无脚本屏幕阅读器比启动了脚本的屏幕阅读器更好照顾。</p><h2 id="">无障碍和可用性</h2><p>回到起点:任何网页都无障碍在浏览器不支持(足够的)JavaScript 的情况下。我们来讨论一些通用的规则。</p><p>我们应该三思 <a href='/posts/2006-11-19-ppk-on-javascript-study-note-part01.html'>JavaScript 的目的</a>,就是增加网站额外的一层可用性。一旦 JavaScript 失效,网站的可用性必经会受损害,整层都消失了。但该层的缺席不应该损害页面最基本的无障碍。</p><p>拿合用表单(另一个例子)来说。当脚本运行时,毫无疑问用户会看到他所需要的特定表单字段。当脚本不能执行时,用户会看到他可能需要的所有表单字段。从可用性角度来看,这确实是令人讨厌的:用户看到的表单字段阅读,他可能越迷惑和恼怒,并且很有可能决定不去理会这些表单了。</p><p>但是,页面保持了完美的无障碍,我已经尽了我作为 web 开发者的责任。用户依然<strong>可以</strong>填写表单并提交到服务器,即使没有我便利的脚本来处理过的过程会变得耗时和困惑。用户可能不愿填表因为太长和困惑,但这是我们力不能及的地方了。当 JavaScript 不被支持,可用性受损。</p><h3 id="">别限制了可用性</h3><p>无障碍不应该限制可用性。如果您有一个 JavaScript 必不可少的精彩绝妙的主意来增加网站的可用性,请使用它,但<strong>尽可能</strong>(但不是必须)确保页面能用。</p><p>完美的无障碍<strong>并不是</strong>由为有脚本和无脚本用户提供相同的功能组成的。有时就是直截了当的不可能。</p><p>比如,让我们来尝试不用 JavaScript 来创建合用表单(还是前面提到的例子)。您可以先提供一个没有可选项的表单,让用户提交到服务器,根据用户的选择让服务器返回其他的选项。技术上,这是可行的。</p><p>但是,从用户体验的观点来看,它比我们刚才研究的无脚本表单更糟糕。尽管那可能迷惑并且没有比脚本化的版本有用,但至少不需要额外的下载和字段,而用户也可能误认为自己已经成功提交了表单。</p><p>总而言之,最好接受可用性缩减了的无脚本页面,总比费心思补救好。</p>%C3p%A3%A6%A5%D8%AA0%81%B1%F6I@%0D%E6%D0ppk on JavaScript 第二章:背景(二)2006-12-27T10:00:00+08:00https://realazy.comRealazy<p>请务必了解,即使穿上防弹衣,仍然有被击倒的可能,世上没有十全十美,无懈可击也有个度。但在恶劣的环境下,防弹衣可以保护甚至拯救您的生命。</p><p>由<a href='http://www.sinzy.net/Blog/Blog.asp?ID=1'>常可</a>翻译的<em><a href='http://simplebits.com/publications/bulletproof/'>Bulletproof Web Design</a></em>: <a href='http://www.douban.com/subject/1937913/'>《无懈可击的 Web 设计》</a> 由清华大学出版社出版了,从印刷上说,相比其他国内出版书目,尚可算赏心悦目,全彩的印刷让阅读更轻松。</p><p>首先,作者 <a href='http://simplebits.com/'>Dan Cederholm</a> 在业界鼎鼎大名,他也并不像 <a href='http://meyerweb.com/eric/'>Eric Meryer</a> 是一位纯理论家,他投入实践,有着丰富的应用经验,他要着手解决的问题确实存在于现实的非理想世界中。这是我推荐这本书的第一个理由。</p><p>其次,译者常可属于国内比较早应用 Web 标准的先锋,同样拥有丰富的实战经验。值得一提的是,他是一位程序员而不是设计师,所以视角可能会更宽泛。这是我推荐这本书的第二个理由。</p><p>请使用 Firefox 打开一个网站,使用标准构建的更好(如果您着实找不到,我建议打开 <a href='http://news.163.com'>http://news.163.com</a>), 使用字体缩放功能(按住 CTRL,然后向下滚轮)放大字体,您会看到什么现象?举个简单例子,您会看到导航字体放大后超出框的边界或者被隐藏,框并未自适应放大后的字体,导致混乱的布局……您回头看看您的项目,是不是也有类似现象?如果您能翻开这本书,或许您能够在里面找到更多的例子让您似曾相识,是啊,这些「有懈可击」的例子或许就出自您手。</p><p>如果连业界楷模的网站都如此弱不禁风,我不相信您没有什么理由不看这本书,无论您是新手想学习到一种新技术,或者老鸟查漏补缺,最重要的是,不是把设计实现出来就万事大吉,而是帮您的设计穿上防弹衣,能够适应浏览器这个恶劣环境,让您的设计无懈可击。</p><p>本书开篇强调灵活,适应和亲和力,这三个元素是如此重要,贯穿本书的始终。从解决一个变态浏览器(您知道我指的是 IE)的字体适应性开始,每一章都从现实世界中找到一个「有懈可击」的案例,然后一步一步教您如何无懈可击,代码详尽,您可以亲手实践,在学习 CSS 技术的同时,最重要的,是让您知道,「有懈可击」错在哪里,如何才能无懈可击。很多案例都有触类旁通举一反三之功。</p><p>总之,这本书不会教您怎么使用 XHTML,不会教您 CSS 的基础,它建立在您有一定的 CSS 经验基础上,它不会教您如何开始,但是它会让您的视野更宽阔,教您如何提升设计的水平,全书只有一个目标:无懈可击。而且这个目标并不是不可能实现的。</p>m%20%92%D66%1B%DCI%C6G%C3%BD%B6J%C7%A6无懈可击2006-12-24T10:00:00+08:00https://realazy.comRealazy<p>JavaScript 为网页而存在,它会被嵌入到一个同时使用 HTML 和 CSS 的环境中,而此环境中不可缺少可用性和无障碍。总而言之,脚本必须给站点增加用处,而站点在 JavaScript 失灵或者根本不存在的情况下依然能继续工作。兼容标准的 CSS 革命改变了 Web 开发,JavaScript 编程也受到这场运动的巨大影响。</p><h2 id="CSS">CSS 革命</h2><p>1998 年,在 Netscape 和 IE4 无法达成任何协议时,一些先天下之忧而忧的 Web 开发者组成了 <a href='http://webstandards.org/'>Web Standards Project</a>(Web 标准工程,简称 WaSP),为解决 JavaScript 某些荒唐的私有元素,并推动使用 CSS 来定义网站的外观。她们的重要使命是「追随标准」,不仅针对浏览器厂商,而且号召 Web 开发者。</p><p>最初,WaSP 及其支持者专注于 CSS. 究其原因,CSS 是一门比较新的技术,尚未被乱七八糟的东西污染,更容易成为历史的一个转折点,JavaScript 就没有这么幸运了,那时候的 JavaScript,无论是编程,还是人们对它的想法,都是完全非无障碍的。这也是导致某些标准支持者产生"JavaScript 就是障碍"观念的原因,无论是过去还是现在。其实,JavaScript 和无障碍可以和谐共存,只要您稍微谨慎。</p><h3 id="Unobtrusive">Unobtrusive 脚本编程</h3><p>2002 年,Stuart Langridge 创造出 Unobtrusive 脚本编程(unobtrusive scripting, Stuart Langridge 的<a href='http://www.kryogenix.org/code/browser/aqlists/'>原文</a>),这是首次重要的尝试——在基于 CSS,标准兼容的新理论中嵌入 JavaScript.</p><p>Unobtrusive 脚本应该具备一下所有的特征:</p><ul><li>可用性,比如,赋予网站明确的可用性好处;</li><li>无障碍,比如,假使 JavaScript 失效,页面应该保持可读和可理解,尽管不可避免地丧失某些可用性;</li><li>容易实现,一个经典案例是,Web 开发者只需把脚本引入和增加一个 JavaScript 调用钩子(hook),脚本就起作用;</li><li>分离,属于本身的 <code>.js</code> 文件而不是散落在 HTML 的各个角落。</li></ul><p>理论上说,第一条自 JavaScript 诞生之日起就有的,但是经常会被程序员在炫耀 JavaScript 能力的热忱中忽略。如果没有可用性多酷都无关紧要。</p><p>其他三条都是新的。通常都认为无障碍和 JavaScript 是互斥的。容易的实现需要 JavaScript 钩子,W3C DOM 的降临使之成为可能。分离,是偷师于 CSS 革命的。如果需要分离 HTML 和 CSS, 逻辑上,也应该把 JavaScript 从它们中分离。</p><h2 id="">三个层面</h2><p>网页包含三个层次(没错,它们都需要各自分离):</p><p><img src='http://farm1.static.flickr.com/130/329082295_2d6875c146_o.png' alt='3layers' /></p><ol><li>HTML 结构</li><li>CSS 表现</li><li>JavaScript 行为</li></ol><p>HTML 结构层是网页最重要的基础。HTML 标签给予内容含义。CSS 表现层则是定义您的 HTML 该如何显示。JavaScript 行为层为页面增加交互。</p><p>不管如何,一个网页必需 HTML 结构层。没有 HTML,没有网页。CSS 和 JavaScript 都是可选项,旧的,无名的,罕见的浏览器可能不支持 CSS 和/或 JavaScript,在这种情形下,这两层或其中一层都不起作用。后果是显而易见的,任何网站应能在行为层(或者表现层,但这种情形相比较少)的缺席下还能「存活」。也就是说,网站不能完全依赖于 JavaScript,但要保证无障碍即使 JavaScript 不起作用。</p><h3 id="">分离的关系</h3><p>一般来说,最好单独管理好每一层。最基础的,确保:</p><ul><li>HTML 是结构性的,不要太复杂,没有 CSS 和 JavaScript 下保持语义。</li><li>CSS 表现层和 JavaScript 表现层分别归属于独自的 <code>.css</code> 和 <code>.js</code> 文件。</li></ul><p>分离更容易维护。您可以轻而易举地把分离的文件连接到整站的网页上,简单举个例子,您需要把字体从 12px 改成 0.8em,您只需打开 CSS 文件编辑它,这样网页变化即刻生效。除此之外,分离让您可以不需修改 HTML 结构层或者 JavaScript 行为层,只需修改整个 CSS 表现层就可让网站换上新衣。</p><h2 id="">分离表现和结构</h2><p>表现和结构分离的基本思想是确保 HTML 定义结构,只有结构,所有的表现都定义到分离的 CSS 文件中去。不再允许 <code>font</code> 标签或者表现性的表格!在一本 JavaScript 的书中似乎没有什么余地来探讨 CSS 和 HTML 的分离。那么我们就来说说这个分离对我们编写 JavaScript 代码方式的影响吧。</p><h3 id="CSS_1">CSS 更改</h3><p>JavaScript 可以让您修改 CSS,比如,您可以在 CSS 定义一个连接为红色,然后用 JavaScript 控制 CSS 再定义为绿色。有时候这是很有用的,样式的变化会使用户能注意变化的 HTML 的元素,比如出错信息。如果没有正确地分离 CSS 表现层,CSS 更改将会变得十分困难。改变元素的 <code>className</code> 通常是最佳的 CSS 更改方式。如下例子,假如表单验证程序发现用户输入错误,则改变该表单字段的 class:</p><pre class='js'><code class='js'>// obj is the form fieldobj.className += ' errorMessage';// in CSSinput.errorMessage { border: 1px solid #cc0000;}</code></pre><p>只有您正确分离了表现和结构,这样的方式才会起作用。class <code>errorMessage</code> 必须定义在 CSS 中为了实现样式的更改,反过来,也只有您一开始就从正确的 CSS 表现层开始才有可能(或者说,可行)。</p><h3 id="">修改结构还是表现</h3><p>JavaScript 实际上允许您改变网站的表现,也允许您改变 HTML 文档。用户并不关心我们改了什么。但还是有所不同的。改变一个应答用户行为的表单应该是修改结构而不是表现。相关表单元素不应该只是从视觉上隐藏而已,而应该从文档结构中移除。当一个表单提交时,浏览器为所有表单元素创建名称/值配对,并发送给服务器。如果仅仅是在 CSS 中隐藏,这些字段依然是表单的组成部分,尽管可能不是服务器所需要的。这只是理论上的东西,您可以不同意我。</p><h2 id="">分离行为和结构</h2><p>分离行为与结构很容易理解:不要把任何 JavaScript 代码写入 HTML 页面中。采取这两步:</p><ul><li>把所有的 JavaScript 函数定义在一个分离的 <code>.js</code> 文件中,让所需的 HTML 页面连接到它。</li><li>删除所有的事件处理器(注:即行内的那些诸如 <code>onmouseover</code>)并归入同一 <code>.js</code> 文件中去。</li></ul><h3 id="">分离文件中的函数</h3><p>JavaScript 代码属于 <code>.js</code> 文件,而非 HTML 文件。</p><p>所以这是错误的:</p><pre class='html'><code class='html'><script type="text/javascript">function doAllKindsOfNiftyThings(){ // JavaScript code}</script></head><body><h1>My HTML page</h1>[etc.]</code></pre><p>这才是正确的:</p><pre class='html'><code class='html'></head><body><h1>My HTML page</h1>[etc.]// 定义在分离的 nifty.js 中function doAllKindsOfNiftyThings(){ // JavaScript code}</code></pre><h3 id="">删除事件处理器</h3><p>第二步是把所有 HTML 内的 JavaScript 函数调用移到分离的 <code>.js</code> 中去。事实上,99%的 HTML 内的 JavaScript 代码是行内事件处理器。</p><p>以下,器在 HTML 内,但不应该属于 HTML 的:</p><pre class='html'><code class='html'><a href="home.html" onMouseOver="mOv('home')" onMouseOut="mOut('home')">Home</a></code></pre><p>应该定义在分离的 <code>.js</code> 文件中去:</p><pre class='html'><code class='html'><a href="home.html">Home</a></code></pre><pre class='js'><code class='js'>// in separate .js filevar nav = document.getElementById("navigation");var navLinks = nav.getElementsByTagName("a");for (var i = 0; i < navLinks.length; i++) { navLinks[i].onmouseover = [code]; navLinks[i].onmouseout = [code];}</code></pre><p>该脚本处理 <code>id="navigation"</code> 的元素并处理其内的所有连接,然后再赋予连接事件处理器。</p><h3 id="javascript"><code>javascript:</code> 伪协议</h3><p>有些情况下你会看到像以下的 <code>javascript:</code> 伪协议:</p><pre class='html'><code class='html'><a href="javascript:doAllKindsOfNiftyThings()">Do Nifty!</a></code></pre><p>这个复杂肮脏代码隐藏的含义是一个 <code>onclick</code> 时间处理器:当用户点击该连接,我们需要的是呼叫 <code>doAllKindsOfNiftyThings()</code> 函数。所以您应该从该连接中删除 <code>javascript:</code> 呼叫而用一个独立 <code>.js</code> 文件中的 <code>onclick</code> 时间处理器来取代之:</p><pre class='html'><code class='html'><a href="somepage.html" id="nifty">Do Nifty!</a></code></pre><pre class='js'><code class='js'>// in separate .js filedocument.getElementById("nifty").onclick = doAllKindsOfNiftyThings;</code></pre><p>因此,对于 <code>href</code>,应该包含一个完整的 url 以备没 script 的用户能够访问,否则整条连接都由 JavaScript 产生,不具备无障碍性。</p><p><strong>注意:以下内容非正文!</strong></p><p>p.s.有人<a href='http://woooh.com/post/from16to25.html'>点名要爆隐</a>,没办法,只好完成指标:</p><ol><li>新千年,学会上网,为了能向心爱的女孩子炫耀,学做网页,想不到现在就跟网页打交道。</li><li>新世纪,开日在江西某所大学无所事事,所念专业跟以后的工作一点关系都没有,世界真奇妙。</li><li>继续无所事事……</li><li>继续无所事事……</li><li>继续无所事事……</li><li>开始在网上写 blog, 让人知道我多多少少还是知道 web 标准的,在这行业资源匮乏的时候幸运地到了北京工作,并不断进步。</li><li>跳了两次槽,希望有个好开始。</li><li>不好意思,还有几天才到这个年龄。</li></ol>%88&%09%CF%F6t%96p%D5%1A%C13%7D%1F%BE%5Bppk on JavaScript 第二章:背景(一)2006-12-21T10:00:00+08:00https://realazy.comRealazy<p>我自己时常会被各种 Web 应用所困惑。我自己的观点也时常也会被某些现实中的应用所推倒。比如,我一向所持的观点是,在 JavaScript 横行的时代尤其需要注意网站的无障碍,就是说,没有/禁止 JavaScript 的情况下保证信息的可传达。但是,可以看到很多应用,比如 Gmail, 比如 Google Reader 等,都是两极的做法,提供一个不需要 JavaScript 的基本视图或者干脆明白地告诉你不开 JavaScript 不给你用。</p><p>那么我一直提倡的 <a href='http://onlinetools.org/articles/unobtrusivejavascript/'>Unobtrusive JavaScript</a> 还有什么意义?其实跟人交流的时候我的底气也不足,因为,确实,不 Unobtrusive 也不见得有什么坏处啊,最典型的例子就是前面所举的 Gmail 例子。跟人论战,若有人举出如此例子,我一般也只能缴械,确实,我没能找到合适的理由来反驳。</p><p>不断地翻资料,终于在非技术层面找到一些看起来应该算是比较可靠的理由,不敢独吞,遂分享。不敢保证正确,如有任何想法,务必留言交流,谢谢!</p><p>WWW(你可以叫它互联网,但也有人称之为万维网)诞生之时,只是一个超文本(hypertext)的系统,所担负的任务只有一个,依靠线性的超连接来<strong>传递信息</strong>,这是一个典型的信息系统体系。</p><p>随着各种前端,后端技术的飞速发展和成熟,加上商业的介入,人们不再满足 WWW 作为一个信息系统而已。很多人尝试将常规软件,特别是桌面应用软件的设计的规则搬到 WWW 中并针对其弱点(比如无状态的传输协议 HTTP)进行改良,使得 WWW 俨然成为一种软件性的应用,即使说, 不停留在信息传播的层面上而已,而是让人们能够依靠它完成某些<strong>任务</strong>。这就是任务型的 WWW,最典型的,还是前面所举的例子,Gmail 和 Google Reader,人们依赖它们去完成收信,抓取 Feed 等的任务,而且是建立在一种比较直观的交互方式上,没错,跟桌面软件一样直观。</p><p>这种利用 WWW 的优势(跨平台,跨时空)可以说是 WWW 的一个发展趋势。因为它能把人们从对某个特定环境(比如特定的计算机,特定的操作系统等)的依赖解放出来。RIA(富互联网应用)的概念兴起已有一段时日, 但真正引爆流行的是 Ajax 概念的出现。这个概念更容易让人把某些 WWW 应用设计成桌面程序,而不分青红皂白,哦,不,是不分自己所做的项目到底是面向任务还是面向信息的。</p><p>面向信息是 WWW 天生就具备的功能,可以说现在的 WWW 大部分应用依然如此,比如门户网站,比如电子商务,这是我为什么每次都强调要保证信息的可传达性,不管在什么情况下。但是就如前所提到,WWW 已经有了新的发展模式, 尽管未来可能有更多的面向任务型的 Web 应用,但不论如何,这两条线是平衡发展的,不会有谁取代谁的机会,顶多是<strong>综合运用</strong>。</p><p>问题就出在综合运用这里。很多人一听说 Ajax,毫无考虑,虽然心中无面向信息或者面向任务的概念,但一开始就认为这是面向任务的,因为 Ajax 的概念先入为主了。在面向信息的应用中,比如门户网站,比如政府公告,使用 Ajax 或者其他 JavaScript 来增色应用是没有问题的,但要确保信息的无障碍(貌似我强调了好多遍了)。</p><p>当然,在面向任务型的应用中,这只是把浏览器当成是一个软件的运行环境( Runtime Environment? ),就如同 Java 程序需要 JRE 一样,只不过面向任务的 WWW 应用充分利用 WWW 作为数据保存、输送的纽带而已, 因此,假如运行出错或者环境限制,我们只能告知用户,你用不了我们的程序,请检查哪里哪里,如果还不行只能抱歉了。</p><p>但是在面向信息的应用中,请问,假如用户在受限的环境下获取不了信息,这说得过去吗?面向信息就是以信息的传达为目标,不应该有所限。</p><p>所以,在项目的开始之初,我们需要必须明确的第一件事情是,我们是面向信息还是面向任务,这才能让项目的方向明确,不至于让各种貌似很高级的东西迷惑了我们的决策。</p>JB%A8%F9n;%09%E4%22%1Aq%1E%5E%94S(面向任务,还是面向信息?2006-12-12T10:00:00+08:00https://realazy.comRealazy<p>离职在家半月有余,趁难得机会把相关的 Rails 书籍看了一遍,参考 <a href='http://jobs.37signals.com/'>37Signals Jobs Board</a> 练习了一下 Rails,很高兴,一介文夫的我终于能写出一些有用的东西,实现了我多年的梦想(是啊,我一直嚷嚷要学编程,直到现在才有所进步)。</p><p>为什么我要参考 37Signals Jobs Board 做这个东西,原因不仅是练习如此简单而已。我的 blog 读者中,很多人都向我咨询人才方面的事情,无论是找工作还是猎工作。半年前,我就有开个工作板方便大伙的打算,但一直没有机会。因为我才不想用手工添加的方式来处理各种工作需求,所以一定得写个程序来处理。</p><p>约 10 天前,得知 <a href='http://dup2.org/blog/'>qyb</a> 的空间可以安装 ruby,这个消息真实让人振奋,我的热情空前高涨,一头扎进去,并最终形成了这个网站:<a href='http://jobs.dup2.net/'>Jobz Board</a>(感谢 X-Noise 绘制的 logo)。</p><p><a href='http://jobs.dup2.net/'>Jobz Board</a> 很简单,但够用了。如果您需要找人才,不如来发个招聘贴?<strong>免费的</strong>(看样子我也不敢收费,呵呵)。如果您在找工作,何不瞧瞧是否有适合您的?当然,现在还没有人发……</p><p>希望能够对您有所帮助。请访问 <a href='http://jobs.dup2.net/'>Jobz Board</a>.</p><p>需要注意的是,为了方便大家,请按照格式书写,请不要发布垃圾信息,请发布正确的而不是测试的信息,必要的时候请与我联系。所有信息都必须经过审核才能发布。</p><p>p.s. 在一天中连续发两篇 blog 真是历史罕见啊,呵呵。</p>%FAS%B5%1E5%F4%F0%81Y%8A%DF%E1%A4%F4'9jobz board,希望能够帮助到您2006-12-12T10:00:00+08:00https://realazy.comRealazy<p>我在这里说无障碍或者亲和力,即 accessibility, 似乎有点尴尬。商业应用需要吗?除非我们的受众有残障,否则没有利润就不需要。没错,<strong>商业</strong>。但是,作为公共服务,天赋人权,应该保证信息的受众,无论在何种情况下(当然不是绝对的)都能获取。我们天天吹啊吹「以人为本」,其实政府应该立类似 section 508 的法了,使我们的政府网站建设有法可依,这才是真正的人文关怀。遗憾的是,作为 accessibility 的最重要的第一步,即 web standards,我们的政府网站不仅还没开始,很多还是 ie only 的……路,漫漫兮……</p><p>虽说商业应用不能太过于关注无障碍,但是,如果不妨碍当前的应用,花点力气,不说为残障人士服务,在某些受限情况下,比如浏览器 JavaScript 关闭的情况下依然可用或者传达可用信息,那么用户顿生好感,对您的商业应用并非没好处。另外,值得一提的是,搜索引擎其实就是最大的盲人,做好无障碍,您其实就是做好 SEO 的根基工作。So,why not...,对不起,是所以,为什么不在项目开始的时候,就应该做做无障碍的工作呢?完全按照现有规范 <a href='http://www.w3.org/TR/wcag2-req/'>WCAG2</a>(<a href='http://www.junchenwu.com/WAI/wai-pageauth.html'>WCAG1 中译版</a>),商业环境,预算等都不允许,但并不是不做点无障碍工作的借口。记住,Web 2.0 并不是<strong>不能用</strong>。</p><p>比方说,现在我们的网站,在没有 CSS 的情况下,<code>ul</code> 标记的导航一般都会很长,对于我们来说,没问题,拖拖鼠标就 ok,但是使用手机的家伙们呢?使用阅读器的家伙们呢(中国有么 -_-)?如果能有一个跳到内容的连接,那么就可以帮助许多人,迅速地跳到她/他所关心的内容上。我们加个连接并不困难吧?那你为什么不加?在我的上一个项目(无须保密,请访问 <a href='http://hithere.com/'>HiThere</a>)中,即使这是个十足的商业网站,我都加上了这个貌似没意义的连接。相信我,努力就有回报,您不能排除您的用户有一天会感动 ^_^。</p><p>当我们步入这个号称<strong>2</strong>的时代,CSS 和 JavaScript 终有发飙之日。忽如一夜春风来,设计师,开发者不断分享各种各样的技巧,是的,有时候甚至是不择手段,目标只有一个,实现项目需求。然而,我们的产品经理不让我们考虑无障碍,right, boy, 你给我实现就是了。这并没有错,但是作为前端最终实现者的我们,在不影响实现的前提下依然可以有所作为。举两个例子,然后结束我们的文章,有所作为并非是为所欲为无所不为。</p><p>比如,以图代字的经典方法有两种,无外乎是使用 <a href='http://www.stopdesign.com/articles/replace_text/'>`display: none;`</a> 和 <a href='http://phark.typepad.com/phark/2003/08/accessible_imag.html'>`text-indent`</a> 两种办法,那么,您考虑过如何选择吗?它们除了技术以外还有什么不同吗?事实上,很多客户端会忽略掉 <code>display: none;</code> 的元素,当其不存在?不存在?!是的,在我们的视觉中,它是存在的,但其实是它的父元素存在。它的父元素?呃,它是个空元素,它只是一个为 CSS 而存在的钩子(hook),对视觉有意义。但对屏幕阅读器呢?将会是空白一片,因为文本被 <code>display: none;</code> 掉了。</p><p>Ajax 让 JavaScript 得以复兴,我们很多网站都做了很多漂亮的应用,可以说,极大地提高了用户体验和可用性,但是在无障碍方面,却是不升反降的。您可以亲自去试一下,关闭掉您浏览器的 JavaScript,看看还能有多少网站,我不要求能用,能提供出错信息不让用户迷茫的网站能有多少个?在此,我严重推荐 <a href='http://quirksmode.org'>ppk</a> 的一篇文章,<a href='http://24ways.org/2006/hide-and-seek-in-the-head'>Hide And Seek in The Head</a>。看看人家的 JavaScript 是怎么用的,您的又是怎么用的?但是,其实您也可以做到,只要多一点点无障碍的考虑,对不?</p><p><strong>对!</strong>所以,为什么不现在就开始?</p><p>P.S. 最近抽空练习了一下 Rails,终于能够写一些小应用了,呵呵,我觉得这是我 2006 最大的进步。欢迎看看:<a href='http://www.dup2.net/realazy/'>http://www.dup2.net/realazy/</a>,感谢 <a href='http://dup2.org/blog/'>qyb</a> 提供的空间,让我也学会了一点点的 Rails 部署。</p>%DE%E6%16&z%7CzzL%DA%92%FC%8A27%C2我们其实还可以做得更多……2006-12-09T10:00:00+08:00https://realazy.comRealazy<p>如何学习编程?一本好教材当然是不可缺少的。一本好的入门教材更是不可或缺的。为什么我说入门教材?我自己非理工科专业更莫说计算机相关专业出身,学习编程并不能像受过系统训练的人一样,拿来一本教材或者手册,熟悉一下词法(lexical)结构即可上手。皆因已经掌握<strong>编程的基本概念</strong>,语言只是工具而已。当我等菜鸟请教编程达人如何开始编程之旅,他们可能会忽略掉我等的非计算机背景,因此门非但不入,往往不得其法,误入歧途,半途而废……</p><p><a href='http://www.douban.com/subject/1923716/'><img src='http://ec3.images-amazon.com/images/P/0976694042.01._AA240_SCLZZZZZZZ_.jpg' alt='cover of Learn to Program' /></a></p><p>今天,我要推荐一本书,即如图这本 [Learn to Program][0], 由 <a href='http://www.pragmaticprogrammer.com/'>The Pragmatic Programmers, LLC</a>(是我喜欢的仅次于 O'Reilly 的出版社 :) )出版。这本书据说是给小孩子学习编程入门用的。所以,这本书给没有任何编程训练的我来说,入门是最合适不过的。如果你,像我一样,需要在设计或者编写 HTML/CSS 之余,还必须承担一定的前台程序编写,或者你对编程的兴趣比我还高,但尚未入门,又或者,你真的是五六岁的小孩(我有这样的读者吗 -_-)要学编程……那么这本书是你的不二之选。</p><p>这本书使用 <a href='http://www.ruby-lang.org/en/'>Ruby</a> 教学,虽然有种种非议,但不可否认,它属于世界上最接近人类语言的编程语言之一,所以,用来学习编程的基础概念,我相信,它比 C 更容易。</p><p>作者 <a href='http://pine.fm/'>Chris Pine</a> 现供职于 <a href='http://www.opera.com/'>Opera</a>,擅长于教学编程。Chris 文风很幽默,一上来回忆编程,就想起怎么会有女孩脸红地看着他原来裤链没拉(真的是小孩看的书么-_-)……所以他让你学习枯燥的编程过程中并不枯燥,他时不时加点料子让你会心而笑。而且语言平实易懂,运用大量现实生活中的实例来解释程序中的术语,让你过目难忘。举个例子,书中是这样解释对象与方法的。假如钟表是一个对象,则时针的转动就是该对象的方法;假如对象是名词的话,方法则类似于动词,而方法的参数就类似于副词等等,只有亲自去阅读才能发现其中的乐趣。你会发现,从来没有能够如此轻松地念完一本讲编程的书。</p><p>这本书从如何在各个平台配置好 Ruby 环境开始,逐一讲解数字、字符、变量与赋值、方法、流程控制、数组与遍历、自定义方法、类、自定义类以及块和过程(Proc),基本上涵盖了 Ruby 的基本要素。就是说,如果学习完这本书,如果还想继续使用 Ruby,那么,你已经准备好了!不想继续用 Ruby 怎么办?前面说过,这本书绝对不是 Ruby 的入门书而已,它也是<strong>编程入门书</strong>,你将从此书获得编程的基本技能,这些技能是所有程序都能适用的(如果是面向对象编程语言则更好),你完全可以开始投入另外一门语言的基本学习中去了。</p><p>如果你已经确定要学习编程,但还在犹豫选择什么语言,选择什么教材,那么,可以看看这本书,我相信,你一定能有所收获的。重要的是,这本书不厚,短短 150 多页,如果你浏览,1 天不到可以看完,用心学,也不过一个星期。你觉得还有什么负担能阻止你学习编程吗?现在就开始吧。</p><p>更新:另外,IBM developerWorks 上也有<a href='http://www-128.ibm.com/developerworks/cn/rational/rationaledge/content/may06/reader/pollice2.html'>此书的书评</a>。</p>%A7+%D8$%98%A59%5C%14%5C%20%D1%17%10k%0BLearn to Program 书评2006-11-25T10:00:00+08:00https://realazy.comRealazy<p>从今天起,我将陆续将 <a href='http://www.quirksmode.org/book/'>ppk on JavaScript</a> 的读书心得发布到这个 blog 上。<a href='http://www.quirksmode.org/'>ppk</a> 是我所景仰的一位 web 开发者,原因无它,只是因为作为一个 JavaScript 的开发者来说,他涉及的领域包括 web 标准,可用性,无障碍等,正是其他开发者所不关注或者故意忽略的。并且,他写了很多案例测试不同的浏览器,总结出 JavaScript 的接口(API)兼容性,成为 JavaScript 开发者重要参考资料,几年如一日,这种钻研精神是很多人所缺乏的。</p><p>ppk 在今年 9 月出版了他的书,我从去年起就在等的书。今天拿到手,迫不及待地把第一章阅读完毕。果然让人充满惊喜,他的功力非同一般。虽然只是一个初学者,但我认为我已经走在正确的学习道路上。我想,我若能将学习心得分享,能让正在学习的人看到,可以一起交流一起进步,尽管我不敢确保你能从我这里得到什么启发,但我可以确信,我这些笔记会比你拷贝粘贴代码的学习方式更正确。</p><p>这本书有十章,章名都简洁明了,分别是:目的,背景,浏览器,准备,核心,BOM, 事件,DOM, CSS 更改和数据获取。从来没有一本书能如此简洁地明确 JavaScript 的方方面面,因此学习不会有太大负担。前言不宜过多,下面就开始我的第一章学习笔记。</p><p>开篇宗义:JavaScript 的目的是,为网页<strong>增加特别的一层可用性</strong>。听起来很简单,但这条黄金定律经常被人误解。就算编写有用的 JavaScript, 开发者可能还是没能结合适当的情景:Web 标准运动发展下,与当代无障碍的 HTML 页面的配合。更为不妙的是,有些开发者不是为网页增加一层可用性,而是用整层取代之,后果是,如果浏览器不支持 JavaScript, 网站就完了。</p><h2 id="">概念概述</h2><p>JavaScript 是一门由浏览器解释的脚本语言。它通过在客户端而不是服务器端处理某些交互,比如表单验证,创建新菜单来给网站增添可用性。传统的网页交互是,客户端的一举一动都必须经过服务器端的出来才能反馈回来,漫长的等待会让用户崩溃。而 JavaScript 可以在客户端代替服务器端做某些事情(最明显的,表单验证),从而提高用户体验。</p><p>随着时代的发展,JavaScript 能够处理越来越多的交互。问题出现了,JavaScript 能做这么多事情,到底要多用还是少用?这就有了富与瘦的对决。是整个页面都用 JavaScript 来控制交互还是只增加些许的 JavaScript 来增强可用性?就是说,尽可能地使用 JavaScript 还是有所节制,甚至不用?</p><p>瘦客户端很大程度上依赖于客户端-服务器的通讯,而富客户端尽可能限制额外的数据通讯。</p><p>哪种方式更好?尽管富客户端带来一些可用性益处,但瘦客户端可能是更「标准」的 JavaScript 用法。Web 被认为是文档集合,而不是界面集合。最明显的证据是,浏览器有后退前进的功能让你在文档中跳转而界面会有么?浏览器可以收藏(书签)文档而界面可以么?从无障碍来说,瘦客户端也更少出错。</p><p>这种非平衡性是很难解决的。富客户端当然也可以在更高级的界面做到前进后退,或者收藏,也可以做到完美的无障碍。这必须需要大量的额外工作,但不是每个项目都有超出预算的时间或金钱。此外,太过专注于可用性而忽略无障碍也是一个问题。</p><p>那么 JavaScript 的目的是为富客户端还是瘦客户端服务?答案是:看情况。得看你的网站,你的受众,你的 JavaScript 水平。</p><h2 id="">技术概述</h2><p>JavaScript 分为六个方面,分别是核心(Core),浏览器对象模型(BOM),事件(Events),文档对象模型(DOM),CSS 变更和数据获取(XMLHttpRequest)。</p><p>上古时代,NetScape 领头之时,NetScape3 是事实标准。</p><p>当代却没有这么简单。ECMA 标准化 JavaScript Core, W3C 标准化 DOM,而 BOM 尚在 WHAT-WG 的标准化中,W3C 也刚有了 XMLHttpRequest 的第一份草稿。今天,BOM 依然遵循 NetScape3 的事实标准,而 XMLHttpRequest 还是遵照 Microsoft 的原始规范。</p><p>JavaScript 的目的在于为网站增加可用性,而不是破坏用户的隐私和安全。因此 JavaScript 不允许读写用户的文件(cookies 除外),采取同源策略,只允许来自相同域的交互。不允许读取历史记录,不能为上传文件的表单设置值,由 JavaScript 控制的窗口关闭需经用户确认,由 JavaScript 打开的窗口不能小于 100x100 的窗口,不能移出屏幕之外。</p><h2 id="JavaScript">JavaScript 的历史</h2><p>探寻历史才能让我们知道 JavaScript 为什么会被误解得如此深。JavaScript 的创造者是 Brendan Eich,首次在 NetScape 2 中实现。它的目的是创建一门足够简单的语言让开发者能容易地为网页增加交互,只要把代码拷贝过来调整一下就可以。这确实令人赞叹,很多 JavaScript 开发者是从拷贝粘贴开始的。</p><p>不幸的是 JavaScript 生错了名字,也生错了语法。最初它叫 LiveScript,但 1996 年的时候 Java 炙手可热,NetScape 想搭顺风车,于是某产品经理(我想知道她/他是谁,呵呵),命令更名,命令 Brendan Eich 让"Javascript 像 Java"。这让很多人误认为 JavaScript 是 Java 的低级版,不能引起严肃程序员的关注。</p><p>1996 年之时,NetScape 3 是王,Microsoft 只能照抄。这是一个难得的和谐期,当然,那时候浏览器比起现在来「瘦」了,仅限于表单验证,鼠标轮换的一些小花招而已。</p><p>接下来就是影响深远的浏览器大战了。为了争夺市场,两家浏览器纷纷实现不同的东西,谁都想成为事实标准。最有名的就是 NetScape 4 的 <code>document.layer</code> 和 IE 4 的 <code>document.all</code>(忘记它们吧!)。它们让 DHTML 流行起来。</p><p>1999 年 Microsoft 以推出良好支持 CSS 和 DOM 的 IE5 胜出,NetScape 的让位终于有足够的时间让一场革命发生,那就是 CSS。WaSP 首先从 CSS 入手,而很多专家也发现/发明了许多浏览器的补救办法,让这场革命成为可能。</p><p>2003 年,一些先锋们在 CSS 革命的影响下开始探索新的 JavaScript 风格,更多地关注无障碍,改观人们对它的坏名声,那就是 unobstrusive——把 JavaScript 从 HTML 结构层分离出来,遗憾的是,那些在浏览器大战存活下来的程序员可能还没有发现这条新道路。</p><p>2005 年,Ajax 热潮为 JavaScript 社区注入新的血液。但某些方面,Ajax 太像 DHTML 了,无障碍,是很多 Ajax 应用的难言之隐。这个热潮趋向于关注技术(如何 Ajax),而可用性和交互(为何 Ajax)却被低估。最后,各种肿胀的库(现在称为框架)迅速发展起来。</p><p>Ajax 依然全速前进,但这会像 DHTML 一样结果,人们渐渐失去兴趣,它们会土崩瓦解。</p><p>JavaScript 兴衰史好像有一定的「定律」支配,我们能打破这个怪圈吗?不管如何,JavaScript 开发者在寻找各种酷代码和华而不实的框架之外,更应该调整自己的行动,让 JavaScript 运行在:标准兼容的,无障碍的网页中。</p>%3C%C8%DE%C2J%B6%A1%9B%F9$Fl%C1x%FF%A0JavaScript 的目的2006-11-19T10:00:00+08:00https://realazy.comRealazy<p>其实标题起得不好,这篇文章更像是一个软件的评论(review). 但为了我憧憬的 TextMate,请原谅我这么做。</p><p>如果你和我一样,是 <a href='http://rubyonrails.org/'>Ruby On Rails</a>(简称 RoR, <a href='http://rubyonrails.org.tw/'>繁体版</a>)爱好者,那么对 <a href='http://www.macromates.com/'>TextMate</a> 这个 Mac OS X 下的编辑器一定不会陌生(事实上,你不能不看到它,因为 RoR 圣经 <a href='http://www.pragmaticprogrammer.com/titles/rails/index.html'>Agile Web Development with Rails — Second Edition</a> 中,作者公然为这个编辑器打广告 ── 整个 Rails 团队都在用!)。多少人,为了这个 €39 的东西买了一台不下 $1200 的 mac 机器……事实上,这也是我的梦想……如果你想知道 TextMate 的魅力多大,不访看看 RoR 或者官网上的演示视频。</p><p>言归正传,对于目前的我来说,「有钱 Mac,无钱 Linux」是我的哲学,憧憬是我活下去的因素之一,为了活得更长久,先不买 mac 吧(借口还不算烂吧 XD)……</p><p>开源社区是伟大的!我发现了一款编辑器,尽管不敢用「媲美」这个词,但实现了我最喜欢的部分,让我更心安不买 mac 了。它叫「书记员」,这是我给 <a href='http://scribes.sourceforge.net/'>Scribes</a> 的翻译。</p><p>如果你已经看过 TextMate 的演示视频,不妨在看看我们「书记员」的:<a href='http://scribes.sourceforge.net/demo.htm'>Scribes In Action</a>. 代码自动完成功能是不是很帅?</p><p>从「书记员」的 <a href='http://scribes.sourceforge.net/functions.html'>UI</a> 上来看,工具栏上真是简洁得不得了,虽然新手可能一下子不知道某些 icon 的功用(对于一些 icon 其实是不言而喻的,因为都遵循了<strong>惯例</strong>),但是,这么少的功能,并不会带来多大的记忆负担,上手并使用绰绰有余。</p><p>工具栏这么少并不代表「书记员」简单。为何叫「书记员」?专注于文本编辑啊。编辑文本要干嘛?跟键盘打交道,尽量减少鼠标的使用,大量使用快捷键,文本自动完成,这才能提高文本编辑的效率。</p><p>快捷键?不错,世界上的编辑器都有快捷键,比如 VI。但是,学习这些编辑器的快捷键就跟学习五笔一样痛苦(假如可以叫痛苦的话),你需要持之以恒的练习(我想这些时间是不是可以节省出来做其他事情,让生活更美好)。我们的「书记员」不会让你这么痛苦。</p><p>它所有快捷键都是<strong>自我注释</strong>的。首先,操作文件,「书记员」是遵循惯例,CTRL + O 打开文件,CTRL + S 保存文件,没错,你已经猜到 CTRL + Shift + S 是另存为。包括查找 CTRL + F,替换 CTRL + R,这些都没有什么出奇之处,因为这已经成了事实上的标准,创新反而会让人接受不了。这不用学习,自然而然。</p><p>对,「书记员」把功力发挥在编辑文本的过程中。如果我告诉你 ALT + W 选择一个单词, ALT + L 选择一行,那么 ALT + P 选择什么? W = <strong>W</strong>ord, L = <strong>L</strong>ine, P =? bingo! right, it is <strong>P</strong>aragraph! ALT + P 选择了一个句子。那么, ALT + S 你还用记忆吗? 那就是 <strong>S</strong>entence,句子。基本上,把说明书上的快捷键看一轮,练习一轮,就能使用「书记员」给你带来的强大功能了,那么,我们有更多时间追求美好的生活了。</p><p>对于需要写代码的我们来说,许多工作都是在重复劳动。我们需要模板,让编辑器帮我们完成重复的工作,比如,我输入 <code>htmlstrict</code>,编辑器应该能够帮我生成 Doctype 是 XHTML 1.0 Strict 的基本模板。提供自动完成的编辑器当然数不胜数,但定制模板的方便性,「书记员」的方式绝对是我见过的最简易的。</p><p>先看看「书记员」的代码自动完成方式,假设我有一条这样的规则:</p><pre class='plaintext'><code class='plaintext'>link: <a href="${URL}">${text}</a>${cursor}</code></pre><p>当我在「书记员」里敲下 link,按 tab 键,则会自动生成 <code><a href=\"URL\">text</a></code> 的代码,并且光标落在 URL 处,改写为实际需要的链接即可,再按一下 tab,就跳到下一个 text 处,最后跳到鼠标定义的位置(<code>${cursor}</code>),如果定义了的话。</p><p>Keep it simple, stupid(KISS)! 强悍的程序不需要注释!因为它们都是自我注释的!没错,「书记员」的模板定制方式是自我注释的。请看这条 <code>link</code> 标签的模板:</p><pre class='html'><code class='html'><a href="${URL}">${text}</a>${cursor}</code></pre><p><code>${text}</code>? 很明显,意思是说,自动完成 <code>a</code> 标签后跳到这里进行编辑,编辑完后鼠标(<code>${cursor}</code>)跳到最后。很简单明了吧?这样你就可以很方便自己定制自己的模板系统,而且随心所欲,爱干嘛干嘛去,生活多美好哦……</p><p>其实这强大的定制性正是我需要的,我可以 YY 一下 TextMate 了,呵呵……</p><p>更新:<a href='http://linuxtoy.org/archives/scribes_0_3_review.html'>Scribes 0.3 点评</a></p>%CA%F7?%11%17%E5%BD%F5%DEp%EC%9Cie%E4%92「书记员」── 穷人的 TextMate?2006-11-11T10:00:00+08:00https://realazy.comRealazy<p>您可以把本篇当做是《与臭虫为友——浏览器补救办法,臭虫以及解决方案 》<a href='/posts/2006-07-29-working-with-buggy-browsers-1.html'>三</a><a href='/posts/2006-07-31-working-with-buggy-browsers-2.html'>系</a><a href='/posts/2006-08-20-working-with-buggy-browsers-3.html'>列</a> 的第四系列。</p><p>其实,对于专职于 web 标准的人来说,我们在 IE7 Beta1 的时候就准备好了,XD。不信可以看看 <a href='http://old9.blogsome.com/2006/04/29/css-hack-for-ie7/'>针对 IE7 的 CSS Hack</a>,如果你是沉迷于各种 hacks 的人,务必务必要看!各位知道我从不转载的作风,所以,给 <a href='http://old9.blogsome.com/'>old9</a> 一个机会,狠狠地点击吧 :D</p><p>IE7 这个版本极度提升了对 CSS 的支持,外加一个长久以来备受抱怨的透明 PNG 支持。除了没有 <a href='http://www.w3.org/TR/CSS21/generate.html'>CSS generate content</a> 以外,其实没有什么好抱怨的,抛弃 hacks 的时代真的来临了,条件注释的方式将是您最好的伙伴。实际上,一直以来,在我的项目中,我只有一个 <code>ie6patch.css</code>(我一直对这个命名引以为豪),是不是需要引入一个 <code>ie7patch.css</code>?在大部分情况下,已经没有必要的了,我前面说过,IE7 对 CSS 的改进是有目共睹的,但不可避免,作为 MS 的产品,没有缺陷是卖不出去的 XD。</p><h2 id="">自动清除浮动问题</h2><p>一个<a href='http://www.positioniseverything.net/easyclearing.html'>自动清除浮动</a>最安全的做法是使用 <code>:after</code>,而这正是 IE7 缺少的支持。在 IE6 中,们使用 <code>height: 1%</code> 来达到相同目的。解释一下这条 hack 的原理吧,<code>height</code> 的值可以是任意的,它只是用来触发 IE 特有的 <a href='http://old9.blogsome.com/2006/04/11/onhavinglayout/'>`hasLayout`</a>(没错,还是 old9 的),而在 IE6 中,<code>height</code> 实际上等同于 <code>min-height</code>。遗憾的是,IE7 已经修正了 <code>height</code> 这个 bug。幸运的是,IE 的 <code>hasLayout</code> 还存在,我们也可以给高度通过指定一个数值来触发它。IE7 新支持的 <code>min-height</code> 可以帮忙。所以,在 IE7 中,这句可以帮您搞定自动清除浮动的问题:<code>min-height: 1px;</code>。(在我看来 1px 要比 1%安全,您随自己的喜好吧)。</p><p>如何做?</p><pre class='css'><code class='css'>* html .wantoclear { height: 1%;} /*ie6*/* + html .wantoclear { min-height: 1px;} /*ie7*/</code></pre><p>这对于小型项目来说可以 quick and dirty,但是大型项目中,我个人的建议是,还是使用条件注释的方式吧,在 <code>ie7patch.css</code> 的中只需写 <code>.wantoclear { min-height: 1px; }</code>,更方便于维护。</p><p>另外,清除浮动的方式有多种,比如使用 <code>overflow</code>,但实践证明,只有上述方式是<strong>最安全</strong>的。其他方式可以酌情使用。参看:<a href='http://old9.blogsome.com/2006/04/21/enclosing-floats/'>闭合浮动元素</a>(还是 old9!)。总之,您现在看到您的项目页面不能正常显示,十有八九是它导致的,按照我的方法,Get ready for IE7 吧 :)</p><h2 id="">百分比宽度问题</h2><p>这个问题让头痛,从 IE5 起,到 IE7,遗憾的是并没有修正。当您使用百分比作为宽度单位时,IE 的计算总是差那么几个像素。在一般应用中,这倒不会导致什么问题。但依靠浮动来布局的话,这确实致命的,因为稍微有 1 像素的差异,IE 不像 Firefox, Opera 还能让布局保持,它会将另外超出父元素的元素狠狠地甩到下一行中,导致布局的错落。举个简单的例子,假设我们有一堆的 <code>li</code> 元素,我们需要每行放置 4 个。用百分比的方式来做,则可以一句很简单的 <code>li { float: left; width: 25%; }</code> 搞定,且具有巨大的灵活性。但是 IE……wtf...有时候确实正常给你每行排列 4 个,有时候却只有 3 个。你看到比你预期的少一个,那就是它计算宽度的误差导致的。</p><p>解决方法其实很简单,把宽度减少 0.1% - 0.5%就可以了。就是说,你可以在 <code>ie7patch.css</code> 写上 <code>li { float: left; width: 24.9%; }</code>。</p><p>IE7 还存在这么多问题,而且不知道到 IE8 会不会修正,这就是为什么还需要使用条件注释的原因。</p><p>IE7 即将通过「高优先级更新」来入侵用户电脑,我们还是提前做好准备吧。</p><p>另外,目前的实践经验还不是很多,很多问题尚在发现中,这是一篇不断更新的 blog,欢迎大家补充,谢谢。</p><p>2006-11-16 更新:</p><p>根据 <a href='http://www.456bereastreet.com/archive/200611/three_reasons_sites_break_in_internet_explorer_7/'>Three reasons sites break in Internet Explorer 7</a>, 导致 IE7 出问题的三个可能理由是:</p><ul><li>在 Doctype 之前使用了导致 IE6 使用 Quirks mode 的 XML 声明,IE7 已经修复这个问题,相同的 CSS 而不同的渲染模式,因此 IE7 可能会出问题。</li><li>网站构建基于大量的 hacks,而这些 hacks 在 IE7 中已经修复。</li><li>使用没有判断浏览器版本的条件注释来引入 CSS,导致 IE7 也引入了那些本不该是修复 IE7 的 CSS。</li></ul><p>另外,推荐阅读 <a href='http://www.thinkvitamin.com/features/design/internet-explorer-7-were-you-ready'>Internet Explorer 7: Were you ready?</a>。</p><p>2006-11-23 更新:</p><p>推荐阅读:<a href='http://www.thinkvitamin.com/features/design/wake-up-and-smell-the-ie7'>Wake up and smell the IE7!</a></p>%ED.MhA%EF%EB%10%9F%D1%FF%1D%CA%850%1AIE7,你准备好了没?2006-11-07T10:00:00+08:00https://realazy.comRealazy<p>看到这个题目,第一感觉是耸人听闻。不管是 95% 或是更多或是更少,排版确实是一个不可忽略的问题。在我所遇到的 web 设计师中,或许是遇到的还少,我没有发现谁真正在意 web 排版。看到这篇文章,感触颇深(因为我刚使用 linux 的时候需要配置字体,顺便学习了不少的字体知识,也顺便成了一个爱好),周日在家挺闲,为了提高英语水平,顺手翻译了出来。希望对大家有所启发。</p><p>这篇文章来自一个研究并提供信息架构的网站:<a href='http://www.informationarchitects.jp/'>Information Architects Japan</a>,原文:<a href='http://www.informationarchitects.jp/the-web-is-all-about-typography-period'>Web design is 95% typography</a>.</p><p>又及,文章多次出现 typography 这个单词。正规的中文翻译应该是「排印」,但考虑 web 这个非印刷媒体,使用排印并不见得好。于是我根据语境,有时用「排版」,有时用「排印」,请读者明鉴。以下是翻译正文。</p><p>web 中的信息有 95% 是成文的语言。为何 web 设计师应该好好学习一下设计成文信息的主要准则,换句话说,排版?这就是最佳的理由。</p><h2 id="">信息设计就是排版</h2><p>回到 1969 年,Emil Ruder, 一个瑞士著名的排印大师,其笔下关于当时的印刷氛围,简直就是我们今天网站的写照:</p><blockquote><p>今天印刷术的泛滥成灾让我们的个人价值被轻视,因为郁闷的我们现在并不能掌控印刷中的一切。分割、组织和实现印刷术的一切是排印师应有的任务,只有这样,读者才有更好的机会找出吸引她/他的东西。</p></blockquote><h3 id="21">信息设计师是 21 世纪的排版师</h3><p>稍微联想一下(请用在线排版取代印刷),这就像是信息设计师的职务描述。<strong>分割、组织和实现印刷术的一切是「信息设计师」应有的任务,只有这样,读者才有更好的机会找出吸引她/他的东西。</strong>宏观排版(所有的文本结构)跟微观排版(样式和空间的细节方面)包括我们今天称为「信息设计」的很多方面。也就是说,信息设计师今天正在做排印师 30 年前的工作:</p><p>排版面临一个很直白的职责,这就是传达信息。没有理由能免除排版的这一职责。一个并不能阅读的印刷品只能变成一个无用的产品。</p><p>优化排版就是优化阅读,亲和力,可用性(!),综合文本平衡等。组织文本块并结合图片,难道不是图形设计师、可用性专家、信息架构师应该做的吗?但为什么这是一个被忽视的主题呢?</p><h2 id="">为什么这是一个被忽视的主题?</h2><h3 id="">字体太少?分辨率低?</h3><p>主要原因——抱怨连天的——不看好在线排版准则的观点是,能用的字太少了。第二个观点是屏幕分辨率太低,让像素或者反锯齿字体之一很难阅读。</p><h3 id="">文艺复兴:只有一个字体</h3><p>认为我们没有足够的字体可用的观点是不切题的:在意大利文艺复兴时期,排印师只有一个字体可用,但这个时期还是产生一些顶级漂亮的排版品:</p><p><img src='http://static.flickr.com/86/282043044_99bc7882dc_o.gif' alt='SpecimenBook' /></p><p>该排印师应该没有太在意他手中手能掌握的字体种类,实际上也不应该太在意字体的选择。他应该更关心时代能赋予自己手中掌握的,并尽力发挥。</p><h3 id="">选择字样并不是排版</h3><p>第二个观点也并不见得好到哪儿去。在印刷初期,印出来的字母质量比我们今天在显示器上看到的更差。更重要的是,如果处理得专业,屏幕字体更易于阅读。信息设计不是关于使用好的字样,而是关于使用好的排版。两者的区别很大。谁都可以使用字样,有人可以选择好的字样,但只有少数人能够精通排版。</p><h3 id="">把文本当作用户界面</h3><p>对,不同平台和不同浏览器如何处理字体是恼人的,也对,分辨率问题很难让注意力集中坚持到五分钟。但是,好啦,确保文本在所有主要平台和浏览器中赏心悦目是 web 设计师的职责。正确的行间距,单词和字母间距,留白,一定量颜色的使用有助于可读性。但还不彻底。一个优秀的 web 设计师知道如何跟文本而不仅仅是内容打交道,<a href='http://www.cameronmoll.com/archives/001266.html'>「把文本当作用户界面」</a>。瞧瞧 <a href='http://www.subtraction.com/'>Kohi Vinh 的网站</a>,你大概会明白他的意思:</p><p><img src='http://static.flickr.com/86/282055763_38c8ffa42f_o.gif' alt='koivinh' /></p><p>稍微著名把文本当作用户界面的 <a href='http://en.wikipedia.org/wiki/Ornament_and_Crime'>unornamental</a> 网站例子有: <a href='http://www.google.com' title='google'>google</a>, <a href='http://www.ebay.com' title='ebay'>ebay</a>, <a href='http://www.craigslist.org' title='craigslist'>craigslist</a>, <a href='http://www.youtube.com' title='youtube'>youtube</a>, <a href='http://www.flickr.com' title='flickr'>flickr</a>, <a href='http://digg.com/design/web_design_is_95_typography' title='digg'>Digg</a>, <a href='http://reddit.com/search?q=typography' title='reddit'>reddit</a>, <a href='http://del.icio.us/post?url=http://www.informationarchitects.jp/the-web-is-all-about-typography-period' title='delicious'>delicious</a>. 一个难以争辩的必然事实是,把文本当作用户界面是成功的唯一参数。成功的网站设法同时创建简单的界面和强烈的特征。但这是另外一个主题了。</p><h2 id="">从哪开始</h2><p><strong><a href='http://webtypography.net/toc/'>web 排版</a></strong></p><blockquote><p>为了「破除关于 web 排版的某些荒谬想法」,他已经「按照 Bringhurst 的工作原则来构建他的网站,并解答如何通过 HTML 和 CSS 中可用的技术来达成各项目标」。</p></blockquote><p><strong><a href='http://www.markboulton.co.uk/journal/comments/five_simple_steps_to_better_typography/'>优化排版的 5 个简单步骤</a></strong></p><blockquote><p>他所谈论的排版「并不是你典型的'该用哪种字体'的排版」。对于相信让字号和行间距默认大小使得文本能够任意缩放会提高网站可用性的人,这是一个不错的阅读材料。</p></blockquote><p><strong><a href='http://www.subtraction.com/'>Khoi Vinh</a></strong></p><blockquote><p><a href='http://www.behaviordesign.com/'>behaviordesign</a> 的创始人之一。现为 <a href='http://www.times.com/'>NYTimes.com</a> 的设计主管。非常天才的一个人。</p></blockquote><p><strong><a href='http://www.rodgraves.com/'>Rod Graves</a></strong></p><blockquote><p>通讯设计师。令人赞叹的工作:"排版是我绝对的中心。排版网格和层次通常会成为我开发的视觉语言的基础。"</p></blockquote><p><strong><a href='http://www.alistapart.com/topics/design/typography/'>A List Apart</a></strong></p><blockquote><p>通过字样通讯。字体和布局。为读者设计。易读性。字样,图形设计。web 排版的问题。控制 web 排版:字号、字体和颜色。CSS 方法,浏览器问题,用户问题和解决方案。</p></blockquote><p><strong><a href='http://www.atypi.org/'>Association Typographique Internationale</a></strong></p><blockquote><p>ATypl(Association Typographique Internationale) 是一个专注于样式和排版的主要国际组织。创建于 1957 年,ATypl 为国际样式社区之间提供通讯的结构,信息和活动。</p></blockquote><p><strong><a href='http://www.thinkingwithtype.com/'>Thinking with Type</a></strong></p><blockquote><p>书籍 Thinking with Type 的在线伙伴:设计师,作家,编辑和学生的关键指南。</p></blockquote><p><strong><a href='http://typetester.maratz.com/'>Typetester</a></strong></p><blockquote><p>比较屏幕字体样式。</p></blockquote><p><strong><a href='http://typophile.com/'>Typophile</a></strong></p><blockquote><p>Typophile 是一个会员制和赞助商支持的社区。2000 年以来 Typophile 引领开放合作,并且我们总是能学习到好主意。我们他们伺服超过每月 3 百万的网页。</p></blockquote><p><strong><a href='http://typophile.com/wiki/start'>Typohile Wiki</a></strong></p><blockquote><p>一个有关样式和设计所有一切的用户创建百科全书。用户为建立协作,有用,平衡和相关的资源而创建和编辑条目。</p></blockquote><p><strong><a href='http://www.poynter.org/column.asp?id=47&aid=78683'>The Next Big Thing in Online Type</a></strong></p><blockquote><p>比尔盖茨要计算机用户,哦,微软用户,能够拥有一个更舒畅的屏幕阅读体验——太重要以至于提高屏幕阅读成为他最重要的五件事之一。</p></blockquote><p><strong><a href='http://www.amazon.com/gp/product/3721200438?ie=UTF8&tag=informationar-20&linkCode=as2&camp=1789&creative=9325&creativeASIN=3721200438'>Emil Ruder, Typographie</a> (Amazon)</strong></p><blockquote><p>Emil Ruder's Typography 是一本永恒的书,几代排印师和图形设计师都从中学习基本原理。Ruder, 二十世纪最伟大的排印师之一,是一个抛弃并用新的规则取代 Emil Ruder 传统规则的先锋,来满足新排印技术的需求。</p></blockquote><p><strong>更新:</strong>该文作者在疑问下迅速推出<a href='http://www.informationarchitects.jp/webdesign-is-95-typography-partii'>第二部分</a>,感兴趣的朋友可以看看。本想翻译出来,但太忙了,有心无力,见谅。</p><p>另外,小小抱怨一下,很多朋友都对这篇文章有很多热烈精彩的讨论,但是为什么不发一个 trackback 回来呢?这样可以方便读者追踪文章的各种观点。</p>%C0C%DBB%EEb*%EDL%B5%B1%3C%E9%9Cx$web 设计 95% 是排版2006-10-29T10:00:00+08:00https://realazy.comRealazy<p>最近对 Web 前端有很多想法,刚好看到这篇文章,跟我想法不谋而合,所以翻译出来与大家分享。许久没翻译了,里面多少还是有些我没能完全理解,意译过来,如果错误,请务必指出和修改,谢谢。</p><p>原文 <a href='http://www.garrettdimon.com/archives/the-time-is-now-for-front-end-architects'>The Time is Now for Front-End Architects</a>, 来自:<a href='http://www.garrettdimon.com/about/'>Garrett Dimon</a>,感谢作者的许可。</p><p>去年,我在 YTS 发表了<a href='http://www.yourtotalsite.com/archives/methodology_tools/the_frontend_architect/Default.aspx'>前端架构师</a>的想法,之后花更多时间来思考,现在更坚信这是一个不可或缺的角色。</p><p>当后端技术伴随.Net, Rails 和 Java 之类的框架发展得越来越抽象和强大,前端技术的潜在发展也日益复杂。在束缚前端技术潜在好处的差劲实现之前, Web 需要更多的前端架构师。</p><p>多亏了诸如跨浏览器支持的先进技术的发展,用户体验、更多有意义的主题比如无障碍都拨云见日,这个世界再也不仅仅就 HTML 和 CSS 如此简单,因此,绝大部分的团队都需要一个真正理解和实践涉及到前端的<strong>一切</strong>的人。</p><h2 id="">角色</h2><p>这并不是一个扼要和简单的清单,对于下面的主题/技术,前端架构师也不能仅仅满足于了解一下里里外外而已,而是需要足够的深入研究,并有自己出色的见解。</p><ul><li>XHTML</li><li>CSS(1, 2, 3)</li><li>跨浏览器和跨平台</li><li>DOM 脚本编程</li><li>AJAX</li><li>Flash</li><li>渐进增强和适度降级</li><li>无障碍</li><li>可用性</li><li>信息架构</li><li>界面设计</li><li>视觉设计</li><li>表现层逻辑(ASPX, Rails 视图等)</li><li>商业规则和逻辑</li></ul><p>作为一个前端架构师,必须拥有这些领域的绝对执行力。例如,前端架构师能够决定某个特性是使用 AJAX 还是传统的页面刷新。哪个更便于使用?对无障碍的影响如何?改用 Flash 有意义吗?</p><h2 id="">拨乱反正</h2><p>表现,结构,行为和商业逻辑的混杂,导致不必要的复杂,导致难以维护的怪胎解决方案。就如后端需要正确地划分为数据层,商业逻辑,表现逻辑等,前端开发复杂到是时候调整其架构了。</p><p>编写良好结构或者说避免使用表格布局是远远不够的。这是第一步,前端架构的哆咧咪而已。现在是时候关注 DOM 脚本编程,AJAX, 无障碍等,该升级了。</p><h2 id="">非编程不可</h2><p>我主张前端架构师必须懂得真正的编程知识,而这正是很多自封为前端架构师的人所缺乏的。我的意思不是能够剪切粘贴改进代码就行了,而是能够跟老练的工程师商讨如何能够最好地结合前端。</p><p>这就是说,前端架构师需要真正理解结构遭遇商业逻辑的问题。如果工程师说某些东西使用 ASP.Net DataGrid 是不可能实现的,前端架构师必须能够解释如何与为何要使用 DataList 或 Repeater 取代,解释为何 DataGrid 在该情景下是个错误的选择……</p><p>这只是个例子,问题还在于仅知道客户端编程也是不够的。能够使用与工程师相同的术语,能够讨论(前后端)关键集成的最佳解决方案,这是绝对必须的。</p><h2 id="">断线的风筝</h2><p>我们今天正处在一个不妙的处境中,原因在于几乎没有人能够为前后端的沟壑搭桥。一般工程师不会有兴趣或实践标记,CSS, 或 DOM 脚本编程,大部分客户端开发者也没有与后端技术协作的经验。几周入门 PHP 不会成为程序员,几周入门 XHTML 也不会成为真正的客户端开发者。</p><h2 id="">罪魁祸首</h2><p>我首先想到的十足例子是,ASP.Net 完全漠视 Web 标准,同样地,web 氛围(我们指表格和占位 gif)让 Web 标准郁闷。企业项目的大多数框架输出的标记,即使使用 1999 年的标准来衡量,都是糟糕无比的。</p><p>如此巨大和「专业」的产品怎么能才够不忽视,按理说是整个项目最简单的方面?只有静态代码。理由是,基于技术的立场衡量产品,结构,CSS 和其他客户端技术都是「事后诸葛亮」。表现逻辑,结构和行为混杂,压根无助于无障碍,Web 标准,或者前端技术干净的分离。抬起你的头来,就在 2006,这些都成受欢迎的惯例了。</p><h2 id="">总结</h2><p>如果这个世界上姿态最鲜明的产品和项目都如此低劣的方式来处理事情,其他的还有什么好说?毫无疑问,我们需要前端架构师,而且就在昨天。</p><p>归结于归结,我们有一堆相互关联的技术,很少人能够埋头钻研它们之间的关系,这很不幸。正确做事的真正价值在于容易的维护和长期的适应性。虽然在关键时刻,有些方式更容易选择其他的方法和拼凑起另外的东西。对某些人来说,这可能是可接受的做事方式。但是,对我们大部分人来说,这是拙劣的抉择,也非常不专业。</p><p>我交给你去想了。我假设你把车交给技工修理,修好了时候,瞧瞧引擎罩内大量的输送管,我不知道你对技工作何感想?</p>%11%D4%B3%04J%EE%93%7FK%01a%CE%93%88%9A?是时候了,前端架构师2006-10-27T10:00:00+08:00https://realazy.comRealazy<p>有交互就有反馈。反馈可以有很多种,包括各种界面元素,声音或者影像的变化。这种变化的目的在于,提醒用户操作结果。这个结果十分重要,成功,则进入下一阶段的使用;失败,则继续或修改当前操作至成功。一般来说,成功的操作,软件设计的习惯都是直接跳转到下一阶段,没有必要使用冗余的反馈来干扰用户。当然,这个阶段是最后阶段的话,反馈不可少,让用户知道已经完成阶段任务。失败的操作,反馈显得十分重要。如果没有明显的反馈,用户会不知所措,或者干脆放弃对软件的使用。</p><p>当然,你知道我不是交互设计师,也不是「用户体验设计师」,我只是一个「页面切割师」(Orz...)……我想从「页面切割」的经验中,从逛到的各种有趣网站(你知道,我热爱互联网 :))中,谈谈网站(现在流行叫 Web App,对,网站也是软件哇)使用过程中的反馈。</p><p>网站有自己的特殊性。<strong>它必须运行在一个已有的软件(各种浏览器)上,必须受到运行坏境的种种制约。</strong></p><p>在'Web 1.0'时代,通常的做法是,所有一切交互,都等待服务器接收数据处理后,返回处理的反馈。看看下面,你是不是觉得很熟悉:</p><p><img src='http://static.flickr.com/104/269193909_1f8c4a0b86_o.png' alt='facebook's profile update' /><img src='http://static.flickr.com/95/269195862_b7f6217c3d_o.png' alt='wordpress' />[1]</p><p>这是经典做法,也是最安全的做法,一切都必须经过服务器的确认。但是碰到网速等因素困扰时,尤其是互联网的洪荒年代,绝大部分人都是通过一根理想值是 14.4k 的电话线上网,等待服务器的反馈结果,慢得往往让人崩溃。</p><p>于是 JavaScript 应运而生,客户端能做一些最简单的检查,再也不用等服务器的处理。比如:</p><p><img src='http://static.flickr.com/113/269265052_610eb36a67_o.png' alt='taobao' /></p><p>JavaScript 还有一个更恐怖的提示形式:</p><p><img src='http://static.flickr.com/107/269193914_453fba5d5d_o.png' alt='google_alert' /></p><p>这种方式可以说臭名昭著,原因在于,弹出这么个对话框时,操作系统(你知道我指 Windows)会发出震耳欲聋气势如虹的声音,令用户感觉「超不爽」。其实,或许能改稍微改善这个问题,这种方式的提示还是有其可取之处的。没有任何<strong>页面上的提示</strong>能比弹出的对话框更让人注意,而且,这个对话框有「冻结操作」的功能,在某些重大场合下,失误的操作会让用户损失惨重。上面的 Gmail 例子,是发生在放弃草稿邮件(可能已经信心苦苦打了很多字)的「重大情况下」,这么做是合适合理的。</p><p>当然,我们可以在页面内模仿这种形式:</p><p><img src='http://static.flickr.com/110/269195863_8cee129159_o.png' alt='zhanzuo' /></p><p>但是,你知道,我一直都强调的,<strong>没有 JavaScript 怎么办</strong>?题外话了,程序设计应该考虑到,没有 JavaScript 情况下,服务器端应该能够正常处理提交结果,不要轻易信任客户端返回的数据而不加检查。</p><p>当然,在'Web 2.0'时代,我们也少不了提示,Gmail 是这样干的:</p><p><img src='http://static.flickr.com/96/269193910_4a720a233b_o.png' alt='gmail_action2' /></p><p>我们知道,「不刷新」是'Web 2.0'的重大技术特征。我们的操作,页面不需重新生成,提示信息可以自动出现在当前页面的某个位置。某个位置?我前面强调过,网站必须在浏览器等客户端内执行,浏览器可以有滚动条。于是,我们的问题来了:</p><p><img src='http://static.flickr.com/122/269344437_6a478b5575_o.png' alt='gmail_action' /></p><p>这个截屏跟上一个是同一操作。假如我把滚动条刚好拉到这里,我的操作将看不到任何反馈结果。'Web 2.0'的「不刷新」让我以为什么都没有发生过,首先我会怀疑我的操作有误,继续尝试……假如我看不到上面的提示,我只能放弃。</p><p>当然,这不是「不刷新」才有的问题。看看豆瓣的一个添加书评的操作。我在上面做了操作,刷新了,返回的还是这个页面,根据经验,这是不成功的状况。但问题在哪?发生了什么事情?</p><p><img src='http://static.flickr.com/121/269193907_43a0a79856_o.png' alt='db1' /></p><p>我努力寻找,我错在哪啦?知道把滚动条往下拖,才发现:</p><p><img src='http://static.flickr.com/100/269193908_899d478f41_o.png' alt='db2' /></p><p>My God...把以说中文为荣的我惊呼夷语。</p><p>由于笔者小学语文没学好,在找不到这篇文章的中心思想后决定分析一下豆瓣为什么会这么做。</p><p>根据心理学的什么原理来着,用户的注意力往往会发生她/他所操作的地方。比方说,我按下了一个按钮,若有任何问题,我希望反馈就在这个按钮旁边发生。比如:</p><p><img src='http://static.flickr.com/97/269195861_fc6a74c531_o.png' alt='google_analyst' /></p><p>这是再正常不过的交互与交互反馈了了。但是,我们可能会忽略掉一点,网页的特殊性让我们的操作区域有可能会在刷新后消失于我们的浏览器的<strong>可视范围</strong>。这种情况下,懒惰,「短暂失忆」的用户不一定会拉动滚动条来找刚才的操作区域,进而找反馈结果。豆瓣是不是忽略了这一点?其实「真正的答案」是:人家豆瓣才不至于像我一样吃饱了没事干去分析什么鸟理论…… XD</p><p>在这种情况下,豆瓣这样的反馈信息是不是该向我们第一个例子中的 Facebook 和 WordPress 学习,反馈信息直接放在标题下或者能在第一屏的某个地方不是很明显么?</p><p>分析这么多,我终于有了一个在写之前没想好的结论:<strong>反馈信息应该放在能够让用户感觉得到,最直接了当的地方;在某阶段交互结束后,用户应该能够没有任何操作就能获取反馈信息。</strong></p>%06%A8l%A2%07Z%81#%E9%A7e.=%CD%8D0网站的反馈提示2006-10-15T10:00:00+08:00https://realazy.comRealazy<p>我经历的一个项目倒下了。倒不是因为技术落后,反而是因为技术太过先进——整站,所有前端页面都是 Ajax 处理而产生的。</p><p>我们现来看看 Ajax 是啥(汗,你以为读者是白痴咩~),根据发明人 <a href='http://jjg.net/'>Jesse James Garrett (jjg)</a> 在 <a href='http://www.adaptivepath.com'>adaptive path</a> 的一篇文章 <a href='http://www.adaptivepath.com/publications/essays/archives/000385.php'>Ajax: A New Approach to Web Applications</a> 中定义,Ajax 由几种蓬勃发展的技术以新的强大方式组合而成。Ajax 包含:</p><ul><li>基于 XHTML 和 CSS 标准的表示;</li><li>使用 Document Object Model 进行动态显示和交互;</li><li>使用 XMLHttpRequest 与服务器进行异步通信;</li><li>使用 JavaScript 绑定一切。</li></ul><p>可以看到,这是一种以 JavaScript 为核心的技术,从广义上说,除了服务器端技术,剩下的都是 Web 标准。</p><p>但上一句话并不是重点,做 Web 标准的读者不用窃喜 XD。</p><p>我们来了解一下 Jesse James Garrett 的背景,他著有一本有名的书,叫做 <a href='http://www.jjg.net/elements/'>The Elements of User Experience - User-Centered Design for the Web</a>. 啥?<strong>User Experience</strong>? 没错,jjg 是一位资深用户体验专家。</p><p>我没有深入调查过,但我身边的人,接触并运用 Ajax 的清一色都是程序员。我对程序员当然没有什么成见,但术业有专攻,程序员看到这个 Ajax 定义,理所当然是从技术上入手,为新技术滥用 Ajax 不亦乐乎无法自拔而不自知,为 Ajax 而 Ajax,忽略了非技术层面的东西,比如,<strong>用户体验</strong>和<strong>亲和力</strong>(accessiblity)。</p><p>所以我认为 jjg 对于他自己发明的 Ajax 的定义是不及格的。实际上,按照 jjg 本来的意思,技术也是为用户体验服务的。<a href='http://www.dearbook.com.cn/book/viewbook.aspx?pno=TS0029148'>About Face 2</a> 说,Web 的出现让交互设计至少倒退十年,Ajax 的出现实际上为了弥补这十年而生,让 Web 界面操作可以一样或至少接近「正常软件」的操作方式。但 jjg 的定义对技术层面的东西阐述过了头,缺乏用户体验背景的程序员只能依瓢画葫芦,为 Ajax 而 Ajax。</p><p>所以我觉得,哪里该用 Ajax,怎么使用 Ajax,如何才能做到符合用户体验的需求,这是产品/策划的事情。事实是很悲哀的,产品认为这属于技术问题(至少我身边所碰到的情况都这样)而不加过问。我们可以看到很多大举 2.0 旗帜的网站,<strong>看起来很酷,用起来很苦</strong>。漫长的等待没有提示,用户未知情的情况下刷新内容,鼠标一不小心就会犯错,不能后退(这倒是普遍问题),不能撤销等等,很多用户患上了<strong>2.0 恐惧症</strong>。</p><p>由于我实际上对用户体验并没有研究,不好夸大其词,不好妄加评论,上面只是我的个人想法,有错的话各位看官一定要批评指正,一起学习,共同进步。我着重谈谈亲和力的问题。</p><p>众所周知,JavaScript 不是什么时候都能用。老旧的浏览器,高安全的环境(比如银行,或者用户设置),文本浏览器(如 lynx, w3m),屏幕阅读器,手机浏览器等等,很多场景都限制了 JS 的发挥与使用。对于正常人,你可以觉得无所谓,反正现在换个或者升级浏览器也是轻而易举的事情,但是,很多障碍人士,却只有很少的选择。<strong>Web 2.0 并不意味不能用</strong>。</p><p>所以我们必须采取适度降级(graceful degradation)的策略。对于一些非必须由 Ajax 驱动产生内容的前端页面(比如分页,提示内容的展示),我们就可以采取此策略。我们必须保证,在没有 JS 的情况下,这些内容可以使用传统(Web 1.0?)的方式来交互产生。这是保证 Web 具备亲和力的基础。有此保证后,我们就可以采取逐步增强(progressive enhancement)的策略来加入 Ajax,使用 JS 来控制前端的交互,达到用户体验的目的。实际上,读者的疑问可能就在这,如何能够保证适度降级的基础上能逐步增强?实践证明,Unobtrusive Javascript(不冒昧的 Javascript? 天,这该怎么翻译?)的思想能够帮助我们做到。何谓 Unobtrusive?那就是说,在没有 JS 的情况下,网页内容依然能够访问(accessible)。概念很抽象,那么我们举个例子说明吧。假如网页有几块内容:</p><pre class='html'><code class='html'><a href="#a">Show a</a> <a href="#b">Show b</a> <a href="#c">Show c</a><div id="a">a</div><div id="b">b</div><div id="c">c</div></code></pre><p>我们需要按照用户的点击展示每一块内容。通常的做法是,我们会给 <code>a</code> 标签加上占位符,然后写上 <code>onclick=""</code> 之类的代码,在某些 <code>div</code> 加上 <code>style="display: none;"</code>,让其在默认情况下不可见,然后再让 JS 操纵其可见性。这是一种不好的,非 Unobtrusive 的做法。</p><p>Unobtrusive 的做法是,我们需要让 <code>a</code> 有意义,而不是只为 JS 而存在。<code>a</code> 应该依照实际情况指向一个实际连接,或者跳到本页的一块内容。我们应该讲 JS 完全分离出来,不要在 HTML 混杂 <code>onclick</code> 之类的事件代码。而且,我们也不应该在默认情况下隐藏内容,应该在页面载入后交给 JS 来控制,这样,用户在没有或禁止掉 JS 的时候,她/他还有机会能够看到所需的内容。这是一个最简单的 Unobtrusive 例子。</p><p>所以,结论是,我们要建设具备亲和力的 Web 2.0 的网站,graceful degradation, progressive enhancement, Unobtrusive 是基础。当然,某些完全依靠 Ajax 的网站,比如 Google Maps,得另外讨论。我们可以看到,即使是 Gmail,它也有一个非 Ajax 版本备用。当然,我的意思不是每个网站都应该开发、维护两个以上的版本,我们应该,在保证亲和力的基础上,才逐步 Ajax 化,而且 Ajax 应该是用在构建一种新的交互方式,用在用户体验上。</p>%F8%F5X/%8F%E8%EC%ED%13%91%3EL%208%81%D0Ajax, 用该所用2006-09-24T10:00:00+08:00https://realazy.comRealazy<p>我不是一个程序员,虽然书是买了一大堆,但从来没有系统学习过编程。大学四年级的时候想过一下计算机三级,所以就买了 K&R 啃了一下,最后只把 <code>if ...else</code> 学到手,至今连指针是啥都不懂。但这并不能磨灭我的兴趣,呵呵。</p><p>我之前考虑过 PHP, Python, Perl, Bash 等等语言的学习,但都无疾而终,为自己汗一把……我不知道现在喜欢上的 Ruby 会不会是这样的结果,但终于能从中找到一些乐趣了,而且不像以前那些想学的语言那么费脑筋,短短几句语句就能做到很多事情,真的很爽。</p><p>得从 <a href='http://www.rubyonrails.org/'>Ruby on Rails</a> 说起。它是 <a href='http://www.37signals.com/'>37signals</a> 公司的一个雇员,2005 年最热门的黑客 <a href='http://www.loudthinking.com/'>DHH</a> 在 2004 年创建的,据称其「敏捷开发」模式撼动了 Java 世界,并吸引不少 Java 牛人。这些对我来说意义不大。我买了一本<a href='http://www.china-pub.com/computers/common/info.asp?id=30058'>《应用 Rails 进行敏捷 Web 开发》</a>,并把书中的例子做了一遍,发现真的太爽了,几乎不能叫编程,只能叫「把问题描述清楚」。还有它的模板系统(rhtml),跟 php 的 smarty 有点类似,但简单多了,对我来说,真是为所欲为呢……</p><p>RoR 的网站是基于 Web 标准建立的,还有最近上线的 <a href='http://www.ruby-lang.org/en/'>Ruby 官方网</a> 也是,我敢打赌世界上没有哪门程序语言的网站能做得有 Ruby 的漂亮,呵呵。虽然这不是重点,但无疑大大地增加了我的好感,让我感觉它是最前卫的语言……</p><p><strong>最前卫</strong>?的确如此。想想看,一个数组,不用遍历,直接拿过来用(<code>puts array</code>),而且排序简单(<code>array.sort</code>),还可以做加减(<code>array1 - array2</code>,array1 中就会 pop 掉 array2 中存在的元素), 还有 chainable(<code>puts gets.reverse.capitalize</code>)(呵呵,这也是我用 jQuery 的原因之一),这只是我刚学 ruby 就能体会的东西,更多精彩的还在后头。<strong>一切都激动人心</strong>。</p><p>所以我打算抽点时间好好学习一下 Ruby,或许让它成为我的「程序母语」也未尝不可,然后再去玩玩 Ruby on Rails。所以今天开了一个 Ruby 类别,好好学习,诸位看官,你们要多多监督啊~</p><p>最后,奉上今天的习作,用 ruby 搞了个 playlist。看看,代码是不是很酷?</p><pre class='ruby'><code class='ruby'>File.open 'playlist.m3u', 'w' do |f| f.write Dir['/home/realazy/Music/*.{mp3,MP3}'].join("\\n")end</code></pre>%93%D7%0F%F5Q9m%3CH%B5%1A%FF1%C30%9A爱上 Ruby2006-09-15T10:00:00+08:00https://realazy.comRealazy<p>1864 年 4 月 21 日,美国一位叫做 William Sellers 的家伙提议了一个螺丝钉的标准,当时是没有啥反响的,即使美国铁路局在 1883 年时采纳了该标准,世界依然我行我素。二战时,美国发现它们带去的螺丝钉根本不能修理英国坦克,因为标准不一样。二战后,从头把交椅退下的英国采纳了美国老大的螺丝钉标准,这让 William Sellers 的螺丝钉标准迅速在整个欧洲扩展开来,也为国际标准酝酿了土壤。</p><p>我们身边大约有 800,000 个标准。全世界总共有 200 多个组织负责制定国际间的标准。其中,超过 96%的已经发布的标准,都来自 3 个设在日内瓦的机构,它们分别是「国际标准化组织」、「国际电气标准会议」和「国际电信联盟」。今天,我们生活在一个标准的世界里,但,你可能不用在意什么标准,认为世界本该如此。</p><p>什么是标准?维基百科的<a href='https://secure.wikimedia.org/wikipedia/zh/wiki/%E6%A0%87%E5%87%86'>解释</a>是:</p><blockquote><p>就是一种以文件形式发布的统一协定,其中包含可以用来为某一范围内的活动及其结果制定规则、导则或特性定义的技术规范或者其他精确准则,其目的是确保材料、产品、过程和服务能够符合需要。一般而言,标准文件的制定都经过协商过程,并经一个公认机构批准。</p></blockquote><p>有一天,你从路边摊(也不一定,王府井地铁也有人摆)买了一片盗版 DVD,回家,推进播放机欣赏的时候,你肯定没有想到,靠,我这片 DVD 还是有标准的呢(倒,谁会去想啊)……就是因为标准,才不至于你把这片 DVD 借给你的亲戚的表弟的大姨妈的时候,她家的播放器播放不了。</p><p>所以,你能看到标准有啥好处了:标准有利于交流,有利于互用,标准让生活更美好。</p><p>但我们不在意。是的,你在生活中在意过什么狗屁标准吗?如果有,那也是美女帅哥的标准……</p><p><strong>所以你在意什么 Web 标准干嘛?</strong>你老板都不在意……</p><p>对,因为他不知道什么 DVD 标准,同样,也不知道什么 Web 标准,他不用知道。他要求 DVD 能播放,也要求网站必须:良好的信息架构(Information Architecture),使用性(Usability),用户体验(User Experience),设计(Design),品牌(Branding)以及用户目标(User Goals)。</p><p>伙计,知道了吗?干好你的活,世界上只有你自己在意你的宝贝 Web 标准。</p><p>我们为什么还在意它?因为 Web 标准并不完美,世界上没有比 Web 标准更烂的标准了。浏览实现的异同,CSS 远比理想中的差劲,缺乏的 HTML 标签,紧紧绑在一块的结构和表现等等等……</p><p>没有人在意标准,所以 Web 标准最高境界是:<strong>一个标准制作的网站,让你压根感觉不到跟标准有关</strong>。</p><p>所以,从今天起,各位,不要再沉浸于各种 hack 中了,好好思考一下你老板真正在意的东西,怎么用 Web 标准来更好地实现它们:<strong>信息架构、使用性、用户体验、设计、品牌及目标</strong>。</p><p><strong>更新:</strong>看完这篇文章后请务必阅读一下子乌的回复(已失链)。</p>:q%0C%A3%C6%3CF%11c%CE%0E%CF%C9%F1%88%91谁在意什么标准2006-09-09T10:00:00+08:00https://realazy.comRealazy<p>HTML 诞生于学术机构,最初的动机很单纯,只是提供一个人人可学人人可用的一套标记或者创建文档的方法。最初的 HTML 很简单,足于应付常规的学术文档的编排(当然,它是不能像 TeX 一样排出漂亮的数学公式的),而且那个时代的浏览器基本都是文本浏览器,能区分标题、段落,一切都是那么美好。没错,它是标记(markup)语言。</p><p>然而「万恶的」商业终于介入了。光荣(过去式)网景和荣耀的(进行时)微软开始大战了,把裁判 W3C 扔在一旁,荒唐的时候,W3C 还要跟着他们的步伐走,HTML 3.2 就是一个短命的笑话(未考证)。为了一己之私(竞争力?),商业浏览器加入许多非 W3C 的私有的有时候连它老 mu 都不认识的标签,属性等,而且大部分都是表现性的,世界一片混乱。</p><p>在混乱中,平面设计师开始转向 Web 设计,WYSIWYG 工具使得他们不需任何思考,甚至连 HTML 是否存在都不知道。完美再现设计,WYSIWYG 工具不知道要浪费多少垃圾代码才能做到。这个时候,HTML 成了表现性语言,没有任何或者很少存在标记的意义了,严重违背了 HTML 的初衷。</p><p>这样的害处何在?很明显,HTML 干了它不该干的事情。人类的智慧远胜于机器的智慧,看到一篇网页,我们可以从表现上看到它要表达什么(除非设计不要我们看懂),机器却是没有眼睛的,它看到的是表现背后的一堆源代码,它需要从里面找到源代码所表达的意义,而源代码表达的意义<strong>应该跟它的表现展现给人类的意义相同</strong>。</p><p>机器读不懂表现性的代码。比如,<code>font size=7</code> 与一个 <code>h1</code>,机器只知道后一个是这篇网页的标题。</p><p>标题?对,老师没告诉过你写作文要写标题吗?老师在讲你有没有在听?有没有有没有……没有嘛~</p><p>哦,是我不好,我应该提前说明我的观点:<strong>写 HTML 就像写好一片文章,基本功在于合理地组织内容,大标题,二级、三级、四级标题,分好段落,列表,画数据表格等等</strong>。整个 HTML 规范就是要教你怎么从结构上写好文章,要不怎么能叫标记语言(markup language)呢。世界上本来不存在 Web 标准(web standards)之说,但人们已经忘掉了 HTML 本来的意义,某些组织(<a href='http://webstandards.org/'>Web Standards Project</a>)不得不用某些看起来很重量级的词汇(Web Standards)来呼吁设计者/开发者重新认识 HTML。</p><p>关搜索引擎优化什么事?事关重大。搜索引擎如何工作?搜索引擎每天派出蜘蛛(spider. 对,它们都是机器)到处游荡,试图把世界上存在的,它能进去的网页都扒出来,然后交给数据库索引,搜索引擎就可以根据人们的需要捡出数据。在一定程度上,蜘蛛的优劣也决定了搜索引擎的优劣。但无论如何优秀的蜘蛛,它们都喜欢 Web 标准,就像你的老师,她/他也喜欢格式良好的作文。如果您的 HTML 里面充斥了表现性的内容,到处都是 <code>table</code>,本来该属于段落或者列表的内容适用使用 <code>br</code> 粗暴地打断,那么,蜘蛛就会很吃力地去试图理解这篇网页到底要表达什么,前面说到,它不是人类,它仅能通过某种固有的被人类定义好的方式去行事,它是瞎子,它看不到网页上的表现,这对它毫无意义,它只希望能从里面找到某些结构,让它知道这篇网页所要传达的意义。如果你不是很喜欢你的老师,你可以写一篇没有标点符号的作文给你老师看,让她/他感受一下这些机器才能感受到的痛苦……</p><p>可见,Web 标准本身就是要我们「写好文章」,但似乎没有多少人真正注意这个,每天沉浸在各种 CSS 奇技淫巧中不能自拔(呵呵,有点离题)。HTML 标签并不多,但已经基本够用。在「下笔」之前,我们是不是该先考虑用该标签是不是能准确传达本来所要表达的意思,还有没有更好的来取代它,或者说用它压根就是一种错误?人类可以无所谓表现后面的源代码,但机器不可无所谓,你做的事情,不仅要让人类看懂,也要能让机器看懂。</p><p>机器看得懂,那么,你的 SEO 已经成功了一半。另一半在哪?兄弟,交钱去吧 XD ……</p><p>推荐阅读:SEO 基础,十分有用。</p>%1B9L%9C%D1%9A&!#3%F6%A4%90%CB%E8%15「写好文章」,SEO 成功一半2006-09-05T10:00:00+08:00https://realazy.comRealazy<p>写个小教程,以期能让大家对 <a href='http://jquery.com'>jQuery</a>有所了解,甚至喜欢上它(请不要怪我)。</p><p>不废话,先说明我们的目的。我们知道,当代浏览器(modern browsers)的文本框的聚焦(focus)样式可以通过 CSS 的伪类 <code>:focus</code> 来设置。假设我们有这么一段代码:</p><pre class='html'><code class='html'><form> <dl> <dt>Name:</dt> <dt></dt> <dd><input type="text" /></dd> <dt>Password:</dt> <dt></dt> <dd><input type="password" /></dd> <dt>Textarea:</dt> <dt></dt> <dd><textarea></textarea></dd> </dl></form></code></pre><p>则我们这样的 CSS 就可以搞定 focus 样式:</p><pre class='css'><code class='css'>input[type="text"]:focus,input[type="password"]:focus,textarea:focus { border: 1px solid #f00; background: #fcc;}</code></pre><p>简单不过,对不?你可以拿任何一款当代浏览器来测试(Firefox, Safari, IE7): <a href='http://realazy.com/lab/jquery/tut/form_hover_step1.html'>http://realazy.com/lab/jquery/tut/form_hover_step1.html</a>。IE6? 呵呵,这就是本篇教程的目的所在,没错,既然 IE 不支持 <code>:focus</code>,我们只能 <a href='http://domscripting.com/presentations/atmedia2006/slides/'>Using DOM Scripting to Plug the Holes in CSS</a>,不过我们用 jQuery 来实现。</p><p>先来看看 jQuery 的工作方式。jQuery 使用美元符号<strong><code>$</code></strong>来返回一个 jQuery 对象,然后我们就可以使用 jQuery 提供的 API(接口。很多很多很实用,赶紧参考 <a href='http://visualjquery.com/index.xml'>Visual JQuery</a>)进行操作了。</p><p>我们都不懂程序,对,我假设你跟我这样,只是了解一些最基本的语法(对不起大虾了,高手不要自扁身份)。既然我们不懂,我们就用 CSS 的方式来思维。</p><p>首先我们要从 DOM 中获得 <code>type="text"</code>, <code>type="password"</code> 的 <code>input</code> 和 <code>textarea</code>。对,我们伟大的美金出场了,哦,是美元符号。参考 <a href='http://proj.jquery.com/docs/Base/Expression/CSS/'>http://proj.jquery.com/docs/Base/Expression/CSS/</a>,我们知道,我们可以这样选择到他们:</p><pre class='js'><code class='js'>$("input[@type='text'], input[@type='password'], textarea");</code></pre><p>选中它们以后呢?我们就要靠事件(event)来处理了。<code>:focus</code> 对应的的事件是 <code>onfocus</code>,然而 jQuery 讨厌 on,于是就是 <code>focus</code> 了,多好 :) (参考 <a href='http://proj.jquery.com/docs/EventModule/'>http://proj.jquery.com/docs/EventModule/</a>)。于是我们知道我们该这么做:</p><pre class='js'><code class='js'>$("input[@type='text'], input[@type='password'], textarea").focus();</code></pre><p>嘿嘿,我们已经迈出一大步了!我们要继续告诉 <code>focus</code> 该做些什么。在 jQuery 中,我们通常需要一些匿名函数来帮我们干些事情,不理解不打紧,让我们继续:</p><pre class='js'><code class='js'>$( "input[@type='text'], input[@type='password'], textarea").focus(function () {});</code></pre><p>我们的目的是什么?对,是给聚焦的文本框加上样式。jQuery 有一个 <code>css (prop)</code> 的 API,参考前面的 CSS,我们可以这么写:</p><pre class='js'><code class='js'>css({ background: "#fcc", border: "1px solid #f00" });</code></pre><p>bingo! 离成功仅一步之遥。我假设你知道,返回对象自身使用 <code>this</code>。在 jQuery 中,返回自身当然也是 this, 但是,需要返回的对象还是 jQuery 对象,我们还必须使用美元符号。所以是 <code>$(this)</code>。那么:</p><pre class='js'><code class='js'>$("input[@type='text'], input[@type='password'], textarea").focus(function () { $(this).css({ background: "#fcc", border: "1px solid #f00" });});</code></pre><p>That's it! 然后我们该怎么执行这段代码呢? 我们知道有一个 <code>body onload=""</code> 可以用,也知道有一个 <code>window.onload</code> 可以用,但 jQuery 提供了一个更佳的方案,让我们可以进一步分离结构与交互。</p><pre class='js'><code class='js'>$(document).ready(function () { // Your code here...});</code></pre><p>所以我们只需将我们的代码放到里面去:</p><pre class='js'><code class='js'>$(document).ready(function () { $("input[@type='text'], input[@type='password'], textarea").focus( function () { $(this).css({ background: "#fcc", border: "1px solid #f00" }); } );});</code></pre><p>呵呵……貌似成功了。等等,我们要送佛送到西天,好人做到底……在上面的交互中,只有聚焦的情况,那么失焦的时候呢?嗯,我们不要想当然,失焦?那么聚焦的样式就自动清楚清除了嘛~不会的,除非我们明确告诉它。依瓢画葫芦:</p><pre class='js'><code class='js'>$(this).blur(function () { $(this).css({ background: "transparent", border: "1px solid #ccc" });});</code></pre><p>嗯,说到 jQuery 的强大之处了,jQuery 对象可以接受无数个函数回调/消息/方法(哪一种是正确说法,请高手指教),也就是传说中的<strong>Chainability</strong>。也就是说我们不必要分两行写,一气呵成:</p><pre class='js'><code class='js'>$(document).ready(function () { $("input[@type='text'], input[@type='password'], textarea") .focus(function () { $(this).css({ background: "#fcc", border: "1px solid #f00" }); }) .blur(function () { $(this).css({ background: "transparent", border: "1px solid #ccc" }); });});</code></pre><p>啊呵,又一次貌似完成……又等一等,我们只需要针对 IE 啊,其他浏览器都可以使用 CSS 来实现,我们何苦要用 JS 来降低它们的处理效率呢,所以,我们用了 jQuery 自带的定义:</p><pre class='js'><code class='js'>$(document).ready(function () { if ($.browser.msie) { $("input[@type='text'], input[@type='password'], textarea") .focus(function () { $(this).css({ background: "#fcc", border: "1px solid #f00" }); }) .blur(function () { $(this).css({ background: "transparent", border: "1px solid #ccc" }); }); }});</code></pre><p>耶!我们真的完成了!嗯哪,要判断浏览器版本?似乎 jQuery 没有提供,但可以看看这个 jQuery 插件(plugin):<a href='http://davecardwell.co.uk/geekery/javascript/jquery/jqbrowser/'>jQBrowser</a>. 嗯嗯,似乎忘了跟大家说,jQuery 还拥有许多超强的插件!有时间我整理几个出来奉献给大家。</p><p>对,看看我们这一步的成果:<a href='http://realazy.com/lab/jquery/tut/form_hover_step2.html'>http://realazy.com/lab/jquery/tut/form_hover_step2.html</a>,一定记得用 IE6 查看。</p><p>貌似又完成了一次(汗,前边不是说我们真的完成了么),不!大家看得爽的时候还记得我是做什么的吗?对对对,是<strong>Web 标准</strong>!Web 标准提倡什么?结构,表现,交互相分离啊,我们把样式写到了 JS 里边,相信这不是好事情。难不倒我们 jQuery 的!我们换种思路,我们把样式在一个 <code>class</code> 里定义好,在 <code>focus</code> 的时候加上的这个 <code>class</code>,<code>blur</code> 的时候去掉这个 <code>class</code> 不就 OK 了吗?聪明……jQuery 相应的 API 是 <code>addClass</code> 和 <code>removeClass</code>。过程不累赘了,否则篇幅又得增加一半(我还要睡觉,明天还要上班,可怜的上班族),代码如下:</p><pre class='js'><code class='js'>$(document).ready(function () { if ($.browser.msie) { $("input[@type='text'], input[@type='password'], textarea") .focus(function () { $(this).addClass("ie_focus"); }) .blur(function () { $(this).removeClass("ie_focus"); }); }});</code></pre><p>我必须承认,我把这个 <code>class</code> 命名为 <code>ie_focus</code> 是有点变态。嗯,进一步,虽然我们这个代码不大,但我们也分离出来一个独立文件吧。这是我们的最后步骤的演示:<a href='http://realazy.com/lab/jquery/tut/form_hover_step3.html'>http://realazy.com/lab/jquery/tut/form_hover_step3.html</a>,记得看源码哦。</p><p>很简单,对不?jQuery 的威力不止于此,还有需多更酷更强的功能留待,你,和,我,共同探索。</p><p>P.S. jQuery 的代码也很优美,对不?这种函数式编程风格我个人是十分喜欢的,但是大括号,小括号敲到我手疼……所有我又深深地爱上了基本上看不到括号的 <a href='http://www.ruby-lang.org/'>Ruby</a>,<strong>Orz</strong>...阿门,晚安……N</p>%C9%14%94)a%B7%06%F8G8%F5%60%E9%7Ch%A4小试牛刀——一篇 jQuery 小教程2006-08-31T10:00:00+08:00https://realazy.comRealazy<p><a href='http://jquery.com/blog/2006/08/26/jquery-10/'>jQuery 1.0 已经于美国时间 2006 年 8 月 26 日发布</a>。这是一个好消息,世界上又多了一个超级优秀的 JavaScript Framework。</p><p>我本身不是 JavaScript 行家,只懂些编程中最基本的东西,甚至连门都没入。但我却能从 <a href='http://jquery.com/'>jQuery -- New Wave JavaScript</a> 做很多事情,而我相信,如果使用最原始的 JavaScript 编写方法,我还得费上至少一年时间来学习,才能达到相同的效果。</p><p>这当然不是我喜欢它的主要理由。它的 CSS 选择器和 XPath 操作器让我最兴奋。写 JavaScript,最基本的就是对 DOM 的选择与操作。JavaScript 自然提供有大量的 API 供使用,但是要精确地选择某个(些/类)元素还是相当苦难,最简单的例子,需要选择 class,我们没有现成的 getElementsByClassName 可用,要达到目的,<a href='http://www.dustindiaz.com/top-ten-javascript/'>甚至连正则表达式都用上</a>,而 jQuery 只需 <code>$(".class")</code>,像 CSS 中选择元素一样对 DOM 进行选择。所以,假如你也是一个 CSS 写手,你会迅速明白这些 <code>$("div.class")</code>, <code>$("#id + .class")</code>, <code>$(".class > a")</code> 是啥意思。而 <code>$(".class/../p")</code> 这种最基本的 XPath 的东西也不难懂,总之,DOM 选择就用 CSS 选择器的思维来思考即可。这对我来说,实在太方便了。</p><p>而操作,jQuery 提供的 API 也十分浅显易懂,我们不用管它背后用什么实现。比如 <code>$("p.surprise").addClass("ohmy").show("slow");</code>,表示,给 <code>class</code> 为 <code>.surprise</code> 的 <code>p</code> 元素赋予 <code>.ohmy</code> 的 <code>class</code>,然后显示(show)出来。我相信,即使第一次见到的人都知道它在干嘛。实际上,我就是第一次见到它就深深爱上了 jQuery。</p><p>从上句中也得知,实际上 jQuery 对象的操作是可以 Chainable(咋翻译好呢),你可以对一个 jQuery 对象传送不止一个信息(函数),这实在也太方便了。</p><p>还有一点,基本上,jQuery 的代码都简洁易读,个人看法,这要归功于函数式编程(functional programing)方式,我认为这是一种强大的,优雅的编程方式,所以我更喜欢。</p><p>当然,我说过,我并不会编程,我说的东西可能都很肤浅,但它确实让我从中找到编程的乐趣,Thank you, <a href='http://ejohn.org/'>John</a>!</p><p><strong>Update:</strong>来自 <a href='http://www.eweek.com/article2/0,1895,2010602,00.asp'>jQuery Eases JavaScript, AJAX Development</a> 的一段话,个人深表赞同:</p><blockquote><p>jQuery is "not a huge, bloated framework promising the best in AJAX---nor is just a set of needlessly complex enhancements---<strong>jQuery is designed to change the way that you write JavaScript.</strong>"</p></blockquote>%EB%AA%91%B8%C7%92?%E56%98%19%1A%08%CD%E5-jQuery——JavaScript 冲击波2006-08-27T10:00:00+08:00https://realazy.comRealazy<h2 id="Mozilla-Gecko">Mozilla 家族/Gecko</h2><p>当上个世纪浏览器大战接近尾声,微软的 IE 实际上已经取胜,Netscape 宣布它的浏览器开放源代码并成立了 Mozilla 组织。Mozilla 组织放弃了所有 Netscape Navigator 的大部分代码,重写了引擎。这是一项艰苦的过程,Netscape 没有能够在最快时间内发布它的新版浏览器,以致 IE 一家独大好多年。而被 AOL 收购后的 Netscape,虽然在 Mozilla 的基础上发布过 6.x, 7.x, 8.x 的浏览器。但是除了忠实粉丝,加上 Mozilla 组织主推 Firefox,没有人再认识它们。</p><p>Mozilla 是一个大套件,包括浏览器,邮件客户端,IRC 聊天,甚至可视化网页编辑器。庞杂的体系吓坏了仅仅需要浏览器的用户。为了各司其职,Mozilla 组织在保留 Mozilla Suite 的基础上,衍生了浏览器 Firefox,邮件客户端 Thunderbird,而网页编辑器也有了个 NVU。它们基于同一渲染引擎 Gecko。基于这个引擎的浏览器还有 linux 下的 Epiphany, Galleon,Mac 下的 Camino 等。 习惯上把它们称为 Mozilla 家族(Mozilla Family)。</p><p>因为这一章涉及到的内容比较少(它们基本上是完美的,没有什么臭虫可以让我增加字数),我更倾向于谈谈历史,莫怪:) 。Firefox 在名字还叫 Firebird, Phoenix 的时候就已经引起了 CSS 设计师们的注意。Gecko 引擎跟 W3C 的规范很靠近,还有许多 CSS3 的新特性,莫不让设计师们兴奋。但依旧有些小小怪癖,但是能够马上得到修正,试想想那个 5 年都没有变化的 Trident……</p><p>我宣布,目前版本的 Gecko 在 CSS 方面没有能够影响到设计的臭虫……</p><h2 id="Safari">Safari</h2><p>2003 年,Mac OS 10.3(Pather)发布以后,Safari 成了默认的浏览器。它的引擎叫做 WebCore,基于开源项目 KDE 下 Konquerer 的 KHTML 引擎。2005 年随着 Mac OS X 10.4(Tiger)的发布,Safari 2.0 成为第一个通过 WaSP Acid2 测试的浏览器(正式发行版)。尽管 1.x 有许多臭虫和怪癖,但是,还是那句话,非 IE 用户都乐意或者不受约束地升级他们的浏览器器,以下只给出已知的 Safari 2.x 的臭虫和解决方案。Safari 2.x 的臭虫及解决方案一览表臭虫解决方案</p><p>对 <code>fieldset</code> 设置 <code>display: inline;</code> 会让表单不可点击。</p><p>相对定位 <code>fieldset</code>:</p><pre class='css'><code class='css'>fieldset { display: inline; position: relative;}</code></pre><p>使用 <code>:hover</code> 伪类的相邻选择器导致错误的行为:</p><pre class='css'><code class='css'>dt:hover + dd { color: green;}</code></pre><p>目前无解决方案</p><p>放大字体时,产生的内容会使文本超出其盒子范围。</p><p>目前无解决方案</p><h2 id="Opera">Opera</h2><p>历史上,Opera 也有一些 CSS 怪癖,有可能是想模仿 IE 行为导致的。但新版表现十分好,桌面版自从 2005 年 9 年 8.5 发布以来完全免费(去除广告),而且移动版大举进军手机市场,所以也是市场的一支生力军。开发 Opera 本身就有不少 W3C 规范的撰写人,技术专家,所以,Opera 对标准的支持绝对让你满意。</p><p>Opera 9,最近发布的版本,使用了称为了 Presto 的引擎,也通过了 Acid2 的测试。</p><p>我宣布,目前版本(9.0+)的 Opera 在 CSS 方面没有能够影响到设计的臭虫……</p><h2 id="">手中无剑,心中也无剑</h2><p>同理,Hack 的最高境界就是没有 Hack。我们这系列的文章,前两部分都在谈 IE。我们来看一张有趣的图:</p><p><img src='http://static.flickr.com/63/206286819_fc25dea57f_o.jpg' alt='time breakdown of moder web design' /></p><p>可以发现,当代 Web 设计,问题绝大部分时间花在 IE 身上。我们身边有一大堆更好的浏览器,为它们设计也是一件轻松的事情,那么,可不可以把 IE 独立出来?对,区别对待浏览器,并让「补丁」式的 CSS 只对劣质浏览器可见。劣质?没错,你知道我指<strong>IE5+/Win</strong>。这样,我们可以专心于设计,实现,并在后期集中精力解决 IE 问题。而且,这样我们就没有必要使用 hack(当然 trick 还是要的)了等可能会导致混乱等乱七八糟的东西了……什么,那你前面的两个部分写出来干什么的?</p><p>我们当然可以使用 JavaScript 或者其他技术来识别浏览器,但是我们有更安全的方法。首先我们来看看 <a href='http://www.thinkvitamin.com/'>Vitamin</a> 的源代码,可以发现这一段:</p><pre class='html'><code class='html'><!--[if lt IE 7]> <link type="text/css" media="all" rel="stylesheet" href="/css/main_IE.css" /> <script type="text/javascript" src="/scripts/pngfix.js"></script><![endif]--></code></pre><p>注释中的东西只有小于版本 7 的 IE 采取执行,其他浏览器都忽略。感谢 MS,再次向太平洋方向鞠个躬……需要更详细了解条件注释,请查看 <a href='http://msdn.microsoft.com/workshop/author/dhtml/overview/ccomment_ovw.asp'>msdn 的相关文档</a>。</p><p>甚至,如果我们都不在意 IE 6 以及以下版本浏览器,我们都不要搞那么多 hacks 或者 tricks,直接用 <a href='http://dean.edwards.name/IE7/'>/IE7/</a>(这可不是 IE7,是一个 js)算了。虽然效率有点问题,呵呵,就让 IE 咎由自取吧……不过基本上是不可能的,尤其在中国的环境下,大家都把 IE 照顾好吧……</p><p>那么,我们这系列就结束了。我希望 IE7 能尽快出来并普及,尽管不完美,但足可以让生活更舒心了。Web 标准本身并不复杂,只是当前 IE6 等浏览器对 CSS 支持的缺乏和误解,需要 hack 的东西太多,让初学者误认为很高深……我希望,将来无须再 hack,无须再 trick,做页面无须再分工,每个网页设计师都能够亲自操刀,嗯,我也可以失业了…… XD</p>N%A9R%0B%EAL%04m%7C%BBj%3E3%D1%DC7与臭虫为友——浏览器补救办法,臭虫以及解决方案(第三部分)2006-08-20T10:00:00+08:00https://realazy.comRealazy<h2 id="IE-6">IE 6</h2><p>这个仅运行在 Windows 平台上的浏览器对许多 CSS 设计者/开发者来说简直就是毒药。自 2001 年发布以来,它的 Trident 引擎和 CSS 解析器没有升级过。跟 Windows 平台上的 IE 5.x(首次发布于 1999 年)相比,最大的差别在于引进 Doctype 开关并在「标准模式」(Standards Mode)下修正了大量 CSS1 的臭虫。</p><p>因为它的引擎自首次发布以来都没有升级过,所以关于它的臭虫和解决方案的文档都基本完善了,您可以从 <a href='http://www.positioniseverything.net/explorer.html'>http://www.positioniseverything.net/explorer.html</a> 找到更详细的信息。</p><p>只对 IE 5+显示所需样式,可以使用 <a href='http://www.info.com.ph/~etan/w3pantheon/style/starhtmlbug.html'>Tan Hack</a>,或者,也可以称之为 * html Hack。</p><pre class='css'><code class='css'>div { color: green;}* html div { /* IE5+将会使用它 */ color: red;}</code></pre><p>在(X)HTML 中,<code>html</code> 是根元素(即老大,它上面没有父元素了)。Tank Hack 实际上是要选择一个元素(在这个例子中,是 <code>div</code>),它属于 <code>html</code> 的后代,而这个 <code>html</code> 又是任何元素(您所看到的型号 *)的后代。在理论上,这是不可以的,所以解析正确的浏览器都会忽略 <code>* html</code>,但是似乎 IE 5+还有实现了某些在 <code>html</code> 的父元素(是什么我们不得而知),从而讽刺地,意外地让我们可以解决很多难题,感谢 Bill,大家面向太平洋方向鞠一躬……</p><p>至于对 IE 5+隐藏样式,那真是太简单了,使用 CSS2 中的子选择符,相邻选择符,属性选择符等,IE 都不可识别(CSS 规范中也有说明,对于不可识别的都忽略,IE 也有严格遵循规范的时候),从而忽略整条规则。比如:</p><pre class='css'><code class='css'>body > #content { ...;}div + #content { ...;}div[id="content"] { ...;}</code></pre><p>但是我并不建议您这么做,因为您要承担浏览器升级的风险。IE7 已经支持这些「先进」的选择符,所以我不建议对过时的非兼容浏览器使用「先进」选择符来做修补工作。</p><p>当我们使用 CSS hack 和 filter 的时候,如果可以,仅对老式/过时/废弃的浏览器使用,不要对当前版本的浏览器使用,以免升级时失效。另外,我也希望使用 hack 和 filter 能够尽量合法(valid),能通过 CSS 校验器的检查。所以对于星号 */下划线 _ 加属性(property,也叫性质),还有在属性和值之间添加空注释的非法 hack,我不在这里提了,我也不建议你去查。针对这个问题,我建议大家可以看看 <a href='http://csszengarden.com'>CSSZenGarden</a> 创始人 <a href='http://www.mezzoblue.com/'>Dave Shea</a> 的 <a href='http://www.thinkvitamin.com/features/css/stop-css-hacking'>Stop Hacking, or be Stopped</a>。</p><p>那么,我们来列一下 IE5+/Windows 主要臭虫及解决方案,我希望在使用这个方案的时候,能考虑一下前面提到的 <a href='http://www.mezzoblue.com/'>Dave Shea</a> 和我的想法。</p><table width='96%' summary='IE5+/Win 的臭虫及解决方案'> <caption>IE5+/Win 的臭虫及解决方案一览表</caption> <thead> <tr> <th>臭虫</th> <th>解决方案</th> </tr> </thead> <tbody> <tr> <td> <p>在一个液态的(liquid)的盒内,跟在一个浮动的元素后的内容会莫名其妙消失(只有 IE6会发生)。学名<a href='http://web.archive.org/web/20100218144305/http://www.positioniseverything.net/explorer/peekaboo.html'>Peek-a-boo Bug</a>.</p> </td> <td> <p>给该盒定义<code>height: 1%;</code>(但要注意对 IE 5.x/Mac 隐藏)。</p> <pre><code>/*\\*/ * html div { height: 1%; }/**/</code></pre> <p>这个就是有名的 Holly Hack(救世 hack?神圣 hack?)。它通过给一个块设置一个十分小的高度值(1%几乎成了通用准则)来工作。但 IE 5+/Win 能够依据内容来扩展这个盒子到足够的高度,就是说,把<code>height</code>当作<code>min-height</code>来用。在大部分情况下,Holly Hack 能够使 IE5+/Win 按照实质的行为来表现。</p> </td> </tr> <tr> <td> <p>在列表元素(<code>dl</code>, <code>ul</code>, <code>ol</code>),定义在相对或浮动定位的块级元素的背景会消失。学名<a href='http://web.archive.org/web/20100218144305/http://www.positioniseverything.net/explorer/ie-listbug.html'>Disappearing List-Background Bug</a></p> </td> <td> <p>给列表元素定义相对定位(但同样注意对 IE5.x/Mac 隐藏,因为它没有这个虫虫——相同名字,表现却咋这么不同呢?)</p><pre><code>/*\\*/ * html ul { position: relative;}/**/</code></pre> <p>尽管不是一个很有技术含量的 hack,但使用<code>position: relative;</code>能够让你从 IE5+/Win 的大部分困境中解脱出来。同时很多场合下你也不愿意使用,因为它产生的块(block)或许是你不需要的,所以还是省着点用吧。</p> </td> </tr> <tr> <td> <p>浮动元素内某些连接 hover 时,该元素的底部会被砍掉。学名<a href='http://web.archive.org/web/20100218144305/http://www.positioniseverything.net/explorer/guillotine.html'>Guillotine Bug</a></p> </td> <td> <p>对包含元素使用 Holly Hack.</p> </td> </tr> <tr> <td> <p>一个相对定位元素内的绝对定位元素,其内容超出页面底部时不会触发滚动条。学名<a href='http://web.archive.org/web/20100218144305/http://www.positioniseverything.net/explorer/unscrollable.html'>Unscrollable Content Bug</a>.</p> </td> <td> <p>对包含元素使用 Holly Hack.</p> </td> </tr> <tr> <td> <p>当多个浮动的元素彼此跟随,中间加注释的时候,最后一个浮动元素内的文本偶尔会复制到最下面去。学名<a href='http://web.archive.org/web/20100218144305/http://www.positioniseverything.net/explorer/dup-characters.html'>Duplicate Characters Bug</a>.</p> </td> <td> <p>不要给浮动元素设置多宽度,使其不会到达包含元素的底部,或者对最后一个元素设置<code>margin-right: -3px;</code>或者更小。</p> </td> </tr> <tr> <td> <p>浮动元素相同浮动方向上的边界是所设置值的两倍。</p><pre><code>div { float: left; margin-left: 100px;}</code></pre> </td> <td> <p>为浮动元素设置<code>display: inline;</code>。注意:根据<a href='http://web.archive.org/web/20100218144305/http://www.w3.org/TR/CSS21/visuren.html#floats'>W3C 的建议</a>,除非值是<code>none</code>,否则不应该在浮动元素上使用<code>display</code>。</p> </td> </tr> <tr> <td> <p>一个块级元素内的文本跟浮动元素之间有一个3像素的间隔。学名<a href='http://web.archive.org/web/20100218144305/http://www.positioniseverything.net/explorer/threepxtest.html'>Three Pixel Text-Jog Bug</a>.</p> </td> <td> <p>对块级元素使用 Holly Hack.</p> </td> </tr> <tr> <td> <p>内容中包含未达到底部的浮动元素,清除(clear)浮动的块级元素的<code>padding-top</code>会加倍。</p> </td> <td> <p>给该清除浮动的元素使用 Holly Hack.</p> </td> </tr> <tr> <td> <p>相对定位容器内连接的<code>background-image</code>失效。</p> </td> <td> <p>给连接相对定位。</p> </td> </tr> <tr> <td> <p>一个有<code>padding</code>的盒子内嵌套一个有<code>margin</code>的盒子,外围盒子的<code>padding-top</code>和里面盒子的<code>margin-top</code>不会相加。</p> </td> <td> <p>里面盒子的<code>margin-top</code>设置双倍值。</p> </td> </tr> <tr> <td> <p>以下划线开头的 class 和 id 会被忽略。</p> </td> <td> <p>防止以下划线开头命名 class 和 id.</p> </td> </tr> <tr> <td> <p>一个绝对定位元素的<code>left</code>值依据容器内第一个元素的左边缘计算,而不是容器本身。</p> </td> <td> <p>根据实际情况调整<code>left</code>的值或者绝对定位容器。</p> </td> </tr> <tr> <td> <p>为<code>table</code>设置 margin 会被忽略(IE6忽略所有,IE5.x/Win 只忽略<code>margin-top</code>和<code>margin-bottom</code>)。</p> </td> <td> <p>给<code>table</code>外包一个<code>div</code>,然后对该<code>div</code>设置<code>margin</code>.</p> </td> </tr> </tbody></table><p>你恨 IE6 不?它可以占据我这么多版面独成一部分……你恨 IE 不?前两部分都在写它们……实际上我们的第三部分会非常少,精华都在这一部分了。</p>d%D5%80%02%03%1B%9B%CF%FFSG%88X%016%0F与臭虫为友——浏览器补救办法,臭虫以及解决方案(第二部分)2006-07-31T10:00:00+08:00https://realazy.comRealazy<p>如果世界是美好的,浏览器都没有缺点,W3C 的规范清晰明了,而这篇文章也永远不会出现。Welcome to the real world. 在这篇文章中,我们将会探讨几个主流浏览器的在 Web 标准(或者 CSS 规范)下存在的典型问题/臭虫,提供相应的解决方案(hacks/filters)。</p><h2 id="NN-4-x">NN 4.x</h2><p>很幸运我们生活在这个时代。这个 10 年前的古董浏览器,网景导航家(Netscape Navigator)4.x,终于只剩下 0.3%的市场份额,大部分网站也声明不再支持。它有着最基本的 CSS 支持,而且依然有不少死忠(我相信越来越少),所以依然有设计师为它设计样式。但最多只能改变下字体,颜色等最基本的东西了。所以,我是说假如,你还要顾及 NN 4.x 的话,比较合适的做法是,做一个基本样式给它用,并对其隐藏不可识别的高级样式(给其他浏览器用)。很容易做到,因为 NN 4.x 不认 <code>@import</code>。举例如下:</p><pre class='html'><code class='html'><link rel="stylesheet" tyle="text/css" href="basic.css" /><style type="text/css"> @import (advance.css);</style></code></pre><p>这个方法屡试不爽。但请注意,这也不是没有副作用。假如只使用了 <code>@import</code> 而没有 <code>link</code> 的情况下,可能会导致 IE 的瞬间无样式(FOUC,详见 <a href='http://www.bluerobot.com/web/css/fouc.asp'>www.bluerobot.com/web/css/fouc.asp</a>。幸好,NN 4.x 只能识别 <code>screen</code> 这个媒体类型(media type),所以,避免 FOUC 也不是没有办法:</p><pre class='html'><code class='html'><link rel="stylesheet" tyle="text/css" media="screen, projection" href="advance.css"/></code></pre><p>到此为止,我们不要为它浪费过多精力,除非你的老板还在用它。</p><h2 id="IE-5-x-Win">IE 5.x/Win</h2><p>在此我们指 Windows 平台上的 IE 5.0 和 IE 5.5。CSS 的支持依然很糟糕,但是比起 NN 4.x 已经有了长足的改变。它们臭名昭著错误的盒状模型(Box model)可能是导致 CSS 界第一个 hack 的出现。我们先来看看盒状模型。W3C 规范的盒子,可以使用「相加」来描述,即,一个元素的实际盒子宽度是由内容宽度(content <code>width</code>),边框(<code>border</code>),边距(<code>padding</code>)堆积起来的。而 IE 5.x/Win 则可以用「相减」来描述,也被称为边框盒状模型(border box model),一个元素的实际宽度就是该元素的 <code>width</code> 设值,边框,边距都从中减去。</p><p>来看一个例子:</p><pre class='css'><code class='css'>div { width: 200px; margin: 20px; padding: 20px; border: 5px;}</code></pre><p>依照 W3C 规范,这个 <code>div</code> 实际所占宽度是 5px + 20px + 200px + 20px + 5px。而对 IE5.x/Win 的边框盒状模型来说,这个 <code>div</code> 实际宽度就是 200px,而内容宽度被压迫到只有 150px:200px - 5px - 20px - 20px - 5px。这时候,传说中的牛人 <a href='http://tantek.com/'>Tantek Çelik</a>(负责 IE5.x/Mac 的前微软员工,现在经营 <a href='http://technorati.com/'>Technorati</a>,<a href='http://microformats.org/'>Microformats</a> 创始人和贡献者之一)出现了,带来了 Box Model Hack. 该 hack 使用了 IE 5.x/Win 不支持的 <code>voice-family</code>,并在值中设置一些 CSS 转义引号(CSS-escape quotes) ,欺骗 IE 5.x/Win 认为规则块(declaration block)已经闭合。</p><pre class='css'><code class='css'>div { /*为了更好说明,width 调了一下写作习惯*/ margin: 20px; padding: 20px; border: 5px; width: 240px; /* 1. IE 5.x/Win 需要的宽度 */ voice-family: '"}"'; /* 2. IE 5.x/Win 看见了},认为规则已经结束了 */ voice-family: inherit; /* 3. 能够正确解析的浏览器重置该值 */ width: 200px; /* 4. 这才是我们需要的真正宽度 */}</code></pre><p>这条 hack 会导致同时期的 Opera 一些小问题,但新版已经没有任何问题了。考虑到非 IE 用户都乐意升级他们的浏览器,所以这个不是问题,不用多加考虑。</p><p>Tantek Çelik 后来还发明了更佳的方式来对待 IE 5.x/Win,叫做滤器(filter)。这跟对 NN 4.x 隐藏样式有类似之处,只不过这是针对其他浏览器隐藏,IE 5.x/Win 自己可识别。不过这许多额外的 CSS 文件。Tantek Çelik 称之为 Band Pass Filter.</p><p>IE 5.0x/Win 的 filter:</p><pre class='css'><code class='css'>@media tty { i { content: '";/*' "*/}}; @import 'ie50winbandpass.css'; {;}/*"; }} /* */</code></pre><p>IE 5.5x/Win 的 filter:</p><pre class='css'><code class='css'>@media tty { i { content: '";/*' "*/}}@m; @import 'ie55winbandpass.css'; /*"; }} /* */</code></pre><p>这样,就可以将针对 IE 5.0x/Win 的 CSS 写到 ie50winbandpass.css 中了,同理,IE 5.5x/Win 的则是 ie55winbandpass.css。需要了解工作原理等更多东西,可以访问 <a href='http://tantek.com/CSS/Examples/'>http://tantek.com/CSS/Examples/</a>。</p><p>还有许多同样会在 IE6 中出现的问题,我们将在 IE6 部分中讨论。另外,如果您已经没有老机器或者老系统来跑这两个浏览器,可以到 <a href='http://browsers.evolt.org/?ie/32bit/standalone'>http://browsers.evolt.org/?ie/32bit/standalone</a> 中下载绿色版。</p><p>那么,我们要来讨论另外一个常被我们忽略的 IE 5.x 浏览器了,没错,Mac OS 上的 IE。</p><h2 id="IE-5-x-Mac">IE 5.x/Mac</h2><p>同时期,IE5.x/Mac(前面提到的 Tantek Çelik 领头开发的)是最先进的浏览器,它的 Tasman 引擎支持 CSS 1, HTML 4.01, PNG 1.0,同时还支持在 2000 年看来十分先进的子选择器(child seletor)和相邻选择器(sibling selector)。就是说,它也有本身的毛病。</p><p>IE 5.x/Mac 的问题主要体现在定位元素,浮动,不必要的滚动条,过宽的元素,不正确的覆盖,奇怪的边界等的处理上。见附表。</p><p>对 IE 5.x/Mac 隐藏 CSS,最有名的 hack 叫做注释反斜杆 hack(<a href='http://www.sam-i-am.com/work/sandbox/css/mac_ie5_hack.html'>Commented Backslash Hack</a>):</p><pre class='css'><code class='css'>#isnotMacIE5 { display: none;}#isMacIE5 { display: block; background-color: #060; color: #fff;}/* commented backslash hack v2 \*/#isnotMacIE5 { display: block; background-color: #060; color: #fff;}#isMacIE5 { display: none;}/* end hack */</code></pre><p>IE 5.x/Mac 碰到了反斜杆(\),就将 *号转义(escape),从而直到碰到下一条注释才认为注释结束,把一整段都当作注释对待,达到隐藏目的。</p><p>当然,还有 hack 能够让你的样式(表)只对 IE 5.x/Mac<strong>显示</strong>。由 Tantek Çelik 开发,<a href='http://stopdesign.com/'>Douglas Bowman</a> <a href='http://www.stopdesign.com/examples/ie5mac-bpf/'>文档</a> 的 <a href='http://tantek.com/log/2004/07.html#ie5macbandpass'>IE5/Mac Band Pass Filter</a>:</p><pre class='css'><code class='css'>/\*\\\*//\*/@import "ie5mac.css";/\*\*/</code></pre><p>这反转了 Commented Backslash Hack,对 IE 5.x/Mac 显示了 ie5mac.css,而其他浏览器会视而不见。如果您需要为 IE 5.x/Mac 设计,IE5/Mac Band Pass Filter 将是最有用的工具。IE5.x/Mac 的臭虫及解决方案一览表</p><table summary='IE5.x/Mac 的臭虫及解决方案'> <caption>IE5.x/Mac 的臭虫及解决方案一览表</caption> <thead> <tr> <th>臭虫</th> <th>解决方案</th> </tr> </thead> <tbody> <tr> <td> <p>绝对定位到屏幕边缘右/下的元素导致垂直/水平滚动条。</p><pre><code>div { position: absolute; right: 0; bottom: 0;}</code></pre> </td> <td> <p>取消被隐藏的15px 边界。</p><pre><code>div { right: 15px; margin-right: -15px; bottom: 15px; margin-bottom: -15px;}</code></pre> </td> </tr> <tr> <td> <p>快捷(shorthand)边界不会居中一个表格。</p><pre><code>table { margin: 0 auto;}</code></pre> </td> <td> <p>使用完整(longhand)性质来设置边界。</p><pre><code>table { margin-left: auto; margin-right: auto;}</code></pre> </td> </tr> <tr> <td> <p>赋予<code>background-image</code>的元素背景图片总是被边框覆盖。</p> </td> <td> <p>使用另外的元素来设置边框,如果要求边框必须在<code>background-image</code>的外围。</p> </td> </tr> <tr> <td> <p>使用<code>overflow: auto</code>会导致页面扩展到能够适应元素的内容,就算不溢出,还是会产生滚动条。</p> </td> <td> <p>总是设置需要<code>overflow: auto</code>的宽度和高度。</p> </td> </tr> <tr> <td> <p>元素的<code>clear</code>会继承设置了<code>clear</code>值的父级元素的值,就算该元素定义了<code>clear: none</code>也无效。</p> </td> <td> <p>目前尚未解决方案</p> </td> </tr> </tbody></table><p>预告:第二部分讨论 IE6,第三部分讨论基于 Gecko 的浏览器(Mozilla Suite, Firefox, Camino 等),Safari 和 Opera。</p>E%87(%D3%E4%D5%FD%E8g%CF%9CO%F2%D3r%DD与臭虫为友——浏览器补救办法,臭虫以及解决方案(第一部分)2006-07-29T10:00:00+08:00https://realazy.comRealazy<p>我的书,乱糟糟的丢得到处都是,包括厕所。今晚抽空整理了一下,幸好还有十几本重量级的书外借,否则就算我这张"4 人床"一行也难放下。</p><p>有没有发现,O'Reilly 的书挺多的,外借的书基本上都是 O'Reilly 的。 XD</p><p><img src='http://static.flickr.com/75/195521074_102a6b4ae7.jpg' alt='mybook' /></p>O%A6%9A%C0JVE%E0h%5B%F1%E8%E9%15%87%05整理了一下我的书2006-07-23T10:00:00+08:00https://realazy.comRealazy<p>使用 web standards 做网页,经常需要定义某个父元素下的第一个子(child)元素或最后一个元素,以便将其与其他元素区分开来,有利于实现某些特殊需求。最浅显的例子是,导航项目间的竖线,我们往往通过 <code>border</code> 或者 <code>background</code> 来实现。特殊需求是,第一项左边无竖线或最后一项右边无竖线。</p><p>那么区分第一项好呢,还是最后一项好?答案是明显的,逼不得已,不要用区别最后一项。按照一般的编程方法,<strong>控制第一项要比控制最后一项容易得多</strong>。</p><p>区分第一项的还有一个好处是,CSS 有一个 <code>:first-child</code> 的伪元素(pseudo element)可以让我们轻而易举的选择第一个子元素。</p><p>遗憾的是,当前全球占有率最高的浏览器,IE6,并不支持这个伪元素。我们可以手工给第一个元素加上 class 然后再定义它。但这不是一个万无一失的办法,尤其对于页面规模达到一定数量大型网站,很容易挂一漏万。那么,让 JavaScript 来帮我们完成这件工作如何?</p><p>查看 <a href='http://realazy.com/lab/first-child-js/'>http://realazy.com/lab/first-child-js/</a>。</p><p>在 IE 中,为每个属于 first-child 的 <code>li</code> 加上 <code>first-child</code> 的 class,则 CSS 则可以这样书写了:<code>:first-child, .first-child { some rules here...}</code>(有没有发现这样写很帅,一个是冒号,两点;一个是点号,一点 :)),十分便于维护和管理。</p><p>p.s.希望哪位高手能够写出通用的 first-child 来,我这里的例子只针对 <code>li</code>,能力有限 XD ...</p>%ED,%03x4%AB%D6%BF%A2%01gW%E4%FD3$在 IE 中使用 first-child2006-07-06T10:00:00+08:00https://realazy.comRealazy<p>很多朋友对我说,她/他有代码洁癖,即,让她/他写 XHTML 的话,从来不愿意加上额外的标签(tag)。举个简单的例子,相信很多人从很多地方都看到过的:</p><pre class='html'><code class='html'><div id="nav"> <ul> <li></li> <li></li> …… </ul></div></code></pre><p>很多人,包括许多业界大牛,都建议你这样写即可:</p><pre class='html'><code class='html'><ul id="nav"> <li></li> <li></li> ……</ul></code></pre><p>当然,我个人很欣赏第二种写法,没错,简洁明了,语义(semantic)确凿。但请等一等,如果需要样式化(stylish)它,哪一种可以提供更多的控制(controll)? 很明显,第一种。</p><p>然后,这个问题就有点让人抓狂了。一句话:你是结构(markup)优先呢,还是表现(presentation)优先?我相信,在如今这个不美好的时代,表现优先是第一准则。很多有理想的人,包括我,最后为了实现表现上的需要,标签汤(tag soup)实际上难以避免。</p><p>所以,这只能是个<strong>度</strong>的问题。<strong>别滥用</strong>。怎么不算滥用,也没有什么准则。我个人的准则是:如果要实现一个表现上的需要,你使用超过三层的外围标签(wrappers?),就应该停下来仔细想想了。尽管有点老,但我还是建议你看一看 <a href='http://www.simplebits.com/bits/simplequiz/'>SimpleQuiz</a> 上面一些有趣的讨论。</p><p>为什么会这样?因为一切都不完美。试想一下,如果 CSS 能够提供更多的规则来控制页面上的元素,或许就不会这么尴尬。比方说,background-image 支持 trlb(上右下左)四个方向不同的图片的话,我们就不必为处理圆角而绞尽脑汁;支持从页面上产生元素,如 content 的话,那么也可以大大减少 tag 的使用……</p><p>XHTML?笑话。实际上目前为止没有多少人在用 XHTML,一切都是自欺欺人。<a href='http://www.autisticcuckoo.net/archive.php?id=2005/03/14/xhtml-is-dead'>XHTML is Dead</a>! XHTML 是<strong>xml</strong>,具有<strong>xml</strong>的一切优越性,但是,我们现在看到的,都是<strong>text</strong>。如果把 text 当作 xml 来处理,这是有害的(<a href='http://www.hixie.ch/advocacy/xhtml'>Sending XHTML as text/html Considered Harmful</a>)。</p><p>尽管我们在 Doctype 上都标明了我们用的是 XHTML,但是实际上我们都在用 HTML。这是现实。要不那些错误百出的非良构的页面怎么可能在宽容的当代浏览器中显示呢……也难怪,XHTML 1 只是 HTML 4 的改良而已。但是,未来的 XHTML 2 并不向后兼容,我不知道我们使用 XHTML 1 的必要性何在。另外,别拿 accessibility 来反驳我,分离结构与表现的 HTML 4 并没有跟 XHTML 1 有任何区别。</p><p>因此,可能,使用 XHTML 1 的意义在于,宣称我们已经有了这样的思想,并且为未来的 XHTML 2 做好了准备。</p><p>这也是我为什么,强烈建议使用 HTML 4.01 Strict Doctype 的原因。从公司/企业来说,要求整个团队都具有 web standards 的思想,并贯彻相关原则并非易事,各种上个世纪遗留的思想仍然负隅顽抗。如果真的使用 XHTML 1,很多只能兼容 html 的 JavaScript 脚本会失效,编辑某个不经意的未转义的字符会导致整个页面出错(xml parsing error),等等。为了避免问题,或许,HTML 4.01 Strict Doctype 是现在的最佳选择。</p>%D4%11%5C%0B-%99%0E%9B%F7BN%EA%A5%E2%00%07杂感2006-06-25T10:00:00+08:00https://realazy.comRealazy<p>截字是一个很恶心的问题。为了照顾表现上的需要,通常需要把过长的句子,比如一个列表中的新闻标题,截短。</p><p>通常这由程序员使用后台技术(各种流行的语言,PHP, JSP 等)或者前台技术(JavaScript)来处理。我个人倾向于使用前台技术,因为这对提高网页的亲和力(accessibility)有好处。在非桌面型的浏览器中,用户可以更方便的掌握信息完整性。</p><p><a href='http://www.w3.org/TR/2003/CR-css3-text-20030514/#text-overflow'>`text-overflow`</a> 是 CSS3 的一个性质(property),它可以截短过长的字符串,并依据值(value)来决定被截掉部分使用何种方式展现。目前,IE 已经实现对其的支持,Opera 也有了私有属性(-o-text-overflow)对其支持,Firefox 似乎落后一步……支持的两者都可使用 <code>ellipsis </code> 值,把截掉部分替代成省略号(即 ellipsis)。</p><p>所以,你要做的只是,把需要截字的元素定好宽度,和 <code>overflow</code> 一起使用,即可实现效果。</p><p>但是 Firefox 不支持,所以大概你知道我下面要干什么了。 :)</p><p>先可以看看 Yahoo 某大牛的解决方案:<a href='http://blog.360.yahoo.com/blog-ktYYK_s5fqJ2Hu1ryv2QSL0-?p=120'>http://blog.360.yahoo.com/blog-ktYYK_s5fqJ2Hu1ryv2QSL0-?p=120</a>。大牛就是大牛,XBL 和 JavaScript 双管齐下。</p><p>然后,我的解决方案是,利用伪类 <code>:after</code> 增加省略号,再把它定位到右边即可。效果有点粗糙,但简洁明了,纯 CSS 实现。</p><p>具体过程不说了,看<a href='http://realazy.com/lab/ellipsis/'>案例</a>,有兴趣者看看源码,很容易理解的。</p>%D0bz3%EC%13%AE%0E%E6%E6q=%81%E6*u使用 CSS 截字2006-06-24T10:00:00+08:00https://realazy.comRealazy<p>目前有一堆 Doctype 可供我们选择:</p><h2 id="HTML">HTML</h2><ul><li><strong>HTML 4.0 Strict</strong>: <code><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"></code></li><li><strong>HTML 4.0 Transitional</strong>: <code><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"></code></li><li><strong>HTML 4.0 Frameset</strong>: <code><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" "http://www.w3.org/TR/html4/frameset.dtd"></code></li><li><strong>HTML 3.2</strong>: <code><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"></code></li><li><strong>HTML 2.0</strong>: <code><!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"></code></li></ul><h2 id="XHTML">XHTML</h2><ul><li><strong>XHTML 1.0 Strict</strong>: <code><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"></code></li><li><strong>XHTML 1.0 Transitional</strong>: <code><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"></code></li><li><strong>XHTML 1.0 Frameset</strong>: <code><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"></code></li><li><strong>XHTML 1.1</strong>: <code><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"></code></li></ul><p>说明一下,XHTML 1 就是 HTML 4.01 的 XML 化,是一种不向前兼容的格式,未来的 XHTML 2 也不会向后兼容。汗……这是一个很可悲规范。</p><p>那么应该选择哪个?我相信很多人,尤其是接触了网页标准(Web Standards)的人,会毫不犹豫地选择 XHTML 1.0 Transitional。这是一股趋势,这是一股潮流,暗示着,嘿嘿,伙计,看见没,我们的网站使用最新技术构建了……</p><p>这里有一个<a href='http://www.elementary-group-standards.com/archives/site-standards/why-xhtml.html'>很有趣的统计</a>,使用 XHTML 1.0 Strict 和 Transitional 的占绝大多数,且对半开。那么,这个世界就那么美好了吗?</p><p>我们先来看看 <a href='http://www.w3.org/TR/2002/NOTE-xhtml-media-types-20020801/'>W3C 的建议</a>,总结出下表:</p><table class='mime_types' summary='This table summarizes recommendation for media type labeling of HTML and XHTML documents.'> <caption>Media types summary for serving XHTML documents</caption> <thead> <tr> <th>Media Type</th> <th>text/html</th> <th>application/xhtml+xml</th> <th>application/xml</th> <th>text/xml</th> </tr> </thead> <tbody> <tr> <th>HTML 4</th> <td>SHOULD</td> <td>MUST NOT</td> <td>MUST NOT</td> <td>MUST NOT</td> </tr> <tr> <th>XHTML 1.0 (HTML Compatible)</th> <td>MAY</td> <td>SHOULD</td> <td>MAY</td> <td>MAY</td> </tr> <tr> <th>XHTML 1.0 (other)</th> <td>SHOULD NOT</td> <td>SHOULD</td> <td>MAY</td> <td>MAY</td> </tr> <tr> <th>XHTML Basic</th> <td>SHOULD NOT</td> <td>SHOULD</td> <td>MAY</td> <td>MAY</td> </tr> <tr> <th>XHTML 1.1</th> <td>SHOULD NOT</td> <td>SHOULD</td> <td>MAY</td> <td>MAY</td> </tr> <tr> <th>XHTML + MathML</th> <td>SHOULD NOT</td> <td>SHOULD</td> <td>MAY</td> <td>MAY</td> </tr> </tbody></table><p>前面说到,XHTML 1 实际上是 HTML 4.01 的 XML 化。即时看起来,写起来,用起来都很像,但这是两种不同的规范。XHTML 的 MIME-TYPE 应该是 <code>application/xhtml+xml</code>,而 HTML 应该是 <code>text/html</code>。两者有何不同,简单的说,<code>application/xhtml+xml</code> 属于 XML,所以行为遵循一切 XML 规范,最重要的是良构(Well-Formed)这一点。</p><p>除了 XHTML 1.0 Transitional 和 Frameset,我们可以看到,XHTML 1.0 Strict 和 1.1 都<strong>SHOULD</strong>使用 <code>application/xhtml+xml</code>。然而,我们可以看到,前面提到的那个统计,并没有多少个网站使用 <code>application/xhtml+xml</code>,可以说他们是扯虎皮做大旗而已(如果您有心查看一下本 blog 的源码,您会发现我也是),它们非良构的内容依然可以通过支持 <code>application/xhtml+xml</code> 的浏览器(如 Firefox, Opera)来展现,因为它们本质上还是 <code>text/html</code>,而 <code>text/html</code> 并不需要良构。</p><p>可以看到,XHTML 1.0 Transitional 的地位很暧昧,它的 <strong>Transitional</strong> 使其 <strong>SHOULD BE</strong> <code>application/xhtml+xml</code>,但也可能 <strong>MAY BE</strong> <code>text/html</code>。也就是说,怎么处理,实际上交由客户端(client side)来决定。在我所见到的桌面浏览器中,它们都毫不犹豫地选择 <code>text/html</code>,这跟它们一贯的<strong>宽容</strong>风格保持一致。</p><p>但是很不幸的是,就有这么一个浏览器,Opera for mobile,从 8.0 开始,凡是声明了 XHTML 的 Doctype,它都一律以 <code>application/xhtml+xml</code> 来处理。所以,你那不良构的网站,只好在它上面显示出错信息。</p><p>所以,推荐使用 XHTML 1.0 Transitional,是鼓励你从 HTML 向 XHTML 转化,且保持过渡性。但过渡性并不代表你就可以不重视 XML 的良构。</p><p>实际上,我们并没有能够发现 XHTML 1.0 Transitional 跟 HTML 4.01 Strict 有何不同,除了一个 <code>target</code>,一些过时的表现标签和属性(attribute)。只要我们在 HTML 中不写作那些 XHTML 中不存在的标签和属性,我们实际上就是在写 XHTML 1.0 Transitional,对我们并没有什么影响,什么 XML 的优越性完全不能在 XHTML 1.0 Transitional 中体现出来,所以与其让 Opera for mobile(或许还有其他变数)不能工作,还不如选择 HTML 4.01 Strict。在这点上,<a href='http://www.456bereastreet.com'>456BereaStreet.com</a> 做出了一个十分不错的选择。当然,如果您能保证你的 XHTML 1.0 Transitional 百分百良构,使用它是一个更佳的选择。然后,请你告诉我,世界上,使用了 XHTML 1.0 Transitional 的网站,<strong>非首页</strong>的其他页面能有多少个是能够通过验证的?</p><p>结论是,如果你没有勇气保证整个站点百分百良构,请使用 HTML 4.01 Strict;如果您有勇气保证整个站点百分百良构,您可以直接选择 XHTML 1.1,像我所做的那样。</p><p><strong>推荐阅读:</strong></p><ul><li><a href='https://www.google.com/search?q=使用正确的MIME类型伺服XHTML&ie=UTF-8&oe=UTF-8'>使用正确的 MIME 类型伺服 XHTML</a></li><li><a href='https://www.google.com/search?q=正确使用XHTML的冒险&ie=UTF-8&oe=UTF-8'>正确使用 XHTML 的冒险</a></li><li><a href='http://www.456bereastreet.com/archive/200606/html_vs_xhtml_on_standards_compliant_websites/'>HTML vs. XHTML on standards compliant websites</a></li></ul>%C8%8E%D9%AB%5B%87%13%E0%F63%05%22%1B%3C8%F1也谈 Doctype2006-06-21T10:00:00+08:00https://realazy.comRealazy<p>目前的浏览器,从 IE 系列(IE5 到即将出来的 IE7)到 Mozilla 家族(mozilla suite, firefox, Camino 等),都提供一个<strong>文字大小</strong>(Text Size)的选项。Mozilla 家族的浏览器可以无限放大/缩小字体(限制只取决于内存 :?),而 IE 系列只支持 5 个级别的选择,IE7 也不例外。</p><p>比较有意思的是,Opera 并没有文字大小的选项,只有一个<strong>Zoom</strong>(缩放)选项。而 IE7 也有一个<strong>Zoom</strong>选项,一开始我以为是 Text Size 的替代,没发现我错了,IE7 是两个功能都提供!</p><p>顾名思义,文字大小只是改变浏览器所呈现的文本字号而已,不会更改页面上的其它元素。而缩放则会改变浏览器主体(body)所呈现出来的元素(是不是所有我也不知道)。这不是一个好消息。现在浏览器所呈现的图像,绝大部分是像素(pixel)格式的,缩放会导致质量的降低。虽然如此,相比只改变文本大小,似乎这种方式给浏览着更好的观感,因为从按比例保持了原站的框架。但看到粗糙的图片我就满身不舒服,不知道诸位看官有何看法,欢迎留言探讨 :)。某种情度上,我只需要让文本放大,以适合我的视力范围,粗糙的图片会降低我的阅读欲。所以 IE7 两种方式都提供,不失为一种调和众口的好策略。</p><p>然而,比较恶心的是,如果定义字体大小使用绝对单位(px, pt 等)的话,IE 的文本大小功能会失效。所以,如果您想照顾一下 IE 用户(想不照顾都不行,90%强的占有率),请务必使用相对单位(em, 百分比等)。但是很少人使用相对单位,因为这会让他们的设计不可控制。遗憾的是这个问题并没有在当前发行的 IE7 中得到解决。</p><p>还有一个问题比较有趣。Mozilla 家族浏览器,版本 7 之前的 IE 系列浏览器,字体的缩放有一个快捷键:按住 CRTL,并滚动鼠标滚轮来改变文本大小。滚轮向前缩小文本,向后放大文本。而 Opera 和 IE7 则反其道而行,缩放方向与前述相反。</p><p>对,Opera 跟 IE7<strong>都把这个功能让给 Zoom</strong>了,对于 Opera 来说还好,因为它没有文字大小这个功能(谁知道未来有没有呢),而 IE7,却把该快捷键<strong>从原来的文本大小让给了缩放</strong>,而至今我也没有找出 IE7 改变文本大小的快捷键。</p><p>很混乱,对不?对于我这样的浏览器使用大户,这种混乱使学习成本大幅提升。</p><p>然后还有一个十分有意思的现象,如果不给 body 强行定宽度,只 margin/padding 留出距离的话,Opera 和 IE7 的 Zoom 是不一样的,Opera 不会改变 body 的宽度,而 IE7 则会。我也不知道谁对谁错。</p><p>最后,我做了一个兼容性还可以的东东,能够保持各种浏览器的观感(主要是 body 的宽度)相似(一致是不可能的)。大家可以帮忙使用各种浏览器测试一下:<a href='http://realazy.com/lab/zoom-ts/'>http://realazy.com/lab/zoom-ts/</a>,尤其是有 Safari 的朋友,谢谢 :)</p><p>P.S. 如前所述,IE7 得使用 Text Size 来观看,Zoom 会加宽 body 的宽度,而 Opera 不会。</p>%5C8%8A%EC6%EC%1F3%0F%F2?%94%D0~SIzoom? or text-size2006-06-19T10:00:00+08:00https://realazy.comRealazy<p>说起网页标准(Web Standards),我一直以来都只关注结构(markup)和表现(presentation),很少涉及行为(behavior),因为这涉及到编程,呵呵,不怕见笑,俺是半路出家了,对编程始终不得法门而入。</p><p>工作以来,接触牛人,耳濡目染,近朱者赤,不知不觉也知道了些结构语法什么的。上周某日无聊至极心血来潮,决定搞搞 JavaScript 看看。嘿嘿,想不到,我胡乱写了一通,竟然达到了我预想的效果!于是我抛下看了将近一个月的色彩管理啊,设计什么的啊,抱起了那本去年 <a href='http://oreilly.com.cn'>O'reilly</a> 送我的<a href='http://oreilly.com.cn/book.php?bn=7-111-11091-9'>《JavaScript 权威指南(第四版)》</a>,一看入迷,那个 DOM,对于手写 XHTML 代码的我来说,太熟悉了……于是上班途中的公交也不忘 K 一下她。</p><p>几年过去了……哦不,几天过去了……</p><p>当前的 IE 并不支持<a href='/posts/2005-08-29-css-attrib-selector.html'>属性选择器</a>,所以对于表单,你得不厌其烦地为每种类型(type)加上相应的 class。最让人不能接受的是,IE 不支持 <code>:focus</code>,所以不得不求助于 JavaScript。</p><p>我做了一个<a href='http://realazy.com/lab/enform/'>表单例子</a>,使用属性选择器 <code>input[type="text"] 等及其伪类 ``:focus</code> 实现我想要的效果,在没有任何 JavaScript 的情况下,Firefox, Opera 工作完美。但是,你知道我要说什么了,对,是 IE,没有丝毫的作用。在我加入额外的 class 的情况下可以为 IE 进行样式化,然而遗憾的是,要支持<strong>focus</strong>行为,CSS 再也无能为力,所以,让 JavaScript 出马吧。</p><p>我的实现思想是这样的。对于表单的每种 <code>type</code> 的 <code>input</code>,使用 JavaScript 为其添加相应的 class,我的例子中,我设置 class 的名字等于其 <code>type</code>,即 <code>type="text"</code> 则其 class 为 <code>class="text"</code>,依此类推。而其 <code>fucos</code> 的效果则增加一个相应的 class 来实现,命名就是 <code>type</code> 加上 focus 这个单词,即 <code>type="text"</code> 则其 <code>fucos</code> 的 class 为 <code>class="textfocus"</code>,依此类推。</p><p>好了,那么,我就用 <code>onfocus</code> 和 <code>onblur</code> 事件(events)来实现了。当然,这只针对 IE,所以在 JavaScript 中判断了一下浏览器。嗯,这就是今天的习作:<a href='http://realazy.com/lab/enform/'>http://realazy.com/lab/enform/</a>(用 IE 看哦)。各位大牛有心的话不妨看看,多指正和优化一下,感激不尽 :)</p><p>继续努力学习中……<a href='http://www.douban.com/people/realazy/'>K 书</a>again...</p>@F%BAV%3C%5C%F0%A5%F7%F3%E7,Mvp%E2JavaScript 习作——优化 IE 的表单交互2006-06-12T10:00:00+08:00https://realazy.comRealazy<p>想离职,从 4 月中旬开始。开始发现这家公司不是很适合我,尽管我对她有感激之情,是她让我顺利地转入这一行,而且是不错的制高点。原因渐不解释。</p><p>自五月购得电脑以来(再次感谢老韩),我不用再跟小弟抢电脑,学习进度大大提升了。Photoshop 已经基本操作熟练,最重要的是,我现在对 JavaScript 产生了浓烈的兴趣,并发现我能够写出些有用的东西了,哈哈,这可是从来没有过的啊,以前说 <a href='/posts/2005-01-30-free-web-developer.html'>学习 PHP</a>,嘿嘿,That is easy say than done...但这次我是真的认真(认真还有假的吗 XD)的了,多掌握几门技能,让自己拥有能够离职的资本。对,我不会避讳说我之所以没有提交辞呈是因为我有生存压力(还欠老韩 2k ||-_-)……</p><p><a href='http://www.douban.com/people/realazy/'>拼命 K 书</a> 中,希望我这次没有半途而废……</p>%20m%A6@o%0E%CE%A2%C1%B6%90%208%03%B9p近况2006-06-11T10:00:00+08:00https://realazy.comRealazy<p>从我的<a href='/posts/2005-07-27-gimping.html'>第一个 GIMP 涂鸦</a>至今,快一年了吧,期间荒废无数时间,还是没有怎么掌握 <a href='http://gimp.org'>GIMP</a> 这一强大开源的图像处理(image manipulation)工具。</p><p>强大?比起 Photoshop 来,当然逊色不少,一个最大的麻烦是,它没有<strong>图层样式</strong>,做起 Web 设计来十分费劲。要想作出相同的效果,必须掌握很多相关的知识,因此,如果你学会了 GIMP,再用 Photoshop 那是如虎添翼锦上添花。</p><p>但我的过程十分曲折……我在公司需要处理设计师的 psd 文档。尽管 gimp 可以打开,但遇到图层样式所做出的效果,gimp 不会处理。这是我近来把工作平台转移到 windows 下的一个重大原因。</p><p>最近我在学习 Photoshop。没错,盗版的,XD...</p><p>今晚兴趣盎然,用了一下半年没有开过的 GIMP,打算练习练习。练习的结果就是这个了:</p><p><img src='http://static.flickr.com/54/164885415_9767056aba_o.jpg' alt='a button done with gimp' /></p><p>做的过程中得出的感想就是前述文字了……如果这个用 Photoshop 来做,十分简单的事情,画圆角形状,使用图层样式调节即可。但是 gimp,得用到通道(chanel)和模糊来获得圆角选区,投影还得使用高斯模糊(gaussian blur)。但这才是本质 :)。所以我决定以后多多关照一下 GIMP,因为那能学到<strong>最本质,最核心</strong>的处理图像的知识。</p><p>这是 gimp 的 <code>xcf</code> 档(已失链),呵呵,有兴趣的下载过来打开看看,多多指教 :)</p>%D8.%83%BB%C4Et%25%FD%07%D0z%02%84%C4%A8GIMP 习作2006-06-11T10:00:00+08:00https://realazy.comRealazy<p><code>abbr</code> 和 <code>acronym</code> 是两个很容易被人忽略的标签(tag)。它们貌似没什么意义,但对网站的亲和力(accessibility)起到很重要的作用。</p><p>这两个标签在 html 4 中已经存在,但目前为止 IE 并没有能够识别 <code>abbr</code>。好消息是,IE7 会支持 <code>abbr</code>。如果您需要让 IE6 也支持 <code>abbr</code>,可以参考:<a href='http://www.sovavsiti.cz/css/abbr.html'>http://www.sovavsiti.cz/css/abbr.html</a>, <a href='http://www.w3cn.org/article/translate/2005/115.html'>这是中文版</a>。</p><p>至于什么时候用 <code>abbr</code>,什么时候用 <code>acronym</code>,这个问题让人头痛。先说说个人看法吧,目前 IE 支持 <code>acronym</code>,如果你不是<strong>基本教义派</strong>,大可不管什么 <code>abbr</code>, <code>acronym</code>, 放心用 <code>acronym</code> 就是。</p><p>如果真的那么较真,你可以 <a href='http://www.google.cn/search?hl=zh-CN&newwindow=1&q=abbr+acronym&btnG=%E6%90%9C%E7%B4%A2&meta='>google 一把</a>,会发现有一大堆区别 <code>abbr</code> 和 <code>acronym</code> 的文章,但是保证你越看越糊涂,因为老外自己也不是那么清楚的。</p><p>今天跟公司同事讨论了一下,我是这么理解的:<strong>名词性语句的缩写,比如 HTML, CSS, UN, HTTP, WWW 等属于 <code>abbr</code>;非名词性语句,比如 FYI ( For Your Information), IMHO ( In My Honest Opinion )则等属于 <code>acronym</code>.</strong> 按照这个理解去衡量,似乎到目前为止我所碰到的缩写词都能够正确理解。</p><p><strong>2006-06-02 Update:</strong>今早又看了看资料,我彻底晕倒了。以上规则似乎是错误的,现在我所理解的规则是:如果缩写后可以按单词拼读,而不是必须一个一个字母念,则属于 <code>acronym</code>, 比如<strong>Radar</strong>:<strong>Ra</strong>dio <strong>d</strong>etecting <strong>a</strong>nd <strong>r</strong>anging。总之,An acronym is a <strong>word</strong>就是。反之,abbr 则是一堆<strong>letters</strong>而已,不是<strong>word</strong>。</p><p><strong>如果您有更好的区分方法,请务必告诉我,谢谢!</strong></p><p>最后要告诉大家的是,如果你真的很费劲的去区别 <code>abbr</code> 和 <code>acronym</code>,那么很不好意思地告诉你,未来的 XHTML 2 只保留了一个:<code>abbr</code>. 估计老外自己也晕了。</p><p><strong>只生一个好!</strong> XD</p>H%DD%13a%8D!l%0C%BE%8FV%FA%FF%83U%1E`abbr` 和 `acronym`2006-06-01T10:00:00+08:00https://realazy.comRealazy<p>需要在内容高度不足的时候把页脚放置在屏幕最下方(footerStick)?如果不想 <code>position: fixed</code> 还有什么办法吗?有的,你可以看看下面这些精彩文章:</p><ul><li><a href='http://my.opera.com/tifa/blog/show.dml/251210'>最小高度 100%,页脚保持在底部的布局方法</a>,来自<a href='http://my.opera.com/tifa/blog/'>踩 IE</a></li><li><a href='http://www.rexsong.com/blog/article.asp?id=308'>自适应高度布局</a>,来自<a href='http://www.rexsong.com/blog/'>一葉の千鳥's Blog</a></li><li><a href='http://solardreamstudios.com/learn/css/footerstick/'>footerStick</a>,来自 <a href='http://solardreamstudios.com/'>solardreamstudios</a></li><li><a href='http://www.themaninblue.com/experiment/footerStickAlt/'>footerStickAlt</a>,来自 <a href='http://www.themaninblue.com/'>The Man in Blue</a></li></ul><p>我个人编写 xhtml 的风格是,对于 <code>div</code>,我是能不加就不加的,可是在这个 footerStick 的问题上,还真没找到完美的解决方案,研究了这么多的 footerStick,没有一个不加额外的 <code>div</code> 的,多少而已。</p><p>我也不能幸免,但我这种方案只需一个额外的 <code>div</code>,而且易于理解。开讲:</p><p>现在组织布局结构的标准一般是这样的:</p><pre class='html'><code class='html'><div id="wrap"> <div id="header"></div> <div id="main"></div> <div id="footer"></div></div></code></pre><p>为了实现 footerStick,我需要在 id 为 <code>#main</code> 的 <code>div</code> 加上一个 <code>#m_ext</code>,如下:</p><pre class='html'><code class='html'><div id="wrap"> <div id="header"></div> <div id="main"><div id="m_ext"></div></div> <div id="footer"></div></div></code></pre><p>首先,为了让 <code>body</code> 内的元素能够实现 100%的高度,需要:</p><pre class='css'><code class='css'>html,body { height: 100%; height: 100%;}</code></pre><p>然后,我们就可以让 <code>#wrap</code> 的高度为 100%了:</p><pre class='css'><code class='css'>#wrap { position: relative; height: 100%;}</code></pre><p>为何要 <code>position: relative;</code>?这是我实现 footerStick 的关键了。首先声明的是,我这个 footerStick 是有前提的:<code>#header</code> 和 <code>#footer</code> 的高度是固定的。在 <code>#wrap</code> 的定位为 <code>relative</code> 后,其内定位为 <code>absolute</code> 的元素就可以脱离文档流而存在。</p><p>其实你可以猜到,如何才能让 footer 在底端?内容不足的 <code>#main</code> 必须有一定高度才能把 <code>#footer</code> 挤开。考虑到各种不同的分辨率和窗口的大小等不同因素,<code>#main</code> 很难有一个精确的高度,那么只能:</p><pre class='css'><code class='css'>#main { min-height: 100%; _height: 100%;}</code></pre><p>嗯,如果你看过上述文章,一定知道什么原因了。IE 6- 不支持 <code>min-height</code>,但 <code>height: 100%</code> 却可以达到相同目的。感谢这个 bug。</p><p>让 <code>#header</code> 脱离文档流:</p><pre class='css'><code class='css'>#header { position: absolute; top: 0; left: 0; width: 100%; height: 96px;}</code></pre><p>由于 CSS 到目前为止并不支持百分比与像素的运算(大家快祈祷吧,CSS3 支持各种不同长度单位的运算),因此此时由 <code>#m_ext</code> 来模拟这个运算:</p><pre class='css'><code class='css'>#m_ext { padding-top: 96px;}</code></pre><p><code>#footer</code> 占据一定高度以后,会超出 100%,因此要用负 margin 值等于其高度:</p><pre class='css'><code class='css'>#footer { height: 64px; margin-top: -64px;}</code></pre><p>为了不影响内容超出一屏时 <code>#footer</code> 会挡住 <code>#main</code> 的内容,需要用 <code>#m_ext</code> 再次模拟运算剪去可能被挡得高度:</p><pre class='css'><code class='css'>#m_ext { padding-top: 96px; padding-bottom: 64px;}</code></pre><p>Perfect! 兼容 IE 5.0+, Firefox 1.5, Opera 8.5(我手头所有的浏览器),麻烦大家用其他浏览器帮忙测试:<a href='http://realazy.com/lab/footerstick/footerstick_with_less_content.html'>http://realazy.com/lab/footerstick/footerstick_with_less_content.html</a>,还有一个内容超出一屏的版本,用以检验超过一屏内容是否正常:<a href='http://realazy.com/lab/footerstick/footerstick_with_more_content.html'>http://realazy.com/lab/footerstick/footerstick_with_more_content.html</a>。</p>%C4G%EE%07%3E%F2%96%D5%15%16%9A6%25u%C9%E1我也来玩 footerStick2006-05-22T10:00:00+08:00https://realazy.comRealazy<p>在热排版年代,熟练的技术工人会全身心地投入排版工作。而现在,随着桌面排版系统的发展,每个人都可以做「排版工人」,排版艺术渐渐衰退了。排版是一门艺术,不能完全交给电脑完成。</p><p>首先认识一下西文字体的发展。老式字体往往有衬线,反映出传统字体设计的特征。老式字体字母往往会有倾斜的轴线,这是镌刻字体的凿子类工具所特有的痕迹。过渡性字体也有衬线,但字母中粗细线条的反差没有老式字体那么明显,而且其轴线也趋于垂直。字体本身看起来更清晰,具有手工绘制的特色。现代字体设计趋于清晰简洁,为迎合现代特征,字体设计被简化,衬线逐渐被去除,线条也不再有粗细之分,干净利落,更具机械绘制的特点。而电子计算机的发展,使所有的字体设计都成为可能,数码(digital)字体中,有的明显继承老式字体风格,也有的体现独特的现代文化气息。下图比较一下四种字体,注意看我在老式字体和过渡字体所标示的轴线。</p><p><img src='/assets/missing.png' alt='' /></p><p>随着各种桌面设计系统的出现,运用不同规格的字体进行设计已不再需要专业培训。在热排版时代对字体处理的专门词汇和技术知识往往被忽略,不幸的是,这些词汇和技术是确保设计成功的最基本训练。能够得到最佳的设计效果,不是由于掌握了最新的流行趋势,而是对字体设计的最基本,最传统的训练。所以让我们来熟悉一些字体排版的术语。</p><p>点的大小和 x 高度。铅字的大小用点(point)作单位。任何字体的点大小都是从字母上升部分顶端到下伸部分底端的距离。而字母 x 顶部到底部的距离被称为 x 高度(x-height),这是视线最集中的部位。影响可读性不仅仅是大小,跟字体的设计也有重要关系。</p><p>加空铅。这个术语可以追溯到字体用金属铸造,然后用打磨好的铅条来分隔每一行字体的年代。因此你也可知,加空铅即是字体每行之间的间隔。</p><p>下图详细描述点、x 高度和加空铅。</p><p><img src='/assets/missing.png' alt='' /></p><p>字母间距和每行长度。虽然电脑会自动清除决定单词中字母的间距,但有经验的设计师会给正文或标题的字母专门设计间距,包括扩大或者压缩每个字母前后的细小空间。关于每行长度,大脑和眼睛在感到疲劳并游离于所阅读的材料之间所能承受的每行长度极限,据测是每行 39 到 45 个字母。</p><p>字体排版的对齐格式。对于世界上的 ltr(left to right,左到右)书写/阅读顺序的语言来说,左对齐也许是最舒服的格式。读者可以沿着左边的垂直的直线方便地找到第一行的开头,而右边的空白也可以让段落显得更自然。右对齐和中间对齐,只能适用于少量的字体,因为每一行不规则的开头会增加阅读的时间、精力和难度。两端对齐适用于要充分利用版面,如报纸,但有时候会让字母间距不均匀。还有一种自由格式,虽然读者需要花更多时间和精力,但确实给设计师提供了无限的可能性。</p><p>混合字体。使用有联系的字体(可以是<strong>一副铅字</strong>)可以帮助避免视觉上的混乱。一次不要混合太多不同的字体,可以考虑改变字体大小,分量或字形,但不要一次改变全部三个方面。老式字体往往能与亚现代(过渡)字体混合,这是因为亚现代字体在设计时融合了老式字体的风格,同时兼备现代字体的简单,这是字体既不相同,又能相互协调。同一份设计中的不同字体应有自己的大小和间距,以便相互之间的融合。</p><p>参考文献:<a href='http://www.amazon.com/gp/product/1581801246/102-7258354-5562555?v=glance&n=283155'>The 7 Essentials of Graphic Design</a></p>%AC%93%8F%B2%07%DF%89a%7F%C3b1%AE%19L%01字体排版漫谈2006-05-14T10:00:00+08:00https://realazy.comRealazy<p>自从发现 <a href='http://users.skynet.be/mgueury/mozilla/'>Html Validator</a> 这个超酷超方便的扩展以后,我基本上没有用过 <a href='http://w3.org'>W3C</a> 的 <a href='http://validator.w3.org/'>Markup Validation Service</a> 来校验我的 XHtml 了。</p><p>网友 <a href='http://blog.sparanoid.com/'>Sparanoid</a> 留言告知,我的页面有错误。我用 Html Validator 检查了一番,发现由于 php 判读语句的位置不对导致在没有留言的情况下会缺 <code>div</code>,于是更改过来,HTML Validator 也告诉我<strong>0 errors, 0 warnings</strong>。然后就自信满满地回复留言说没问题了。</p><p>晚上,心血来潮,用 <a href='http://chrispederick.com/work/webdeveloper/'>Web Devloper</a> 的 Validate HTML(实际上就是使用 W3C 的 Markup Validation Service)来校验一下页面。My Godness...有一个 <code>dt</code> 标签没有关闭,未能通过……这么说 Html Validator 欺骗了我一把……为了验证我的想法,我用 Html Validator 重新看了同一页的源码,对,它就是没有发现,还沾沾自喜地说:<strong>0 errors, 0 warnings</strong>……</p><p>还是 W3 的服务品质有保证。 XD</p>%9D%83*u%DD%FA%ECs%15%9A%EEw%95%04I3Html Validator 不可全信2006-05-14T10:00:00+08:00https://realazy.comRealazy<p>想知道世界上为什么还有如此丑陋的设计吗?想知道就算是<strong>政府网站</strong>也不能正常使用 Firefox 的原因吗?请<a href='http://www.miibeian.gov.cn/'>备案</a>去。</p><p>很荣幸备案成了分类「政治」的第一篇 post,虽然这个分类实际上已经存在了半年之久。</p><p><strong>莫谈国是</strong>,只谈技术。</p>/%82%FB8%06%22%8F$%FE%8E%92%16;%80%9D%FE备案成功2006-05-12T10:00:00+08:00https://realazy.comRealazy<p>每当我访问一个新站点,我比较关心的是,当我离开首页以后,我该如何返回呢?我常见的方式有几种:</p><ol><li>在 Logo(或站点标识文字或站标)上加入首页的连接</li><li>在导航中列出一个「首页」(或类似)的连接</li><li>在某个角落写上「首页」(或类似)的连接</li><li>第一种情况结合第二,第三种情况之一</li><li>压根没有,用户只能键入站点的网址重来一次</li></ol><p>虽然我不懂什么用户体验的狗屁分析,但依据某人说法,用户就是专家。我是用户吗?是的,所以我也是<strong>专家</strong>,既然是专家,我就说说我的对于上述问题的用户体验吧。我认为,在 logo(或站点标识文字或站标)加上首页连接,结合在主导航上加上「首页」(或类似)的连接是最佳方式,只给 logo(或站点标识文字或站标)加上首页连接是次佳方式,其他的就不是什么好的实例了,用户体验一定不是那么爽,原因很简单啊,要回到首页,怎么还那么费劲啊……</p><p>在 logo(或站点标识文字或站标)加上首页连接是一股世界潮流,不知道我的看法对不对,我感觉这是 Blog 的流行所触发的。对于你我这种经常浏览 blog 的人是不难发现这股潮流的,所以对我们来说,想返回首页,最习惯的做法是点击 logo(或站点标识文字或站标)了。但为什么我还是认为这是次好的方式呢?并不是所有的用户都知道 logo(或站点标识文字或站标)的点击就是首页,了解这个近乎常识的东西还得培养<strong>用户习惯</strong>。所以,我认为最佳方式是,在主导航上加入一个「首页」(或类似)的连接。举个实际例子:<a href='http://www.adaptivepath.com/'>adaptive path</a>。无论是哪一类用户,无论到了哪里,用户总能轻易找到回「家」(首页)的路。</p><p>反观国内的 BSP 们,就举 Sina Blog 和 Bokee 吧,当你访问某人的 blog 时,请问你能轻易够找到「回家」的路吗?</p>%7B%01h%A2%F5%89X%DFi~%96#%DC%A9%133如何回到首页?2006-05-10T10:00:00+08:00https://realazy.comRealazy<p>其实,这个文章你不必花精力去看,因为,假如你给 Firefox 装上 <a href='http://users.skynet.be/mgueury/mozilla/'>Html Validator</a> 插件,在查看源代码的时候,它会忠实地指出你的错误所在。</p><p>结合我的工作经验,我想把我经常碰到的一些常见错误分享,引以为戒 :)。</p><p><strong>Doctype 前除了 xml 声明(<code><?xml version="1.0" encoding="UTF-8" ?></code>)外不要有任何东西,注释也不行。</strong>IE 6-甚至连 xml 声明都不能要,否则会导致浏览器触发 quirksmode。对于我们在做网页的过程中,可能不会犯这样的错误。但交由程序员开发时,他们可能会在 html 文件中输出一些东西,当这些东西在 Doctype 之前,就会出现问题。如果你在 IE 中发觉你的网页不对劲,请看看源代码,是否 Doctype 前是否有乱七八糟的编码 :)。</p><p><strong>转义,请转义!关闭,请关闭!</strong><、>、&是必须转义的。尤其是 URI 中的&很容易忘掉转义。至于关闭 xhtml 标签,这个错误太常见了,尤其是没有关闭标签的 <code>input</code>,<code>img</code>,<code>br</code>,<code>hr</code> 等。说到这,感概一下现在的浏览器,对 xhtml 的代码实在太宽容了,容许你犯很多很多的错误。但是 Opera for mobile 8+似乎就没有那么宽容了,如果你声明了 xhtml 的 Doctype,无论是 xhtml transitional, strict, frameset 还是 xhtml 1.1,它都严格以 <code>applications/xhtml+xml</code> 的 MIME-Type 来执行。这些不转义的实体,是造成 xml 非良构(not well-form)的常见原因。这样会导致不是那么宽容的浏览器(Opera for mobile 8+)拒绝处理(render)页面,直接输入源代码。</p><p><strong>别忘了 <code>type</code>。</strong>相信大家在写 <code>style</code> 时都会加上 <code>type="text/css"</code>。但是写 js 时,我没有看到有多少人能够给它加上 <code>type="text/javascriot"</code>。根据标准的建议,js 的正确写法是 <code><script type="text/javascript"...></code>。为了兼容性,我认为最佳的写法是 <code><script language="JavaScript" type="text/javascript"...></code>(发现有人懒到连 <code>language</code> 都不要 ||-_-)。你想想自己的编码,是不是忘了 <code>type</code> 呢?回去加上吧。</p><p><strong>请给标记写上必须的属性。</strong>最常见的错误是,<code>img</code> 忘了或者压根不加 <code>alt</code> 属性。每个标签必须的属性有哪些?哦,God,我还真记不住,交给 Html Validator 告诉你好了。</p><p>暂时总结这些。这是一篇会随时更新的 blog,欢迎补充 ^_^。</p>%9Eh7cV%C3%E3%EFf%97%19%BC%D9%F0%DF%C0网页标准化编码中的常见错误2006-05-09T10:00:00+08:00https://realazy.comRealazy<p>接上篇,我终于成功安装上了 <a href='http://slamd64.com'>Slamd64</a>,但是,唉,用起来真的不爽诶,软件太少了。如果是大学时代,我也许会爽,因为我有时间,我可以自己编译大部分常用的软件。另外,文档也很少,我已经没有那么多精力去探索了。</p><p>为了不浪费这颗 64bit 的 AMD Athlon 64 3000+,我决定再找一个 64bit 的 linux distro。<a href='http://gentoo.org'>Gentoo</a>? 同理,我现在 real real realazy,才不想整个系统都编译一遍呢。<a href='http://archlinux.org'>Arch</a> 也有了一个 <a href='http://www.arch54.org/'>Arch54</a>,但跟 <a href='http://slamd64.com'>Slamd64</a> 没有多大区别,维护十分困难。<a href='http://fedora.redhat.com/'>Fedora</a>? 老天,饶了我吧……<a href='http://opensuse.org'>OpenSuSE</a>? 嗯,这个不错,可惜她的 <a href='http://gnome.org'>Gnome</a> 做得并不是那么符合我心意……最终,只剩下 <a href='http://ubuntu.org'>Ubuntu</a> 了,这个超超超人气的版本……我记得我在 <a href='http://eedok.voidofmind.com/linux/chooser.html'>linux chooser</a> 中的答案并没有 <a href='http://ubuntu.org'>Ubuntu</a>,可能是那时候我还没有 64bit 的机器吧……</p><p>Ubuntu 果然如日中天啊,搜索引擎到处都是指向她的连接,中文社区也挺活跃。我用 VMware 装实际硬盘的方法把她给装上了(每当我不想折腾硬盘安装,这一招屡试不爽),虽然遇到了一点小麻烦(把 SATA 硬盘认作 IDE 了),但凭我多年的经验,一下子搞定,呵呵。</p><p>很顺利安装完毕。由于文档丰富,配置我的 ATi X1300 显卡也很顺利(Slamd64 我可是到现在都没有能够驱动它)。然后安装软件呢,实在是太人性化了,比 Windows 还要好用,压根不需要满世界找,太太爽了。</p><p>还好了,桌面应用使用 Ubuntu 还不错,放心,我不会丢掉我的 Slackware 的,有时间,我还是要把 Slamd64 折腾好的。不过现在,我真的很懒了,先用一段时间 Ubuntu 吧。</p><p>p.s.五一长假终于过去了,嗯嗯,睡觉去了,明天上班,好好工作,努力学习!</p>%01%EC%9A%8CwE%87%BB%BF%C6N%5B%DE%03%CC%C3赶了一把潮流,安装了 Ubuntu2006-05-07T10:00:00+08:00https://realazy.comRealazy<p>昨天忙活了一整天,在<a href='http://itican.net'>老韩</a>的慷慨解囊下,终于攒到一台 AMD A64 的机子。</p><p>一向就是 <a href='http://slackware.com'>Slackware Linux</a> 爱好者的我,早就想试试它的 64bit 版本 <a href='http://www.slamd64.com'>Slamd64</a> 了。按照我一贯的作风,我当然是去把 current 目录下过来,然后通过 <a href='http://elserv.ffm.fgan.de/~lermen/'>loadlin</a> 或者 <a href='http://sourceforge.net/projects/grub4dos/'>Grub for Dos</a> 引导安装了。</p><p>很顺利,跟 Slackware 的硬盘安装模式一样嘛,可是,可是,到了选择安装方式时,我一如既往的键入安装文件所在分区时,它竟然告诉我它 mount 不了盘!Alt + F2 切换终端,试着 mount 一下盘,才知道,Slamd64 压根不认 vfat 格式的盘,讽刺的是,在安装的提示中有"fat or linux partition",抄过来也不擦擦屁股的……</p><p>我想起了 <a href='http://zenwalk.org'>Zenwalk</a>,它也是这样,在安装时只支持 mount linux 格式的分区。我靠,这什么世道!就是说,如果你机器没有事先安装有 linux 的话,基本上,你要硬盘安装它们是十分费劲的了。难道加一个 vfat 的 mod 很困难吗?真搞不懂!这些版本难道认为世界上的人都很有钱,都可以刻盘安装吗?(呜呜,发泄一下,俺昨天装机就是没钱买刻录机……)这只会减少他们的用户。Slackware 是我最喜欢的版本,它的一切,包括安装,都交给了你自力更生。为什么这些衍生版本都要把这些优秀的东西抛弃?真真搞不懂!!</p><p>虽然有些工具可以在 win 下读写 linux 分区,或者我可以先装个最简板的 Slackware……但是,我还是决定睡觉先,累死我了,天亮了还要去老韩家……</p>%18X%C5ri%E0uC%7FsX%17%F7%F9Z%09wtf! Slamd64 的 bzImage 不认 vfat2006-05-02T10:00:00+08:00https://realazy.comRealazy<p>首先奉上 Photos:<a href='http://www.flickr.com/groups/csser-org/pool/'>http://www.flickr.com/groups/csser-org/pool/</a>。</p><p>然后引用 <a href='http://my.opera.com/tifa/blog/show.dml/237324'>meltifa</a> 同学的通讯稿,希望他不要介意。:)</p><blockquote><p>应该说,这也是中国第一届网页标准主题聚会。</p><p>流程:</p><p>happydesigner 的朋友(抱歉,忘了名字)简单介绍了他们这个组织以及推广 web 标准的计划和心得。</p><p>阿捷上台作演讲,我们的现状、所遇到的困难、将来的发展规划等等,翻译网站重构的背景,建一个 WEB 标准人才库,还提议可定期来类似的聚会,结果大家一致响应。</p><p>大家自由踊跃发言,机械工业出版社的 PLJJ 表示将来还会出类似的书,期待啊。最后大家分成了小组讨论。直到各自散去。</p><p>参加聚会的不只有 WEB 标准的爱好者,还包括了一些从事 UE 和产品人员,通过交流,大家遇到的问题基本都是差不多的。WEB 标准现阶段还是比较理想化的东西,从构建到实施需要付出很大的代价,其中学习的成本,用阿捷的话说,一个勤奋好学的设计师至少需要一个月时间来适应这种新模式。</p><p>推广的成本,如何说服公司的领导层支持标准?光有水平和口才恐怕还不够,如果老板只认前期效率和成本那你就死心吧。</p><p>沟通的成本,你不能指望公司所有参与项目的人都懂标准,为了代码的尽善尽美和合理规划,你需要和设计师以及程序员做很多的沟通工作。</p><p>目前来讲,还没有一个科学且适用范围较广的模式供参考,大家都在实践中摸索。现在,技术已经不是问题,如何推广这个理念才是首先要面对的问题。</p><p>其实,这样的聚会真应该多办办,通过聚会大家相互交流学习,增强信心和知识面,如果能引起业内一些大人物的注意就更好了,无论如何,老板们不重视和支持我们,那我们永没有出头之日。</p><p>ETC:</p><p>很荣幸能见到阿捷并和他交流,真人和网上一样和蔼可亲,下次一定带着我那本《网站重构》找他签名。</p><p>有的人发言声音不大,以后可以考虑准备一个话筒。</p><p>机械工业出版社的 JJ 很漂亮。</p><p>这次算是 bokee 的主场,又见到了不少老同事。怀念怀念~</p></blockquote><p>本人「修正」一下,他说的 happydesigner 是 <a href='http://lukhnos.org'>lukhnos</a>,那个 PLJJ(bingbingzi)是电子工业出版社的。ETC 似乎应该改为 P.S.。</p><p>最后说说本人看法。第一次嘛,大家算是认识认识,知道我们并不孤独,大伙的势力也不小啦。由于第一次,准备不足是有的,就像王宗义说的<a href='http://www.uuki.com/blog/index.php?2006/04/28/129-csser'>那样</a>,没有一个议题,大家各自谈论自己的话题。</p><p>不足之处还有,好像并没有引起媒体关注,比较失败。:) 或许像 Ajie 在会上所说那样,我们 Web 标准需要得到管理层(决策层)的支持,然后昨晚似乎只有 bokee 的一个经理过来了。我想是不是可以通过某些方式吸引管理层(决策层)的注意,能稍微对其施加影响的话,CSSer 可以说功德无量了。 :)</p><p>希望类似聚会可以制度化,大家可以定期交流。这个重任就交给 Csser 了。 :)</p>%DBlkyv%E0%F0-%CC%14/F%BE%AD%BA%14CSSer 第一次聚会2006-04-29T10:00:00+08:00https://realazy.comRealazy<p>最近比较忙,忙得心神不宁。</p><p>关于 Web 标准,我有很多话要说。可是我总是写不出来。我感觉我在哪方面还有很大欠缺。一年了,我依然一而再再而三的 coded, coding...有点厌烦自己了。</p><p>我打算近期自己设计一个 Wordpress theme,什么时候做好了,就什么时候更新。</p>%CFz,%A4%B4%B2%D6%B7.%CF%A4%0F%04%E27@暂时停止这个 blog 的更新2006-04-18T10:00:00+08:00https://realazy.comRealazy<p>先请看如下代码:</p><pre class='css'><code class='css'>filter: alpha(opacity=50); /* IE */-moz-opacity: 0.5; /* Moz + FF */opacity: 0.5; /* 支持 CSS3的浏览器(FF 1.5也支持)*/</code></pre><p>简单解释,IE 使用私有属性 <code>filter:alpha(opacity)</code>,Moz Family 使用私有属性 <code>-moz-opacity</code>,而标准的属性是 <code>opacity</code>(CSS 3, Moz Family 部分支持 CSS3)。后面的数值是透明度,使用百分比或者小数(<code>alpha(opacity)</code>)使用大于 0 小于 100 的数值,其实也是百分比)。</p><p>从上面的代码中你没有看到 Opera。没错,Opera 还未支持标准的 <code>opacity</code>,也没有其私有的可支持 Alpha 透明的属性。</p><p>但是,我们知道,Opera 是支持 Alpha 透明的 <a href='http://www.libpng.org/pub/png/'>PNG</a> 图片的(当然 Moz Family 也支持)。所以我们可以使用背景图片来实现 Alpha 透明效果。</p><p>例子:<a href='http://realazy.com/lab/alpha/'>http://realazy.com/lab/alpha/</a></p><p>关键在于:</p><pre class='css'><code class='css'>background: transparent url(alpha80.png) left top repeat !important;background: #ccc;filter: alpha(opacity=50);</code></pre><p>既然 Moz Family 支持 Alpha 透明的 PNG,所以我们没有必要使用其私有属性了。当然,你可以使用标准的 <code>opacity</code>,但别同时使用 Alpha 透明图片和 <code>opacity</code>,这样的话就成了两者的混合了。你可以把上面的例子下载过来,然后 <code>/*opacity: .5;*/</code> 的注释看看。</p>%C2%86f%1A%B7'%D0%A9%D2%CB%90%FB%F3%99%8F!IE, FireFox, Opera 浏览器支持 Alpha 透明的方法2006-03-21T10:00:00+08:00https://realazy.comRealazy<p>垂直对齐一直让人头痛,想用 <code>vertical-align</code>?它只对表格有效。没错,只对 <code>display:table-cell</code> 的元素有效。嗯,所以,怎么办?简单,让元素 <code>display:table-cell</code> 呗,然后你就可以随心所欲让 <code>vertical-align</code> 的 <code>top</code>, <code>middle</code>(注意:不是 <code>center</code>), <code>bottom</code> 了。很遗憾的是,IE 不支持。另外需要注意的是,<code>display:table-cell</code> 的元素的上一级元素必须是 <code>display:table-row</code>。</p><p>例子:<a href='http://realazy.com/lab/valign/align2.html'>http://realazy.com/lab/valign/align1.html</a></p><p>下面这种方案使用相对定位和绝对定位相结合的办法。这种办法底对齐十分有效,但需要垂直居中或者水平居中,则需要强制定义该元素的宽度和高度。在此只举例底对齐,需要垂直/水平居中对齐留待大家研究,可以参考我以前的水平居中的文章:<a href='/posts/2005-04-13-horizontal-center-align.html'>元素水平居中方案总结</a> 的「负边界解决方案」一节。</p><p>一个元素 <code>position: relative</code> 后,里面使用 <code>position: absolute</code> 的元素则可以相对上一级元素定位。该元素 <code>bottom: 0</code> 就可实现严格意义上的底对齐了。</p><p>例子:<a href='http://realazy.com/lab/valign/align1.html'>http://realazy.com/lab/valign/align1.html</a></p>Y%8B%87%07%01%D8%B7%D7D%12%C2'T%CDO%B0垂直对齐的两个方案2006-03-10T10:00:00+08:00https://realazy.comRealazy<p>原谅我再次使用英文标题 :)。这篇文章首发于 <a href='http://csser.org'>CSSer.org</a>,地址是 <a href='http://www.csser.org/2006/03/bad-tags-good-use/'>http://www.csser.org/2006/03/bad-tags-good-use/</a>。</p><p><code>b</code>, <code>i</code>, <code>tt</code>, <code>sub</code>, <code>sup</code>, <code>big</code>, <code>small</code>, <code>hr</code> 这些 HTML 标签(tag)属于表现性(presentational)标签,在提倡使用结构与表现分离的今天,不推荐使用它们,因为它们的表现效果均可以通过相应的 CSS 设置来实现。但它们不同于那些如 <code>center</code>, <code>u</code>, <code>blink</code>, <code>marquee</code>, <code>font</code> 等过时标签,不推荐跟不使用是不一样的。有时,适当使用一下这些表现性标签对我们的结构并没有什么坏处,反而能使我们的结构性代码更优雅。</p><p>先简单说一下这些表现性标签的作用,<code>b</code> 加粗, <code>i</code> 斜体, <code>tt</code> 打字机效果, <code>sub</code> 下标, <code>sup</code> 上标, <code>big</code> 加大字体, <code>small</code> 减小字体, <code>hr</code> 水平线。</p><p>在一些特殊场合,它们都可以用的上的,比如商标 TM 联合使用 <code>sup</code> 和 <code>small</code> 效果就很不错。再如,使用 <code>hr</code> 可以让页面的结构在没有 CSS 支援的情况下表现得更清晰。当然,你可以说,我都可以通过 CSS 来实现,没错,如果你适当使用它们,再用 CSS 加以修饰,你就可以锦上添花,何乐不为?</p><p>再者,为了实现某些效果,嵌入一些无意义的标签是十分有必要的。最明显的一个例子,以图代字(参看 <a href='http://stopdesign.com/articles/replace_text/'>Using Background-Image to Replace Text</a>),需要加入一个 <code>span</code> 标签,这时,你是否觉得这个 <code>span</code> 有点又长又臭?伙计,来个 <code>b</code> 如何?本身标题(h)就是粗体,加个 <code>b</code> 并没有什么影响,当然也没有什么结构上的意义,却比 <code>span</code> 少三个字母,看起来也舒服得多,伙计,谁说 <code>b</code> 就不能用?</p><p>重要的是,在一些不支持 CSS 的客户端,比如文本浏览器,像手机浏览器,*NIX 下的一些运行于终端的浏览器(lynx, links 等),它们可以解析这些标签,看起来比使用 <code>span</code> 更有吸引力。并且,它们并没有易用性/可用性方面的问题,既然它们就是表现性的,亦即无意义的,跟使用 <code>span</code> 一样,对我们的结构并没有害处。</p><p>当然,我并不是提倡滥用它们。我粗粗举几个例子,是想,我们 Web Standards 中国也需要自己独立的思考,把 Web Standards 结合实际问题,有效地应用到实际中去,这才有发展的动力。</p>%159%89C%07@%9F.Oe7I%7F%85%5E%F4Bad tags, good use2006-03-10T10:00:00+08:00https://realazy.comRealazy<p>来自《Series 60 Developer Platform: 设计 XHTML 移动描述内容》。</p><h2 id="">为移动电话设计应用软件</h2><p>当开发者要决定移动终端的各种应用软件应包含什么信息时,他们应考虑用户在什么情况下使用移动电话。服务内容应满足目标用户群的需要,并且应该对最常见的任务进行最优化。由于较小显示设备便于移动,所以,在没有 PC 机的情况下,用户可能会首先使用移动电话访问英特网以及获取急需信息。相应的范例包括快速访问航班信息、查看简讯和天气情况等。但用户使用移动电话上网冲浪的可能性要小一些。</p><h2 id="">保持用户任务流的流畅及图像的合理使用</h2><p>彩色页面看起来很诱人,但当图像传输使得服务减慢时,彩色页面也许并不让人觉得很舒服。根据可用性研究 2,用户不太热衷于那些由于图像传输而中断他们任务流的服务。特别地,当用户在搜寻目标页面时,大的图像就不太合适。含有信息价值的图像是令人青睐的,但在多数情况下,用户或是关掉图像以节省时间和金钱,或是不等图像下载就切换到下个页面。在下载所有图像之前允许用户继续浏览页面,是很重要的。</p><p>大表格也许会产生相似的问题——也就是说,在某一页面下载完之前,用户的操作被冻结;或者在页面下载完之前不能继续进行其他操作。因为移动电话显示屏的大小不同,所以开发者应确保即使是在最小的显示屏上,也能够阅读数据表格;通常这些数据表格要进行压缩以符合显示屏的要求。</p><h2 id="">结构对新用户要简单但也不能忽视熟手用户</h2><p>在移动通信服务中,浅层结构似乎常常比深奥结构更容易理解。链接和页面应该冠以描述性的名称,这样可以帮助用户找到他/她需要的信息。</p><p>很难说在一个链接列表页面上应该提供多少个链接。如果链接明显属于同类且容易浏览(每个链接占一行,以字母顺序,或另外以逻辑顺序排列,这样用户就不必把每个链接都读一遍),那么,比较好的方法是在一个页面上提供 30 个链接,而不是每个页面上提供 5 个链接,总共需要 6 个页面。如果有好几十个链接,在显示这些链接前提供排序选项是个不错的主意。如果链接能放置在一行上,则选择起来一目了然,页面也更美观。</p><p>WAP 2.0 没有 <code><do></code> 元素,相反,它们由 access keys 取代。然而,大多数用户似乎并不了解 access keys 元素,并且无法找到他们。为了帮助用户理解 accesskeys 的概念,开发者应确保 access keys 在屏幕上可见,而且以类似电话键的形式出现。</p><p>如果有可能,应提供搜索功能。熟手用户很欣赏这个功能,正如新手用户浏览著名站点一样。</p><h2 id="">在页面上提供足够信息</h2><p>交互式页面应该简短,信息页面应该较长 3。不建议使用 doormat 页面来启动站点,doormat 页面除了问候访问者和显示 logo 外,没有其他作用。较好的方式是用户能够直接访问服务。</p><p>在 XHTML 下,信息以页面形式下载,而不是以 WML 下的 deck 形式。这意味着向用户提供单个页面上的信息以支持他们的任务流就显得更为重要。由于 XHTML 页面是各自独立下载的,所以在 XHTML 页面间来回切换可能会消耗更多的时间。后向导航尤其存在这种情况,在这些情况下页面不能缓存,例如,与付帐或提供私人信息有关的系统就是这种情况。</p><p>任何页面的第一屏(最顶端)都是最重要的。所有经常使用的导航链接、搜索域、登录屏幕和大量信息都驻留在那儿。用户可在页面的剩余部分加载之前向前浏览,并且无需滚动页面。应避免在页面顶端放置横幅广告或没有任何信息的图形。最好是把广告放在页面的左侧或右侧边框。</p><p>上下滚动页面是困难的,因此带有表格的交互式页面不应该太长,因为用户不能确信他们是否已经填完长表格上的每个域。如果表格所占空间超过两屏,用户可能容易失去控制感。用户访问的目标页面应该具有足够多的信息。例如,如果目标页面包含故事或用法说明,则应该在一个页面上显示所有内容。当用户浏览一个长而信息量较大的页面时,能够在页面内引导用户的子标题将帮助用户浏览页面。</p><p>信息是以单个页面下载而不是以 deck 下载的这个事实是影响导航以及 WML 和 XHTML 之间结构性差异的最大单个变化。</p><h2 id="">为用户操作提供信息反馈</h2><p>开发者应该对用户操作、以及错误和问题情况提供正确的反馈。例如,在用户点击链接之后,页面标题应该与链接名相同。减小导航步骤应该不增加用户的不安全感,例如,用户操作的确认页面是必要的,尽管这些页面需要再次点击。如果确认页面丢失,用户也许觉得她/他需要检查,以确认这一行为是否发生——这会导致更多次的点击。应该认用户觉得他们始终在控制着系统如果出现问题,应提示用户下一步该怎么办。向用户解释期望输入的格式以及对必填项进行标记可阻止错误发生。</p><h2 id="">尽可能减少图像数量和减小图像容量大小</h2><p>应该认真考虑一个 XHTML 页面上图像数量和容量大小。页面上的每一幅图像就产生一次独立的来回,这反过来使整个页面的显示速度减慢。因此,应该尽量减少来回的数量。还要注意的是,当每次一幅图像到达移动设备时,整个页面的内容可能需要重新排列,这会占用时间和处理器资源。因此,一个仅有几幅图像的页面也许比一个有许多更小图像的页面下载得更快。如果有可能,建议在全部服务中各个页面上使用相同的图像;那么一个特定的图像只需下载一次且能够保存到高速缓存器中。例如,如果自定义的图像被用作 bullet,则在整个服务中应该使用相同的图像。</p><p>TCP/IP 连接也许会造成页面下载速度的不同,即使其数据量相同。例如,下载一个包含四个图像(每个图像大小为 2 KB 的 XHTML 页面)要比下载一个包含八个图像(每个图像大小为 1KB 的页面)的速度要更快。</p><p>如果使用 WAP 网关,则 WAP 网关应与通用分组无线服务(GPRS)支持节点网关(GGSN)放得近一点。在这个例子中,「近」是指数据延迟及数据包丢失的概率。由于 HTTP 重传,丢失信息会产生附加延迟。WAP 网关和内容服务器间的时延应尽可能的小。</p><h2 id="">定义图像高度和宽度属性</h2><p>建议内容开发人员在标记语言中明确地指定图像的高度和宽度,以使浏览器为图像预留适当的空间。如果在图像标签中使用高度和宽度参数,那么 XHTML 浏览器就能在下载图像之前为图像预留空间。因此,在图像下载之前页面就能够显示出来,当然,图像在下载后也能够出现在页面上。这并不影响 XHTML 页面的完整下载和处理时间,但却大大改善用户的感受,因为在下载图像之前用户可浏览页面。例如:</p><pre class='html'><code class='html'><img src="pics/header_main_page_001.gif" width="175" height="41" /></code></pre><h2 id="">谨慎使用表格</h2><p>XHTML 页面浏览器支持表格和嵌套表格的使用。在定义表格单元宽度,尤其是处理嵌套表格时,开发人员应谨慎行事。</p><h2 id="">考虑添加样式定义选项</h2><p>开发者可以用各种方式来定义自己的样式,例如:使用外部样式表、使用文档头部的样式元素,或通过使用指定元素的行间样式属性等。一般而言,虽然使用外部样式表无论何时都有可能把样式从标记语言中分离出来,这是一种好的方法,但应注意权衡考虑。如果样式定义包含在 XHTML 代码中,则 XHTML 页面的显示就更快,但是外部样式表的使用提供一种在整个服务中更改样式的便利方法。在整个服务中应该使用相同的外部样式表以避免把多个样式表下载到电话上。外部样式表仅需下载一次并能够保存在高速缓存器中。</p><h2 id="">删除代码内不必要的空白区和代码内的注释</h2><p>确保代码内没有多余的空白区非常重要。虽然空白区在屏幕上是不可见的,但仍要被处理,因为浏览器要对空白区进行分析、排版、CSS 分配和显示等。</p><p>XHTML 代码内注释数量应尽量地少,以使代码尽可能地紧凑。</p><h2 id="HTTP">使用 HTTP 标题指示来支持页面缓存</h2><p>浏览器能够把已经阅读的 XHTML 页面放在缓存器中。然而,内容开发者不应假定页面缓存是默认的 4。如果可能,应与文档一起发送明确的缓存标题以确保页面在客户端能够缓存。另外,应将过期时间设置为至少数天,这是为了确保在跨越多个时区的情况下,内容能够缓存一段适当的时间。</p><p>浏览器不支持在 Meta 标签内 (例如,使用 HTTP-EQUIV)放置缓存指示,但可用 HTTP 标题控制缓存。HTTP 服务器可设置"Cache-control: no-cache" HTTP 标题指示,而此服务器放置了能够定义「页面不进行缓存」的页面。</p><p>缓存使用「最近最少使用」算法,这意味着最少使用的项首先被清除。建议重复使用所有 XHTML 页面内的图像和外部 CSS,以确保它们留在缓存中,以便每次使用它们时不需要重新下载。</p><h2 id="Unicode-2-0-XHTML">使用 Unicode 2.0 字符集编写 XHTML 的内容</h2><p>诺基亚 XHTML 浏览器支持 ASCII 和 Unicode 2.0 字符集。因此,为了确保 XHTML 最大程度的互操作性,应该使用非拉丁语的 Unicode 来创建所有的 XHTML 内容。对于拉丁语,也可使用 ASCII 来创建。有些网关和代理能把本地字符集转换成 Unicode ,但并非所有的字符集都能转换。所以,保证终端接收 Unicode 的唯一方法就是用 Unicode 创建内容。</p><h2 id="MIME-XHTML">使用正确的 MIME 类型和经过验证的 XHTML 代码</h2><p>由 OMA 定义的 XHTML MP 内容的首选 MIME 类型为:<code>application/vnd.wap.xhtml+xml</code>。这一类型可以用于向 XHTML 用户代理提供 XHTML MP 文档支持。另外,也可使用 <code>application/xhtml+xml</code>。在一些 Series 60 浏览器上,必须使用 MIME 类型<code>application/vnd.wap.xhtml+xml</code> 以确保正确的 XHTML MP 内容视图。MIME 类型 <code>text/html</code> 也是可用的,但是,对于 XHTML 来说,这种类型应被保留,以便用于在现有的 HTML 用户代理上的显示功能。应注意」text/html「格式的 XHTML 文档将不作为 XML 格式来处理。例如,这意味着用户代理也许不能检测到形式上不像错误的错误。对于既想支持 XHTML 用户代理又想支持 HTML 用户代理的软件开发者来说,可以通过让 HTML 文档作为 <code>text/html</code> 类型,XHTML 文档为 <code>application/vnd.wap.xhtml+xml</code> 类型来使用内容协商机制。</p><p>建议所有 XHTML MP 内容使用 <code>.xhtml</code> 的文件扩展名。为了避免出现任何互操作性问题和提高性能,应该对 XHTML 代码进行验证。例如,可用 http://validator.w3.org 上的 W3C 验证器来验证 XHTML 内容。如果动态地创建 XHTML 内容,则生成的代码是合法的 DTD XHTML MP 1.0 代码。</p><h2 id="">使用描述性页面标题和元素标签</h2><p>页面标题描述所显示的页面内容。在 WML 中推荐使用标题,而在 XHTML 中强制使用标题。标题帮助用户浏览应用软件,因为它们会提醒用户她/他处于应用软件的什么位置。一个较好的方法就是标题用应该用服务的名称开头并且应该很短。用户以前选择的栏目将决定标题文本。例如,标题「书签」告诉用户显示屏包含了应用软件的一个书签列表,以及前一次选择的选项项目是「书签」。</p><p>标题文本应该使用比例字体,如果标题文本太长,文本会被自动删减。通常,删减标题的效果要比缩写更好,因为用户可能会对不熟悉的缩写困惑不解。虽然建议元素标签使用缩略词,但不应该使用目标用户群不大熟悉的首字母缩写词。相同的标签应该总是用于相同的操作,尤其是诸如 Delete、Remove、Erase、Clear 和 Destroy 的功能标记。</p><h2 id="">进行可用性测试</h2><p>对新的应用软件进行可用性测试总是正确的选择。没有参与设计和开发应用软件的人往往会注意到潜在的可用性问题,这些问题对于那些非常了解设计的人常常不是显而易见的。可用性测试应该在开发过程中尽可能早地进行。这样,在开发时间表内能够完成根据测试结果需要进行任何必要的更改。应该邀请能够代表未来最终用户的测试人员进行测试。如果日程安排不允许进行大量测试,至少应进行小规模测试。</p>%97%05%C9%60y%CF%04%5E%22%BCL%06%01%11O%E5优化移动 XHTML 服务的主要指南2006-02-23T10:00:00+08:00https://realazy.comRealazy<p>Yahoo!开通了 <a href='http://yuiblog.com/blog/'>Yahoo! User Interface Blog</a>,并<a href='http://yuiblog.com/blog/2006/02/13/welcome-to-the-yahoo-user-interface-blog/'>宣布开放</a> <a href='http://developer.yahoo.net/yui'>Yahoo! UI Library</a> 和 <a href='http://developer.yahoo.net/ypatterns'>Yahoo! Design Pattern Library</a>,依我很浅的阅历看来算是史无前例了。而且授权是这样的:</p><blockquote><p>The Yahoo! User Interface Library is released under the friendly <a href='http://developer.yahoo.net/yui/license.txt'>BSD open-source license</a>. The Yahoo! Design Pattern Library is released under the <a href='http://creativecommons.org/licenses/by/2.5/'>Creative Commons Attribution-By license</a>.</p></blockquote><p>真不错,以后学习 Web 设计和 UI 设计可以有一个重量级的老师了。</p>%98%A6%19%0Foh%BCO%ACTG%DE%CB%CA~GYahoo! UI Library 和 Yahoo! Design Pattern Library2006-02-16T10:00:00+08:00https://realazy.comRealazy<p>今早小试了一下 IE 7 Beta2,其他的我就不多说了,我来谈谈它对 CSS 的支持吧。</p><p>第一,对 <code>dotted</code> 样式的 <code>border</code> 改进了,不再是 <code>dashed</code> 的样式。不过有时候显示还不是很细腻,刷新一下就好了(似乎是 IE 的通病,啥都刷一下就 ok)</p><p>第二,对 <code>fixed</code> 和 <code>absolute</code> 的 <code>position</code> 定位支持更加好了。可以用 IE 7 Beta2 浏览 <a href='http://www.meyerweb.com/eric/css/edge/'>CSS Edge</a> 来检验。</p><p>第三,非 <code>a</code> 的元素也可以使用 <code>:hover</code> 了,但 <code>:focus</code> 还是不行。</p><p>第四,已经修正了前几天我提到的 bug。见 <a href='/posts/2006-01-20-removing-dotted-links.html'>Removing dotted links?</a>。</p><p>发现中……</p>%13%C96%5DF%E4/%DC4%A1%BA%B9T%B9%9C%0FIE 7 Beta2 的 CSS 支持2006-01-23T10:00:00+08:00https://realazy.comRealazy<p>看了 <a href='http://sonspring.com/journal/removing-dotted-links'>Removing Dotted Links</a> 这篇文章。我早就注意到了(因为我之前使用过 Fx 1.5 的 Alpha 版)文中提到的关于连接虚线边框的问题:</p><p><img src='http://sonspring.com/images/78.png' alt='dotted links' /></p><p>该文提出了去除虚线边框的办法。</p><p>我反对去除连接的虚线边框,因为这是连接的一个浏览器默认<strong>特性</strong>。我怀疑去掉以后有些用户还认不认为(不是知不知道)它是一个连接。</p><p>我还认为 Firefox 这样做是对的。产生这种情况的原因是 CSS 中一种用图片取代文本的方法导致的。这个方法使用 <code>text-indent</code> 这个性质,把值设置到足够大,让文本超出屏幕的可视范围。只是超过而已,并没有消失,所以虚线边框的做法只是正确地把 CSS 的设置展现出来而已。</p><p>使用 <code>text-indent</code> 本身就是一个 hack,hack 一般都是很 dirty 的,呵呵。</p><p>由于这个原因,我建议使用 StopDesign 的 <a href='http://stopdesign.com/articles/replace_text/'>Using Background-Image to Replace Text</a> 的方法,不过需要多加 <code>span</code> 标签,很不爽啊……</p><p><strong>Update:</strong> 经 <a href='http://old9.blogsome.com/'>old9</a> 在旧版 blog 中的留言(刚好在 blog 搬迁期间留言,所以没能整合过来)指教,其实可以通过 <code>overflow: hidden</code> 来去除超出的虚线框。感谢 old9,这确实是一个很好的方法。</p>9%F5n%F0%9A2%5C%A5%9C%18ux%0B3_%B7Removing dotted links?2006-01-20T10:00:00+08:00https://realazy.comRealazy<p>来自 <a href='http://www.digital-web.com/'>Digital Web Magazine</a> 的 <a href='http://www.digital-web.com/articles/designing_for_the_web/'>Designing for the Web</a>, 总结了一些针对<strong>WEB</strong>设计需要注意的问题。</p><h2 id="">第一,分辨率。显示器分辩率使用情况如下:</h2><table><tbody><tr><th>Screen Resolution</th><th>2005</th><th>2004</th></tr><tr><td>Larger</td><td>11%</td><td>10%</td></tr><tr><td>1024 × 768</td><td>56%</td><td>50%</td></tr><tr><td>800 × 600</td><td>28%</td><td>35%</td></tr><tr><td>Smaller/Unknown</td><td>5%</td><td>5%</td></tr></tbody></table><p>我们不去追究数据的权威性,但至少可以反映个大概。依据中国国情,使用 800x600 的用户难说不会比上述数据大,所以,现在绝对不要抛弃我们的 800x600 的用户。</p><p>至于图片的分辨率,这篇文章说得太多了,我觉得 web 设计中,时刻记着 72dpi 就够。</p><h2 id="">第二,浏览器使用情况。</h2><table><tbody><tr><th>Browser</th><th>2005 (July)</th><th>2004 (December)</th></tr><tr><td>IE 6</td><td>67.90%</td><td>65.50%</td></tr><tr><td>IE 5</td><td>5.90%</td><td>9.90%</td></tr><tr><td>Firefox</td><td>19.80%</td><td>n/a</td></tr><tr><td>Opera</td><td>1.20%</td><td>1.80%</td></tr><tr><td>Mozilla</td><td>2.60%</td><td>17%</td></tr><tr><td>Netscape</td><td>0.50%</td><td>1.60%</td></tr></tbody></table><p>如果不是特殊需求,我觉得 Designing for IE 6+ & Gecko based (etc. Firefox)就够了。</p><p>而浏览器实际能够处理的分辨率如下:</p><table><tbody><tr><th>Screen size</th><th>IE 6</th><th>Firefox</th><th>Opera</th><th>Mozilla</th><th>Netscape</th></tr><tr><td>800 × 600</td><td>779 × 400</td><td>781 × 434</td><td>777 × 427</td><td>779 × 420</td><td>781 × 389</td></tr><tr><td>1024 × 768</td><td>1003 × 568</td><td>1005 × 602</td><td>1001 × 595</td><td>1003 × 588</td><td>1005 × 557</td></tr></tbody></table><p>不知道为什么在宽度上 Mozilla 会比 Firefox 少 2px?总之以最小值来衡量就 ok 了。文章还提到基于百分比设计(可伸缩、可扩展),但这确实是一个难题,得看实际情况,该文也没有给出什么能令人满意的答案。</p><h2 id="">第三、用色。</h2><p>唯一能够跨平台的就是 web216 安全色。</p><h2 id="">第四、图片压缩</h2><p>对比 gif, jpg, png。我的感觉是,png 未来比较有前途。我敢打赌,等 IE 支持 alpha 的 png 后(应该不远了),满世界都是 png。</p><h2 id="">第五、文本(字体)</h2><p>这个字体常用列表比较有用:</p><ul><li>Arial, Helvetica, sans-serif</li><li>Times New Roman, Times, serif</li><li>Courier New, Courier, mono</li><li>Georgia, Times New Roman, Times, serif</li><li>Verdana, Arial, Helvetica, sans-serif</li><li>Geneva, Arial, Helvetica, sans-serif</li></ul><h2 id="">其他相关</h2><p>还需要注意易用性,可用性还有受众(Audience)的问题。</p><p>"As Web designers, everything we do is for the user. "</p>o%CE%E2r%CF%09%B58jO%60%BCUQ%91%BCDesigning for the Web2006-01-20T10:00:00+08:00https://realazy.comRealazy<p><a href='http://zenwalk.org'>Zenwalk</a>(刚开始的时候叫 MiniSlack)是一个轻量级的 Slackware 衍生版本,跟 Vector 差不多。两者比较大的区别是,Zenwalk 支持 ReiserFS 4,从而引起我一丝兴趣,决定安装一个试试。</p><p>既然是 Slackware 的衍生版本,那么按照 Slackware 的方法来安装它应该没有问题。Slackware 只要有 bzImage 和 color.gz 即可安装,把 Zenwalk 安装盘文件(iso)解压开来,可以在 kernel 目录下找到 bzImage(有 ata 和 scsi 两个版本),在 isolinux 目录下发现 initrd.img。有这两个文件就够了。</p><p>接着,使用 lilo/grub(Dos 版或者 linux 版都可以)进行引导,即可进入安装,安装界面跟 Slackware 差不多,配色稍微变化而已。加了 swap 区,分好 root 等分区后,即可开始了。这里要注意,选择硬盘安装方式后,Slackware 要你填入分区,然后填目录,Zenwalk 需要直接填写安装源,因此必须先把安装文件所在的盘 mount 好。</p><p>一切按照 Slackware 的方式进行,直到它叫你 Ctrl + Alt + Del。注意!!!此时不能这样做,你还不能重起!或许是由于 Zenwalk 对硬盘安装的支持还不是很完善,如果此时重起,你是没有机会进入 Zenwalk 的……因为,它还没有装上引导!</p><p>Alt + Fn(n=2-7)打开终端,把刚才所安装 Zenwalk 的 root 目录 mount 上,chroot 进去,编写/etc/lilo.conf(还没有就新建),写好后执行 lilo,这样才算大功告成。</p><p>好了,我可以试用我的 ReiserFS 4 版的 Slackware 了……</p>%14%CE%ACc%92LM%D4%8A%7C%C2Y%A4%B3%812硬盘安装 Zenwalk 的注意事项2006-01-15T10:00:00+08:00https://realazy.comRealazy<h2 id="bug">bug 描述</h2><p>某个元素如 <code>div</code> 使用了 <code>position: relative;</code> 后,其内的元素设置了 <code>border</code> 或者 <code>background</code>,在 IE 浏览器下,这些 <code>border</code> 或者 <code>background</code> 展现出来的线条或者图片会神秘消失,拖动滚动条偶会出现。如图:</p><p><img src='/assets/missing.png' alt='' /></p><h2 id="">解决办法</h2><p>暂时发现的方法是给设置了 <code>border</code> 或者 <code>background</code> 的元素也设置成 <code>position: relative;</code>。</p><p>还有什么办法,欢迎大家交流、分享经验。</p>@%01%D2%FC%84i%B4%DC%15%ED.F%3C%99%E6%88笔记:消除 IE absolute/relative 下某些 border,background 神秘消失之 bug2006-01-06T10:00:00+08:00https://realazy.comRealazy<p>2005 年,我是幸运的。我一个坚持了 4 年多的兴趣,靠着一门并不高深的新技术,走入一个我没有想过的工作领域,并在工作中不断地得到提升。</p><p>路是荆棘的。理想主义让我吃了些苦头,至今才明白商业需要妥协。可以说,至今没有人提供或者分享大型项目的 Web 标准解决方案,经过近 9 个月的实践,我想我可以总结出一些经验。</p><p>第一、Web 标准更重要的意义在于是手段而不是目的。跟传统的网页制作方式相比,Web 标准只不过提供了另外一种实现的方式,并由此产生更好的效益、更高的效率而已。在实际应用中,不能为了标准而标准,应该综合现有的一切手段,析取最合理、最有效的方式。</p><p>第二、抛开 Web 标准的诸多方面不谈,就 XHTML 的写作而言,这是一门很考究的技术。这要牵涉到网页的版面结构、栏目安排、内容展现等。尤其对于大型项目,Web 标准需要了解整个网站功能,内容等方方面面,才能合理地组织 XHTML。做 Web 标准,策划是不可或缺的能力之一。</p><p>第三、标准的 CSS 能够控制页面的每一个元素,但最「主流」浏览器(你知道我在说 IE6)对 CSS 糟糕的支持,我们绝不能吝啬代码,得不厌其烦给每个元素尽可能多的 class 或者 id,当然,不能产生混淆与混乱。</p><p>第四、理论上说,Web 标准的表现形式(这里主要指 CSS)可以实现一切传统方式可以实现的效果。但是,两种方式的制作观念从根本上是不同的。在进行页面设计的时候就需要考虑实现方式,否则事倍功半。</p><p>现在,我仍然是专职的 CSS 代码工人,无法染指页面设计。我在 2006 目标是,改造自己,学好 Photoshop 等工具,修习艺术,让自己可以参与设计,更有效地提高工作效率。</p><p>我长远点的目标,是统领整个设计部。从今年今天起开始努力!</p>M%0CV%B6%7C%92%1A0%F3%B5%D9Cr%07%DF;总结和展望2006-01-01T10:00:00+08:00https://realazy.comRealazy<p>有点郁闷哇……</p><p>有点失去信心啦……</p>%8E%BE%A9?%D0%CA%EC%0D%81k%EE%D3Z%D4%3B%F9孤独的 web 标准2005-12-20T10:00:00+08:00https://realazy.comRealazy<p><code>line-height</code> 很容易被人忽视。</p><p>这个 CSS 性质(Property)不仅对文本排版有十分重要的作用,同时也可以做些 hacks and tricks(不要忘了,在这个 CSS 如此烂的年代,web 标准靠这些 hacks and tricks 维持生计)。</p><p>对一些装饰性的 border,当与字体等高时会有比较良好的观感。比如 <a href='http://link.eyou.com'>http://link.eyou.com</a> 最上面的整站导航。许多人可能会给这个需要等高的 border 搞得很郁闷,不得不用图片代替。其实解决方法很简单,把该区块的 <code>line-height</code> 设置为 1 即可。</p><p>另外,不知道为什么 <code>vertical-align</code> 形同虚设,现在使用 CSS 来控制元素的垂直居中是一个很富挑战的课题。当元素只有一行字,比如标题,此时可以使用 <code>line-height</code> 来控制。把 <code>line-height</code> 的值(value)设置为该区块的高度,则文本可以居中了。</p>2%BB%82%5B%B1m%15%929%8B&%C5x%D6%E9%CBline-height2005-12-20T10:00:00+08:00https://realazy.comRealazy<p>时下 web 风行标准,然而许多人目的并不明确,似乎是为了标准而标准。web 标准重要意义之一是提高可用性(usability)和易用性(accessibility)。考究网页布局的结构,内容的安排对可用性和易用性的影响,实际上也应该是 web 标准中的内容。</p><p>依本人<strong>个人看法</strong>,在 HTML/XHTML 的代码结构上,应该遵循内容优先的原则。举个简单的例子说明。在小屏幕设备上,普通网页的内容往往会很长。如果内容之前有长长的导航、广告等,用户阅读需要不断滚屏才能读到想要的内容。这绝对不是一个好的用户体验。</p><p>然后许多人并不使用内容优先的法则来编写(code)HTML/XHTML 代码。即使通晓内容优先的法则,人们最常见的做法是,把边栏放前,内容置后。这样做的好处是,可以方便的浮动(float)边栏,以适应布局的需要。这是最典型的为布局而布局,为标准而标准的做法。当然,边栏很少不足影响内容浏览体验的可以反驳我,仁者见仁。</p><p>我想举个例子,来证明使用内容优先的法则,在布局上并没有比其他结构更逊色,反而有更大灵活性。内容居中,两侧两栏?内容居左,右边两栏或相反?内容置于两栏之上?都没有问题!至于两栏置于内容之上,也不是不行,只是栏目的液态设计(liquid design)的灵活性受限而已。</p><p>我激昂地说到这里时,可惜的是,发现 IE6 的有严重的 bug,我的天!我只好把一个未成品展出,IE6 只能部分有效。IE5.x(有些小 bug,但不足影响布局),Opera 8.x,Gecko 1.x 都没有问题。这是不是让人更期待 IE 7?</p><p>进入<a href='http://realazy.com/misc/ra.html'>样例</a>查看,选择所愿的布局方式,看看使用 <code>relative</code> 跟 <code>absolute</code> 的强大灵活性。</p>e%B5%0F%E7%85%EFr%ACD%E2%F0u%82)%10%87内容优先,布局爱用 `position`2005-11-25T10:00:00+08:00https://realazy.comRealazy<p>我们都知道使用 <code>@import</code> 可以对 NS4 隐藏样式表。</p><p>我们知道 <code>@import</code> 的格式是:</p><pre class='css'><code class='css'>@import url(path/to/css.css) mediatype;</code></pre><p>后边的 <code>mediatype</code> 是可选的。</p><p>但加上 mediatype 后,无论是一个还是两个还是多个,IE6 就会过滤(filter)掉整个 CSS,郁闷啊,呵呵。</p><p>如果你着实要使用 mediatype,你可以在 <code>style</code> 标记(tag)指定。比如</p><pre class='html'><code class='html'><style type="text/css" media="screen,projection"></code></pre>%C8%85%82%5C%1F%03%0FjJ%B4gG%5Bl%0D%5D让 IE6 去死——对 IE6 隐藏样式表2005-11-21T10:00:00+08:00https://realazy.comRealazy<p>噻,这年头,连不吃人间烟火的<a href='http://zhyj.com'>郑渊洁</a>都写 <a href='http://blog.sina.com.cn/m/zhyj'>Blog</a> 了,但为什么不叫自己的天才儿子给搭建一个,却跑去新浪呢?还美其名曰不花钱进了妓院。噻,依我推测,可能还要新浪给钱才进的吧,真是「既当婊子又立牌坊」……依郑渊洁的商业头脑,这种天上掉的馅饼最合胃口……新浪被盛大搞过去后真是财大气粗啊,一下来找了这么多名人……想不到,想不到,对我影响最大的作家——郑渊洁,也不能免俗,来了……</p><p>看看吧……</p>4%8C.%80%DC%C37:7%A8%D2O%1E#5%15郑渊洁的 blog2005-11-18T10:00:00+08:00https://realazy.comRealazy<p>label 是一个很有用的标签(tag),它最大的作用是可以在表单元素(文本框,选框等)之外的地方获取输入焦点,直接影响 web 的可用性和易用性。</p><p>举例。</p><pre class='html'><code class='html'><label for="email">Email: </label><input type="text" id="email"></code></code></pre><p>或者:</p><pre class='html'><code class='html'><label for="email">Email:<input type="text" id="email"></code></label></code></pre><p>之前我写过一篇<a href='/posts/2005-03-23-table-web-standards-solution-1.html'>表单的 Web 标准解决方案</a>,使用的是第一种方式。不过在此我严重推荐使用第二种方式,这样可以利用更大的点击空间。</p>%F8%A5%ACPa%81GL%5D%9E0%14vNQt`label`2005-11-15T10:00:00+08:00https://realazy.comRealazy<p>Opera 为了让自己的手机版浏览器能够轻松地处理网页,在官网上提供了相应的教程,包括 XHTML 和 CSS 的编写建议、测试等。随着一切皆移动趋势的发展,手机、PDA 等上网需求越来越大。可以说,Opera 抓住了先机,自然,为了促进自己事业的发展,跟开发者的互动也不可少了,呵呵。</p><p>废话不说,给个地址,<a href='http://my.opera.com/community/dev/device/'>http://my.opera.com/community/dev/device/</a>,Web 标准的 fans,别光说不练,大家都来 SSR 吧……</p>%05%E9%88%D1%D7%F3%16:%C5%0F%89%C6%22%BD%9C%B2Making Small Devices Look Great2005-11-08T10:00:00+08:00https://realazy.comRealazy<p>一直被一个问题所困扰。</p><p>使用 <code>float</code> 布局的页面,在 firefox 好好的,在 IE 下初时看也是好好的。可是,可是,在某处点击一下,呱啦,右边的 div 滚下面去了。原因是,右边 div 设置为 <code>overflow: auto</code> 的,即,内容超出固定高度时出现了垂直滚动条。</p><p>就是这个垂直滚动条,IE 跟 Firefox 处理方式的不一致导致了问题。Firefox 的处理方式是,div 的原有宽度不变化;IE 的处理方式是,把 div 的宽度加宽,加宽部分刚好等于滚动条高度,从而超出 CSS 所设置的宽度,导致布局损毁。</p><p>没办法,只好麻烦我们的老好人 <code>!important</code> 了……</p>A%8F%E5KI%F7%9AC%AE%E5%D8%BC%81%9E%DA%8B小心 IE 的滚动条2005-11-01T10:00:00+08:00https://realazy.comRealazy<p>当你准备全面进军 web 标准时,有时候你是不是被表格的弄得焦头烂额呢?比如,原来使用「非法」的 <code>nobr</code> 现在要用什么来代替呢?</p><p>今天,就让我来一个终极解决方案。</p><p>或许你已经 Google 到 <code>white-space: nowrap</code> 可以实现 <code>nobr</code>,不错,但关键还有一个,那就是 <code>table-layout</code>。</p><p><code>table-layout</code> 有两个值可以设定,预设的是 <code>auto</code>。</p><ul><li><code>auto </code>: 默认的自动算法。布局将基于各单元格的内容。表格在每一单元格读取计算之后才会显示出来。速度很慢。</li><li><code>fixed </code>: 固定布局的算法。在这算法中,水平布局是仅仅基于表格的宽度,表格边框的宽度,单元格间距,列的宽度,而和表格内容无关。</li></ul><p>为了让表格布局固定住,我们需要 <code>table-layout:fixed</code>。假设没有 <code>fixed</code>,把你的分辨率减小到一定程度,你会发现 <code>white-space: nowrap</code> 是不会生效的。</p><p>技巧:为了使表格能够适应液态布局(liquid layout,即可伸缩性),不建议你为表格设置绝对宽度,使用相对宽度如百分比是不错的选择。另外,把宽度相对固定的内容(比如时间格式)格设置绝对宽度,非固定的内容格不设置任何宽度,只需给予其设置 <code>white-space: nowrap</code>,虽然在小分辨率的情况下会溢出,但依据某些观点看来,这比折行更美观。</p><p>当然我的观点不这样,如果是属于我自己能控制的东西,我是不会要 <code>white-space: nowrap</code> 的,让用户能够清晰地看到内容,我认为比美观更重要。</p>A%96If%FE%15%AEU%EC*i%BC%D2%FA%E2%C3`table-layout:fixed`2005-10-24T10:00:00+08:00https://realazy.comRealazy<p>准备对 eYou 的免费邮件界面全面优化,以增进「换皮」的灵活性和手机浏览体验。</p><p>回头一看我半年前做的的东西,有点让人不好意思……不过现在开发已告一段落,终于有机会让我细心打造精品了。</p><p>用 <a href='http://opera.com'>Opera</a> 的 Smallscreen 模式看了看,哗,单导航就有几屏。主要原因是,我大量使用了 <code>li</code>,使得没有 CSS 支援的情况下导航菜单竖排。这让我重新思考使用 <code>li</code> 的适用性和必要性。</p><p>依稀记得,我刚入道时受到的严重影响:全世界都在鼓吹菜单使用 <code>li</code> 你就 web 标准化了……确实,有阵子全世界都是 <code>li</code>……菜单为什么必须使用列表?没有人告诉我。这只是一些大牛的使用习惯,后来影响了很多人,不仅仅你我,还有千千万万不知道 web 标准为何物的初哥。</p><p>我不反对菜单使用列表,尤其是单项比较长的时候。但是菜单单项比较短,比如只是一个单词,三三两两个汉字时,没有必要。使用 <code>a</code> 就够了(不要告诉我你的菜单不是连接)。比如:</p><pre class='html'><code class='html'><div id="toolbar"> <a href="compose.php?folder={{$smarty.get.folder|escape:'url'}}" id="writemail" ><b>写邮件</b></a > <a href="refresh.php?uid={{$eyou.UID}}&url=listmail.php&folder={{$onFolderInfo.folder_path|escape:url}}" id="receivemail" ><b>检查新邮件</b></a > <a href="pop_mail.php" id="popmail"><b>POP 收信</b></a> <a href="javascript:fake_func();" id="move"><b>移 动</b></a> <a href="javascript:move('垃圾箱');" id="delete"><b>删 除</b></a> <a href="search.php?folder={{$smarty.get.folder|escape:'url'}}" id="search" ; ><b>查 找</b></a ></div></code></pre><p>你可以打开 <a href='http://csszengarden.com'>CSSZenGarden</a> 参考参考,它对于 footer 和 linkList 是怎么写的。</p><p>如果嫌一个 <code>a</code> 不够用,你可以在里面添加代码,<code>span</code> 呀,<code>strong</code> 呀等等。假如你是一个想像我一样狂热,你可以使用过时的 <code>b</code>,原因无它,就一个字,省。况且,你不觉得 <code>a</code> 后跟着 <code>b</code> 不是很优雅吗?:)</p>%99%94%AC%DD%01e%A6@r%FB%9A%87%B5%11%08%1B不要滥用 `li`2005-10-19T10:00:00+08:00https://realazy.comRealazy<p><a href='http://tango-project.org'><img src='/assets/missing.png' alt='Tango Project' /></a></p><p>Linux 下的桌面环境(DE,Desktop Environment)跟它的发行版(distro)一样多,跟 DE 结合和/或独立的软件更是杂如牛毛。除非世界上所有的用户都像我一样坚持只用一个 DE(我使用 <a href='http://gnome.org'>Gnome</a>),并且只用这个 DE 下的软件,那么问题不大。问题是,许多用户并不关心软件属于哪个 DE,有时会被冒出来的一些界面感到极度不爽,为什么风格(color, icon 等等)都不一致?原因很简单,你在此 DE 里打开了一个不属于此 DE 的软件,比如在 KDE 里打开了 GEdit。简单地说,用户体验极为糟糕。</p><p>有的发行版在努力。就举两个比较大的发行版来说,Fedora,Mandrave,它们紧紧把各种 DE 和软件杂糅在一起的,他们各自开发了相关的主题如 Bluecurve,Galaxy,使得 KDE 和 Gnome 结合看起来不致于在外表上看来很糟糕。但对不起,如果选择不同的主题,一切又变得很糟糕,并不是所有用户都喜欢系统自带自设的主题。</p><p>KDE 似乎更关心这点,它有一个叫做 <a href='http://www.freedesktop.org/Software/gtk-qt'>GTK-Qt Theme Engine</a>,使得 GTK 的样式可以模拟 Qt 的。</p><p>但这些都是私有的,没有通用性。</p><p>很多年了,终于等来了 <a href='http://tango-project.org/'>Tango! Project</a>,她的目的是为自由(free)和开源(Opensources)软件的用户界面提供一致的体验。同时,<a href='http://novell.com'>Novell</a> 也赞助开通了 <a href='http://betterdesktop.org/'>BetterDesktop</a>。</p><p>相信不久,无论我们进入 KDE,Gnome 或者 XFCE 等,都可以看到一致的界面了。在这种情况下,或许我还会装上个 Qt,装上一些 Qt based 的程序…… :P</p>6%3C%18%DF%06%BD%85%A5xv9%3E6N%C1%5C美化开源软件运动——来跳探戈吧2005-10-12T10:00:00+08:00https://realazy.comRealazy<p>自从离开海淀以来,我基本上下了班之后就与世隔绝了。</p><p>因为没有了网络。</p><p>离开海淀后才是我生活真正考验的开始,忽然发现钱不够花,房租、伙食、交通、债务等等等等,让我焦头烂额。所以我在未到北京前就计划买的显示器,直到 10 月 2 日才在中关村实现。</p><p>而这次搬家,应该会常住了,所以交了三百块押金,装了个电话,顺便上了宽带。</p><p>我这台颇电脑终于可以发挥余热了。</p><p>我也该定些学习计划了。</p><p>初步确定在 XML, XSL 和 JavaScript 上下下功夫。</p>z%C4%FC:%95%DF%AA%0FW%B9%C0%60/%85%A0%96终于买了显示器,上了宽带2005-10-05T10:00:00+08:00https://realazy.comRealazy<p>查看 <a href='http://opera.com//css/media.css'>http://opera.com//css/media.css</a>,你可以发现你一条你可能从来没有见过的规则:@media screen and (max-width: 760px)。</p><p>这就是 Media Queries,我译作媒介查询,直译。从字面上就可知道,这跟 CSS 的展现舞台——媒介有关,而后面的表达式(expression)则是该媒介作用的条件。</p><p>这是一个 CSS3 特性,尚是规范候选( Candidate Recommendation),具体请看来自 W3C 的文档:<a href='http://www.w3.org/TR/2002/CR-css3-mediaqueries-20020708/'>Media Queries</a>。</p><p>这个特性让 CSS 可以更精确作用于不同的媒介类型,同一媒介的不同条件(分辨率,色数等等)。</p>%E2%8D%A4UV5%CF%99%8C%E1%81%BC%CB%EDr%EEMedia Queries2005-09-29T10:00:00+08:00https://realazy.comRealazy<p>假如你已经看过 <a href='http://meyerweb.com/'>Eric A Meryer</a> 的<a href='http://oreilly.com.cn/book.php?bn=7-5083-0560-4'>《CSS 权威指南》</a>(<a href='http://www.oreilly.com/catalog/css2/'>Cascading Style Sheets: The Definitive Guide</a>),你一定对 <em>Lorem Ipsum</em> 很熟悉。它们是什么意思呢?别企图用字典查,当然,我干过这样的傻事。XD</p><p>以下是引自 <a href='http://www.richyli.com/blog/'>http://www.richyli.com/blog/</a> 的解释:</p><blockquote><p>Lorem Ipsum 原本是指欧美在设计、排版时,用来填充版面的假文字,大体上以拉丁文为主,让版面上看起来有标题、文章,但评估字型、版面的时候,又不至于被文字实际的内容影响。</p><p>在欧洲读书的时候经常看到 Lorem Ipsum,当时以为那些是有意义,但我看不懂的拉丁文,后来看了维基百科等网站的介绍后,才豁然释怀,原来我看不懂是正常的,那本来就不是要让人看懂的。</p></blockquote><p>今天我才知道……详细请看维基百科的 <a href='http://zh.wikipedia.org/wiki/Lorem_ipsum'>Lorem ipsum</a> 词条。维基百科真是好东西,以后要常上。:)</p><p>还有一个中文的<a href='http://www.richyli.com/tool/loremipsum/wordcount.asp'>亂數假文產生器</a>,虽说是正体(繁体)中文的,但排版方面跟简体中文应该无甚区别,以后做网页,没有内容填充时,你可以试试这个东西。</p>%B3%8E%E2w%BF%D9%D9%A1%D6%B5%C7%05%01%183%7DLorem Ipsum?2005-09-13T10:00:00+08:00https://realazy.comRealazy<p>来自 <a href='http://www.webstandards.org/'>WaSP</a> 的 <a href='http://www.webstandards.org/buzz/archive/2005_09.html#a000558'>Best Practices for Declaring Languages in HTML and XHTML</a>,这是在 HTML/XHTML 声明语言的几条指南,我简单翻一下,作为<a href='https://www.google.com/search?q=在XHTML和HTML中使用语言信息&ie=UTF-8&oe=UTF-8'>在 XHTML 和 HTML 中使用语言信息</a>的补充。</p><p>何时何处如何在 HTML/XHTML 中声明一种或多种语言?根据 <a href='http://www.w3.org/International/geo/'>GEO group</a> 的建议,声明语言的一些准则如下:</p><ul><li>一定要为页面声明默认语言,使用 <code>html</code> 标签(tag)。除非页面的主要语言超过两种。</li><li>使用 <code>lang</code> 和/或 <code>xml:lang</code> 属性(attribute)来指出语言的变化。比如 <code>span xml:lang="zh-TW"</code>。</li><li>不要用 <code>Content-Language</code> 来声明页面的默认语言,也不要用语言属性来声明主要语言 metadata。</li><li>不要在文档的 <code>body</code> 标签上声明语言。</li><li>HTML 中只用 <code>lang</code>,伺服为 <code>text/html</code> 的 XHTML 1.0 使用 <code>lang</code> 和 <code>xml:lang</code>,而伺服为 XML 的则只需使用 <code>xml:lang</code>。</li><li>如果属性值跟元素内容的语言不同,可以考虑使用 russian doll(一种 XML 组织模式?我也不懂)来处理。</li><li>对于一个多语言的页面,由你来决定是否在 <code>html</code> 中声明一种语言,或者不要定义它。</li></ul><p>另外,<a href='http://www.456bereastreet.com/archive/200509/declaring_languages_in_html_and_xhtml/'>456 Berea Street</a> 建议不要在 <code>DOCTYPE</code> 中更换语言,即不要改变//EN。</p>%1A%F2%13%E1%A0B%AD%B8%E9%B9%19%C08#%CF%BEHTML/XHTML 声明语言指南2005-09-13T10:00:00+08:00https://realazy.comRealazy<p>啥心情也没有,尽管终于把我妹妹的事情搞妥,顺利进入首师大。</p><p>周五晚上中介公司来封房,md,跟我合租那个鸟人不交房租。搞得我只好暂时龟缩在一<a href='http://humanitybook.com.cn'>朋友</a>家里。</p><p>房子一直是我在北京最头痛的事情,当然,如果我可以有钱点,或许不会这么狼狈。</p><p>我妹妹终于来上大学了,以后的生活费都归我管了 :O 。我一直想住到西三环去,因为不仅离她近,好照顾,而且我更喜欢大学环境,周末可以去看书,多爽……</p><p>可是,可是,那里房价不菲……唉唉,继续窝在乌烟瘴气的通州吧……给自己点信心,攒点钱再说……</p><p>继续找房中……</p>%9Ans0rF%C7%E6%91NX4房子啊房子2005-09-05T10:00:00+08:00https://realazy.comRealazy<p>我始终无法在 Quirks Mode 下工作,我无法掌控 CSS 在 Quirks Mode 下的表现。我对 Quirks Mode 敬而远之,遇上必须要用 Quirks Mode 的,我就叫其他人做,他们哗啦啦一堆表格就完成了,让我佩服之至,感叹不已。如果你对 web 标准有所了解,你应该知道这个 Quirks Mode 是什么东西。</p><p>但我今天要说的 Quirks,却跟 Quirks Mode 完全无关,我被 <a href='http://www.metaldudu.com/blog/'>MetalDuDu</a> 点名参与这个「病毒」游戏,相信许多朋友已经卷入其中了,那么我就不多作解释了。</p><p>怪癖?古怪的行为?古怪的性格?古怪的爱好,嗜好?似乎我都没有,我再也普通不过,我不古怪,除了有点孤独。你叫我写什么好呢?怪癖,不就是想证明自己特立独行而已么。我早已经对特立独行不感兴趣,虽然我真的可能有那么一点点。</p><p>我在前几天也看到某些人的「怪癖」了,竟然有人写在 ml 时喜欢怎样怎样,靠,这样的「怪癖」不是明摆着欺负我这个新世纪的纯洁处男么?#$%@#^%^%$^!43……算,考虑到我的读者有未满十八岁的,按住不提。</p><p>思考一个晚上,我还是凑不足 5 个能够称得上「怪癖」的东西。</p><h2 id="">惜书</h2><p>如果我的书不借给别人看,那么在握完整的读过几遍之后,他们还是跟新的差不了多少,除了封面的折痕,用个术语,叫做「九成九新」。我要求借我书的人都要把书<strong>摊</strong>开来看,而不允许<strong>折</strong>起来看。所以下次你跟借书看,我会很高兴如果你能跟我一样惜书。</p><p>甚至对报纸,我都不折起来看,成习惯了,唉。说到看报纸,我喜欢按照版面顺序看,不知道是不是怪癖呢 :P 。</p><h2 id="">极少看电视</h2><p>深信电视是垃圾出口站,这是我初中二年级得出的结论,但我在小学五年级基本上就停止了。尤其是电视剧,我更没有耐心看。我完整看过的电视剧没有一部,虽然反复看过《西游记》。到目前为止的生涯中,我集中看电视的时间大概只有大三的时候,看凤凰卫视资讯台那几个老头子和美女主播组合的节目还有李敖有话说。遗憾的是北京收不到这个凤凰卫视。</p><p>所以假如你问我看啥电视呀,我的答案可能是:牡丹牌,每天下班我都看它几眼呢。</p><h2 id="">极其关注台湾、香港</h2><p>我也不知道什么原因,总之我很关心这两个地方。澳门风平浪静,我没兴趣。我曾一度把人生寄托在这两个地方,最希望去的地方是台北,香港。这是我粤语不错的一个重要原因,想当年我还想念深圳大学的研究生然后找个机会去香港。毕竟是梦想。</p><p>我喜欢上这两个地方的网站,遗憾的是 GFW 太厉害,代理又不好找,我似乎又跟这两个地方隔绝了,再也难以看到东森上两岸三地的精彩(当然也有无聊的口水战)辩论了,遗憾。</p><p>随便告诉你我的理想吧:三民主义统一中国。我希望台湾能够做点实事,大胆西进,就当来解放我好了 :) 。</p><h2 id="">爱憎分明</h2><p>太明显了。这让我在做人方面看起来不是那么的通情达理,甚至带刺。</p><h2 id="">看美女</h2><p>从某种角度看,我是色狼,只不过有色无胆而已。</p><p>写到这里应该进行下一个程序了,即借机把「怪癖」传递下线。可是我想,已经没有必要了,现在已经到处泛滥,我似乎更愿意做一个「怪癖」终结者。</p>i%1Au3%D7t%EE%F9%B8m%1C)%B2%87J%DDQuirks?2005-08-30T10:00:00+08:00https://realazy.comRealazy<p>选择器,或者选择符,selector,是构建整个 CSS 的基础。</p><p>选择器,让我们可以样式化(X)HTML 的元素(<code>h1</code>, <code>p</code>, <code>ul</code>, <code>ol</code> 等)或者 XML 的元素(<code>BOOKTITLE</code>, <code>QUOTE</code> 等,具体看 XML 的元素);可以样式化 id(<code>#header</code>, <code>#content</code>, <code>#foote</code>r 等);可以样式类(<code>.warning</code>, <code>.more</code> 等);可以样式化伪元素(<code>:first-letter</code>,<code>:first-line</code>, <code>:before</code>, <code>:after</code> 等);可以样式化伪类(<code>:link</code>, <code>:visited</code>,<code>:focus</code>, <code>:hover</code>, <code>:active</code>, <code>:first-child</code>, <code>:lang()</code> 等);可以样式化伪元素(<code>:first-letter</code>,<code>:first-line</code>, <code>:before</code>,<code>:after 等)……</code>。</p><p>相信我上述所列举的选择器中,有几个你没有见过,就算见过就不知道怎么用,比如 <code>:first-child</code>, <code>:lang()</code>,我无权责怪你,但希望你能冲出 IE 的束缚,即使在支持上 IE 缺席,我们还是可以学习了解更多更强大的 CSS 选择器,这不仅对支持的浏览器有好处,对 IE 也有好处,比如可以做针对 IE 的过滤器(filter),想想我们现在的 web 世界,全靠 hacks , tricks and filters 来应付。不是有本书叫做:<a href='http://www.amazon.com/exec/obidos/ASIN/0764579851/102-5724850-6955352'>CSS Hacks and Filters : Making Cascading Stylesheets Work</a> 么,呵呵。</p><p>针对 CSS 2.x 的这些不常用(还是那句话,IE 支持的缺席使它们变得寂寞)但十分强大灵活得选择器,我将逐步讲述它们,今天,我要写的是,属性选择器,Attribute Selectors。</p><p>实际上,你不应该对属性选择器感到陌生,从本质上说,id 跟类选择器其实就是属性选择器,只不过是选择了 id 或者类的值(value)而已。</p><p>属性选择器的格式是元素后跟中括号,中括号内带属性,或者属性表达式(不知道描述是否正确,自创的词),比如 <code>h1[title]</code>, <code>h1[title="Logo"]</code> 等,你可以从我下面的论述中看到 4 种具体形式。</p><h2 id="">简易属性选择器</h2><p>只顾其名不顾其值,这是简易属性选择器的特点。</p><p><code>h1[class] {color: silver;}</code> 将会作用于任何带 <code>class</code> 的 <code>h1</code> 元素,不管 <code>class</code> 的值是什么。所以 <code><h1 class="hoopla">Hello</h1></code>、<code><h1 class="severe">Serenity</h1></code>、<code><h1 class="fancy">Fooling</h1></code> 的 <code>h1</code> 都会受到这条规则的影响。</p><p>当然,这个「属性」不仅仅是 class 或者 id,可以是该元素所有合法属性,比如 <code>img</code> 的 <code>alt</code>,这样 <code>img[alt]{css declarations here;}</code> 将会作用于任何带有 <code>alt</code> 属性的 <code>img</code> 元素。那么 <code>a[href][title] {font-weight: bold;}</code> 呢?聪明的你一定已经知道,这会作用于<strong>同时</strong>带 <code>href</code> 和 <code>title</code> 属性的 <code>a</code> 元素,比如 <code><a href="http://www.w3.org/" title="W3C Home">W3C</a></code>。</p><h2 id="">精确属性值选择器</h2><p>id 和类本质上就是精确属性值选择器,没错,<code>h1#logo</code> 等于 <code>h1[id="logo"]</code>。如前所述,我们不要局限于 id 或者 class,我们可以使用任何属性!例如 <code>a[href="http://www.w3.org/"][title="W3C Home"] {font-size: 200%;}</code> 将会作用于 <code><a href="http://www.w3.org/" title="W3C Home">W3C</a></code>。</p><h2 id="">部分属性值选择器</h2><p>如其名,只要属性值部分匹配(这里的部分,实际上要匹配整个单词)就会作用于该元素。让我们来看个例子:</p><pre class='html'><code class='html'><p class="urgent warning"> When handling plutonium, care must be taken to avoid the formation of a critical mass.</p></code></pre><p><code>p[class~="warning"] {font-weight: bold;} </code> 和 <code>p[class~="urgent"] {font-weight: bold;}中任何一条都可以让这个 ``p</code> 的字体变粗。</p><p>该选择器十分有用,比如你要样式化插图,其 <code>title</code> 中都含字符串"Figure",如 <code>title= "Figure 5:xxx 说明"</code>,则你可以使用 <code>img[title~="Figure"] </code>。</p><p>需要注意的是,如我第一句就强调的,你需要匹配的是整个单词,<code>img[title~="Figure"] </code> 不会匹配 <code>title= "Figure5:xxx 说明"</code>。</p><p>另外,我做了个小小的测试,你把例子中的"Figure「改成」插图",把 <code>img[title~="Figure"] </code> 改成 <code>img[title~="插图"] </code>,在 Firefox 中依然可以匹配,不管编码(encoding)是 GB2312 还是 UTF-8。看来 CSS 对中文的支持还不赖。</p><h2 id="">特殊属性选择器</h2><p>有点怪,这个选择器。它是这样工作的,嗯,举个例子比描述更容易。</p><p><code>*[lang|="en"] {color: white;}</code>,这条规则(rule)将会选择属性 <code>lang</code> 的值 <code>en</code> 或者 <code>en-</code> 打头的元素。就是说,它可以匹配 <code><h1 lang="en">Hello!</h1></code>、<code><p lang="en-us">Greetings!</p></code> 和 <code><div lang="en-au">G'day!</div></code> 而不匹配 <code><p lang="fr">Bonjour!</p></code> 和 <code><h3 lang="cy-en">Jrooana!</h3></code>。</p><p>说完了,呼……CSS 因你而强大,好好练吧。</p><p>参考文献:<a href='http://www.amazon.com/exec/obidos/ASIN/0596005253/qid=1124114266/sr=2-2/ref=pd_bbs_b_2_2/102-5724850-6955352'>Cascading Style Sheets: The Definitive Guide, 2nd Edition</a>。</p>%D5FC%16%86t%D80%DA%B7%8E)%90+%C6%B8更富弹性的 CSS 选择器——属性选择符2005-08-29T10:00:00+08:00https://realazy.comRealazy<p>最近觉得怎么 <a href='http://alistapart.com/'>A List Apart</a> 没动静了,在 <a href='http://blog.jjgod.org'>Web4C</a> 了解到,原来它 <a href='http://www.jasonsantamaria.com/archive/2005/08/23/a_list_apart_redesign.php'>Redesign</a>(重构?已经不是第一次了)了,升级为 ALA 4.0。由于 DNS 解析问题(jjgod 说的,我不知道)新版可以通过 <a href='http://alistapart.textdrive.com/'>http://alistapart.textdrive.com/</a> 来访问。</p><p>ALA 4.0 布局十分简洁:(点击看大图)</p><p><img src='/assets/missing.png' alt='' /></p><p>基本上就是一个 static 的一栏(navbar),float 定位的三列(content、secondary 和 sidebar),外加一个 absolute 定位的 masthead。因为使用到 float,为保证布局的稳定,三列都定了宽度,即这并非是流动(fluid)的设计,最佳浏览分辨率是 1024*768。</p><p>navbar 定义了 <code>min-width: 750px;</code>,但 IE 并不买帐,所以还有了 <code>overflow: hidden;</code>,很可怜,分辨率小的 IE 用户会损失后面的导航。</p><p>但是,分辨率小并没有影响到你的浏览体验,请看图:(点击看大图)</p><p><img src='/assets/missing.png' alt='' /></p><p><img src='/assets/missing.png' alt='' /></p><p>ALA 的三列 content、secondary 和 sidebar 的宽度加上相应的 padding 值明显经过精确算计,在小分辨率下对浏览体验的影响很小(我是指 1024*768 跟 800*600 是目前主流的情况下)。</p><p>最后,发现一个很酷的效果:</p><p><img src='/assets/missing.png' alt='' /></p><p>想知道怎么做吗?显然,你要对我嗤之以鼻了,呵呵,这么简单的效果你也敢来显摆?在此我提示一下,字体必须为 serif 字体,行高必须为 1,这样在放大缩小时,效果才不至于改变。</p><p>显然,ALA 4.0 的 CSS 并没有什么出众之处,但这并没有妨碍她设计的优秀。学院派的风格,易用的导航,易读的文章等等,在 web 标准之外,我们还需要学习更多东西。</p>%E4%8F%9Cb%15%DC%60%EC%B5rbJ%C0%B9%EA%CAALA 4.0 非专业分析2005-08-24T10:00:00+08:00https://realazy.comRealazy<p>一个网友给我留言:</p><blockquote><p>自己不能正视自己,心态失衡,又身无一技之长,还要在这里怨天尤人。我实在觉得你这人挺可悲的。</p></blockquote><p>没错,这位网友意见很中肯,其实我一直很可悲,只是不愿承认而已。我知道我只不过时来运转,才有了第一个工作机会(在此要感谢 <a href='https://www.dup2.org/qyb'>qyb</a>,真的,很感激)。</p><p>然后我觉得自己要干点什么了,不能再浪费时间了……</p>%C0%04%D8z%02i%5B%A3%E4f7e%15%E3T%0D我要做点什么了2005-08-22T10:00:00+08:00https://realazy.comRealazy<p>自从下厨来,有个问题我一直找不到良好的处理办法。</p><p>做饭吃,有时会有所剩余,即使不剩,也不能完全把锅里的米饭完全挖干净;做菜吃,有时也会有所剩余,即使不剩,有些青菜的枝叶在下锅之前会被清理出来……</p><p>你跟我说,好办,放进塑料袋,丢到垃圾桶去……</p><p>可是老大,你知道不?那是米啊,那是食物啊,人不能吃,那可以饲养动物啊。诺大的中国,每天得浪费多少啊……在农村,这很好处理,因为总会养些家禽家畜。但是在城里,该怎么办呢?难道丢到垃圾桶是唯一途径?</p><p>能不能用分类垃圾的办法呢?能,但好像我没看到过这样的垃圾桶,只看见过废旧电池回收桶。</p><p>那么能不能参照宾馆的模式?在每个生活小区设立一个专门回收食物垃圾的地方,居民可将食物垃圾归入,然后由某些需要的机构比如猪养殖场来收购,这样不仅可以干净地处理食物垃圾,还可以给社区带来收入,何乐不为?</p>s8%D3%82%F6%87%B6bNN%D1%14%9E%08%9D%C0食物垃圾该如何处理?2005-08-12T10:00:00+08:00https://realazy.comRealazy<p>好难找啊,所以放到这里整理一下。也好方便正在找的人。出自 <a href='http://gimp-savvy.com/BOOK/'>Grokking the GIMP</a>,基于版本 1.x,新版 2.2.x 有细微变化。等我熟练以后再对其中变化的作出说明。</p><p>S 代表 Shift,C 代表 CTRL。<strong>table B.1:</strong>The Toolbox FunctionsTool nameKeystrokeFull Path</p><p>airbrush aImage:Tools/airbrush</p><p>Bezier Select bImage:Tools/Bezier Select</p><p>Blend lImage:Tools/Blend</p><p>Bucket Fill S-bImage:Tools/Bucket Fill</p><p>Clone cImage:Tools/Clone</p><p>Color Picker oImage:Tools/Color Picker</p><p>Convolve vImage:Tools/Convolve</p><p>Crop & Resize S-cImage:Tools/Crop & Resize</p><p>Default Colors dImage:Tools/Default Colors</p><p>Dodge & Burn S-dImage:Tools/DodgeBurn</p><p>Ellipse Select eImage:Tools/Ellipse Select</p><p>Eraser S-eImage:Tools/Eraser</p><p>Flip S-fImage:Tools/Flip</p><p>Free Select (Lasso) fImage:Tools/Free Select</p><p>Fuzzy Select (Magic Wand) zImage:Tools/Fuzzy Select</p><p>Ink kImage:Tools/Ink</p><p>Intelligent Scissors iImage:Tools/Intelligent Scissors</p><p>Magnify S-mImage:Tools/Magnify</p><p>Move mImage:Tools/Move</p><p>Paintbrush pImage:Tools/Paintbrush</p><p>Pencil S-pImage:Tools/Pencil</p><p>Rectangle Select rImage:Tools/Rect Select</p><p>Smudge S-sImage:Tools/Smudge</p><p>Swap Colors xImage:Tools/Swap Colors</p><p>Text tImage:Tools/Text</p><p>transform S-tImage:Tools/transform</p><p>Xinputairbrush S-aImage:Tools/Xinputairbrush</p><p><strong>table B.2:</strong><br/>View Menu FunctionsFunction nameKeystrokeFull Path</p><p>Info Window C-S-iImage:View/Info Window</p><p>Nav. Window C-S-nImage:View/Nav. Window</p><p>Shrink Wrap C-eImage:View/Shrink Wrap</p><p>Toggle Guides C-S-tImage:View/Toggle Guides</p><p>Toggle Rulers C-S-rImage:View/Toggle Rulers</p><p>Toggle Selection C-tImage:View/Toggle Selection</p><p>Toggle Statusbar C-S-sImage:View/Toggle Statusbar</p><p>Zoom In =Image:View/Zoom In</p><p>Zoom Out -Image:View/Zoom Out</p><p>1:1 1Image:View/Zoom/1:1</p><p><strong>table B.3:</strong><br/>Select Menu FunctionsFunction nameKeystrokeFull Path</p><p>all C-aImage:Select/all</p><p>Feather C-S-fImage:Select/Feather</p><p>Float C-S-lImage:Select/Float</p><p>Invert C-iImage:Select/Invert</p><p>None C-S-aImage:Select/None</p><p>Sharpen C-S-hImage:Select/Sharpen</p><p><strong>table B.4:</strong><br/>File Menu FunctionsFunction nameKeystrokeFull Path</p><p>Close C-wImage:File/Close</p><p>New C-nImage:File/New</p><p>Open C-oImage:File/Open</p><p>Quit C-qImage:File/Quit</p><p>Save C-sImage:File/Save</p><p><strong>table B.5:</strong><br/>Edit Menu FunctionsFunction nameKeystrokeFull Path</p><p>Clear C-kImage:Edit/Clear</p><p>Copy C-cImage:Edit/Copy</p><p>Copy named C-S-cImage:Edit/Copy named</p><p>Cut C-xImage:Edit/Cut</p><p>Cut named C-S-xImage:Edit/Cut named</p><p>Fill C-.Image:Edit/Fill</p><p>Paste C-vImage:Edit/Paste</p><p>Paste named C-S-vImage:Edit/Paste named</p><p>Redo C-rImage:Edit/Redo</p><p>Undo C-zImage:Edit/Undo</p><p><strong>table B.6:</strong><br/>Layers Menu FunctionsFunction nameKeystrokeFull Path</p><p>anchor Layer C-hImage:Layers/anchor Layer</p><p>Merge Visible LayersC-mImage:Layers/Merge Visible Layers</p><p><strong>table B.7:</strong><br/>Image Menu FunctionsFunction nameKeystrokeFull Path</p><p>Duplicate C-dImage:Image/Duplicate</p><p>Offset C-S-oImage:Image/transforms/Offset</p><p>Grayscale a-gImage:Image/Mode/Grayscale</p><p>Indexed a-iImage:Image/Mode/Indexed</p><p>RGB a-rImage:Image/Mode/RGB</p><p><strong>table B.8:</strong><br/>DialogsDialog nameKeystrokeFull Path</p><p>Brushes C-S-bImage:Dialogs/Brushes</p><p>Gradients C-gImage:Dialogs/Gradients</p><p>Layers & Channels C-lImage:Dialogs/Layers & Channels</p><p>Palette C-pImage:Dialogs/Palette</p><p>Patterns C-S-pImage:Dialogs/Patterns</p><p><strong>table B.9:</strong><br/>Filter Menu FunctionsFunction nameKeystrokeFull Path</p><p>Re-show last a-S-fImage:Filters/Re-show last</p><p>Repeat last a-fImage:Filters/Repeat last</p>%A9%C2f~%9A%EA%85%B59%0C%E3#%DA(%F5%D3GIMP 默认快捷键2005-08-11T10:00:00+08:00https://realazy.comRealazy<p>唉唉,为了活命,我下厨了。</p><p>虽然已经工作近 5 个月 了,但来来回回学校,加上还债,我的薪水,像我喜欢的很多小 MM 一样,纷纷离我而去……最惨的是,我 5 个月的房租到离开才结算,啊啊,一下子就得拿出 3000 多块钱,刚好一发薪水就缴械……比最惨更惨的是,找到的新房,得交押金租金……啊啊啊,这个月我怎么过?麽办法,自己做饭去吧……</p><p>买了 10 斤米,应该够吃一个月了,呵呵,我胃口不大。太懒了,realazy == Real lazy,我在北京可还没有做过饭呢……第一次做,靠,米还是生的,只好复煮一次。我发现北方的米跟家里的不太一样,要煮更长时间,这次我在第三次做饭得到的经验。第一次,买了一点空心菜(我们家乡叫蕹菜),此菜在家可是到处泛滥的,想不到在北京竟然要买 1.7 元每斤,倒,啥东西一进京就飙价,希望首都也能飙点薪水给我,呵呵。这道菜我在家实在是做得太多了,我基本不费劲就完成了把它从塑料袋到盘中的转变,虽然后来发现忘了撒盐,嗯嗯……</p>f%B9%7BW%7B%16%9D%AB%15+%1FTL%F6qt下厨了2005-08-05T10:00:00+08:00https://realazy.comRealazy<p>试用了 IE 7 Beta1,然后又看了很多相关的 Blog,可以说让人失望之至。远的不说,IE 7 如果对 Web 标准还是那么蹩脚的话,我想,我的工作是干不下去了……</p><p>幸好,<a href='http://blogs.msdn.com/ie/'>IEBlog</a> 及时放出 <a href='http://blogs.msdn.com/ie/archive/2005/07/29/445242.aspx'>Standards and CSS in IE</a>,据称要修补以下 bugs:</p><ul><li>Peekaboo bug</li><li>Guillotine bug</li><li>Duplicate Character bug</li><li>Border Chaos</li><li>No Scroll bug</li><li>3 Pixel Text Jog</li><li>Magic Creeping Text bug</li><li>Bottom Margin bug on Hover</li><li>Losing the ability to highlight text under the top border</li><li>IE/Win Line-height bug</li><li>Double Float Margin Bug</li><li>Quirky Percentages in IE</li><li>Duplicate indent</li><li>Moving viewport scrollbar outside HTML borders</li><li>1 px border style</li><li>Disappearing List-background</li><li>Fix width:auto</li></ul><p>而且还要支持:</p><ul><li>HTML 4.01 ABBR tag</li><li>Improved (though not yet perfect) <object> fallback</li><li>CSS 2.1 Selector support (child, adjacent, attribute, first-child etc.)</li><li>CSS 2.1 Fixed positioning</li><li>Alpha channel in PNG images</li><li>Fix :hover on all elements</li><li>Background-attachment: fixed on all elements not just body</li></ul><p>这下,叫人怎能不期待呢?于是,我对我的未来又充满了信心 :) 。</p>%B8j%14ue%04%A6%1442E%C5%F7%ED&%96亲爱的 IE7,请不要让我失望2005-08-01T10:00:00+08:00https://realazy.comRealazy<p>从我的第一个 web 作品至今,大约已经有 4 年多了吧,但不怕见笑,老实跟你说,我的 Photoshop 水平仅止于打开,导出文件。我也不知道为什么,我曾经也买过 PS 方面的教材,但是就是学不会,我对它有抵触情绪,再说一次,我真的不知道为什么,我弄 Fireworks 比 PS 更顺手。</p><p>自从我使用 linux 那天起,我就发誓学好 GIMP,这个誓发了两年了,我还是在门外。因为我懒,也总没有机会,现在好了,我在 linux 的工作平台下,处理一些美工给的图片,慢慢摸出一些门道了。我总用 Fireworks 那种思维看待 GIMP,结果让我灰头土脸……唉,究其原因,我的美术理论不过关。</p><p>我决定好好看下相关书籍了,把 GIMP 学好。说不定等我学好了 GIMP,我成了一个 GNU Artist 也难说,呵呵……</p><p>今天用 GIMP 涂鸦,为了勉励自己,发上来自恋一下……</p><p><img src='/assets/missing.png' alt='' /></p>P%03%95%B0%5B%19%FBP%D4%EEz%E1%DFJ%96%7F我的第一个 GIMP 涂鸦2005-07-27T10:00:00+08:00https://realazy.comRealazy<p>一直不知道 Yahoo 可以 pop 收信,今天得知,一兴奋,使用 T<a href='http://www.mozilla.org/projects/thunderbird/'>Mozilla Thunderbird</a> 测试,灾难来了。</p><p>Thunderbird 默认从服务器端删除邮件的!就是默认害死我了。我用这个邮箱 4 年多了,就是说它从服务器删了我 1300 多封信!30 多 m 的容量……这下你叫我怎么备份!我从来都懒于备份的,在本地硬盘上的东西一般寿命不长……好好的服务器帮我管着多好!却要用 Thunderbird 来产生让我不快的灾难,唉……</p><p>怎么搞的,默认是删除邮件的啊?我去看看 Foxmail,默认设置也是这样……估计是以前邮箱容量有限,于是这些客户端自作聪明「人性化」地帮我们设置好了,唉,倒,晕,靠 % ^&! @ #) % % ^ & * ……如今邮箱哪一个不是 G 量级的?怎么不与时俱进一下?</p><p>这个该死的 Thunderbird 还没有多信导出的功能,我靠……害的我一封一封导出,累死我了……Thunderbird,拜托你以后不要默认删除服务器邮件了,就算不改,你加个导出功能行不行?</p>5D%96%8D6%87%BE%89%25%A2%F6P%98%D79%B9我的信啊2005-07-26T10:00:00+08:00https://realazy.comRealazy<p>终于交了定金,下周我就可以搬家了。</p><p>几乎操心了一个月,终于找到了。在这一个月中,我几乎翻遍了租房有关的网站,尤其跟「牛皮癣」网站诸如<a href='http://kijiji.com.cn'>客齐集</a>、<a href='http://post.sina.com.cn'>新浪招贴</a>、<a href='http://ganji.com'>赶集网</a>、<a href='http://naalee.com'>北京那里</a>打交道甚多。我推荐大家都到这些网站贴牛皮癣,以免影响市容。我就是你们的榜样,我没有在网络以外的空间贴过牛皮癣。而我也练就了一身好功夫,凡中介贴子我一眼就能辩出,哈哈,以后谁要找房可以叫我帮你辨别,以免浪费电话费。不过建议你,由于树大招风,这些网站中贴子多数为中介,我还是通过 Google 不经意在一个小网站找到的可靠信息。所谓踏破大站无觅处,柳明花暗小站中,一切心急不得,需待水到渠成。</p><p>房主是个中年人,起初我还心有隔阂,见面一交谈才知道,他心直口快豪爽坦率,实是与我臭味相投之我辈中人,哈哈,知己难觅。你能理解一个用了 800 租下来的房子跟别人合租却只要对方 380 块租金的人么?而且知道对方是两人时也不加价,理由却是:我住进来比较早。</p><p>我一想忌殚北京的房价,在我几乎绝望的通是竟然能让我找着这个这么便宜的地方:5 楼,装修不差带空调,厨卫中等。除了一台破电视外没甚家电,真合我意,家电对我来说简直是浪费。我一不看电视,二手洗衣服,三无东西可冷藏。一下子,我对未来生活充满了憧憬,哈哈,原来我还害怕我不多的工资会被租房剥得体无完肤,这下好了,尽管上班路程远些,但我终于可以对该死的钱有点交待了,省下的钱可以多给点我即将上大学的妹妹了,我的 wishlist 也可以早一点实现了……年轻人,多吃些苦头,呵呵。</p><p>忘了告诉你,我住的地方是,通州梨园小镇,轻轨终点站的倒数第三站所在地,从这里到王府井的路程比我家到县城的还长……</p>'%DChv%D6%B7a7%11%3CqW&Z%DB%B3终于找到房子了2005-07-24T10:00:00+08:00https://realazy.comRealazy<p>人民币升值了……</p><p>现在抓紧时间赚钱,出国就能换更多美元了……</p><p>但是为什么我去吃饭的时候,餐馆还是不降一分一毫呢?我想去问老板,喂,国家已经规定人民币更值钱了,为什么我买你相同分量的东西还是要一样的钱?</p><p>还有,我 5 个月没交房租了,算了算,欠了 3300 多块钱……我也想说,国家规定人民币升值了,能不能打个折,否则我这个月工资一下来就上缴,下个月怎么混啊?</p><p>人民币更值钱了,是不是说我以后就可以少花点钱干更多的事情了呢?我 <a href='/posts/2005-03-16-wishlist.html'>Wishlist</a> 中,有个显示器还没有实现啊……</p><p>我虽然手中使用的是人民币,而且国家也规定了它更值钱,但好像更我什么关系都没有……</p>r%B2z%D2%29%D6%7B%82g:%FB%9Cc%0C%F7f人民币升值了2005-07-22T10:00:00+08:00https://realazy.comRealazy<p>前不久看到在 <a href='http://w3planet.info/'>Web Standards Planet</a>(了解 web 标准最新动态,推荐这个网站)看到 <a href='http://particletree.com/features/quick-start-your-design-with-xhtml-templates'>Quick Start Your Design with XHTML Templates</a> 这篇文章,是有关 XHTML 跟 CSS 模板的。想想我初学时,在记忆方面费了不少劲,模板对初学者应该还是有用的。对于高手,我想没有必要了,而且这些模板的有些 XHTML 写法我也不是很认同。当然,对于那些一头雾水,能用软件捣鼓出 HTML 却连个 <code><p></code> 也写不出的人来说,也很有用。</p><p>顺便把这篇文章翻了下来,给初学者或者像我这样懒的人瞧瞧吧。顺便可以更新一下我那半年不动的首页了。</p><p>地址:<a href='http://realazy.com/archs/templates.php'>http://realazy.com/archs/templates.php</a>。</p>%B6%8C%D7%20%F0o%BA%98%00%BB2%7B%D8%8D%E0%A0给你模板,自己填空2005-07-18T10:00:00+08:00https://realazy.comRealazy<p>热啊,昨天傍晚吃完饭去双榆树小区公园的路上,不小心瞟了一眼阅报栏,曰:北京 1961 年来最热……想不到,北京如此「热情」地迎接我的到来……</p><p>公园是个避暑的好地方,老大爷在吹笛子,拉二胡,老太太配合着唱革命歌曲,而中心的舞场,脖子妞妞屁股妞妞……在这歌舞升平的公园,我找个角落,打开我手机的掌上书院,开始看《鹿鼎记》,唉,是了,我的破电脑还没有显示器,所以可以迷上金庸的武侠小说:我已经把《笑傲江湖》、《雪山飞狐》、《飞狐外传》和《倚天屠龙记》看完了……真是惬意啊,我这个手机,Nokia N Gage QD,人家用来玩游戏,当然我也玩,只不过我找到了适合我的阅读新方式,嘿嘿,这叫阅读无处不在。</p><p>可是回到住处,热啊!墙壁上挂着个空调,我敢打赌,从央视黄金广告上找到的任何号称最安静的空调都比不上它,因为……对不起,它是坏的。人云画饼充饥望梅止渴,可看到这空调我只会更热——我一团火气啊,1961 年来,34 年,半个甲子了啊,虽不千载难逢,但你也不应该在这个时候悄无声息错过这个难得的机会啊,我靠。虽然如此,嘿嘿,我练就的一声好功夫可不是吹的,羞答答的玫瑰能静悄悄地开,我也能汩汩冒着大汉一觉到天光,扇子也不用摇一下,何况我没有扇子这东西。</p><p>洗完澡,擦干水珠,拿过裤衩一套,哟,烤死我的两片小 PP 了,这不是刚从烤箱拿出来的衩衩吗?</p><p>北京的太阳出来忒早。在这个东西朝向的房间里,某个早上,不小心被太阳烤醒,哟,要迟到,拿过那个当闹钟用的手机一看,太夸张了,才 4:50……烤吧,你恶狠狠地烤,我继续睡我的觉……</p>%E76Q2%A3%7D%BE%C2%0C0%08%8A%95C%07%BF热啊2005-07-06T10:00:00+08:00https://realazy.comRealazy<p>今天去找房子了。</p><p>地铁到四惠,耳边响起"……终点站……",结果我下车处站才发现,还有一站到四惠东,害得我走路去,热啊,大汗淋漓,真想去告北京地铁,明明四惠东才是终点站,却在四惠时就误导我。到了四惠东,附近有一个什么花园小区,笃定住不起,跑去康家沟,唉,路也忒破,碰到雨雪天气肯定很难走。试探了一下,房租价格也不菲,走人……到通州北苑转一圈,不错,楼房蛮多的,不过却没有去找,因为……</p><p>昨天晚上在南昌大学北京校友群聊天,得知有两个大三校友要来北京实习,找不到房子,我「宅心仁厚」,答应了人家,可是,人家实习没有工资的,叫我尽量找便宜的。便宜?那就去通州啊,然而她们一人在朝阳左家庄京华时报,一个在崇文区体育馆路中国体育报,通州对她们来说交通实在不方便。</p><p>找来找去,还是觉得朝阳区劲松最合适。因此,我借用我的 Blog,按照<a href='http://beijing.kijiji.com.cn'>客齐集</a>或者<a href='http://post.sina.com.cn'>新浪招贴</a>的格式在此发布一下求租广告:</p><p>本人,男,22 岁半,今年毕业于南昌大学,在王府井上班,至少一年内工作稳定。无任何社会背景,不抽烟不喝酒,除了有点小脚气搔搔脚丫子外无不良嗜好。欲在朝阳区劲松及其附近租赁两居室,要求带厨卫,有床铺,能上宽带即可,一切为便宜着想,简单最好,租金在我们能够承受的人民币<strong>1200</strong>元以内。除了地下室,房型不限。</p><p>如有房子提供者,或者知情者,请电告 i_anzi 圈儿 yahoo.com.cn,或手机 13811407875 联系,本人江南雅名陈贤安。在免费的情况下,本人不谢绝而是大大欢迎中介,呵呵。</p><p><strong>2005-07-10 更新</strong>:两人已经找到房子,现在剩下孤零零的我了。我改要求了,现在我要租的房子,一居,带厨卫,能上宽带,在四环内,距离王府井车程在 1 小时内。</p>%F9U%06%AB%0E%9B求租2005-07-02T10:00:00+08:00https://realazy.comRealazy<p>图像替代,即用图像替代网页上出现的文字。为什么不直接插入图像非要搞个麻烦的图像替代?Web 标准的原则之一是亲和性( Accessibility),直接在网页中插入图像会导致一些残障人士所使用的阅读器不易获取图像上面的信息,且不利于 XHTML 的文档结构。</p><p>图像替代方法很多,现在也正被设计师疯狂利用。我推荐你阅读:</p><ol><li><a href='http://www.stopdesign.com/articles/replace_text/'>Using Background-Image to Replace Text</a></li><li><a href='http://www.kryogenix.org/code/browser/lir/'>A new image replacement technique</a></li><li><a href='http://phark.typepad.com/phark/2003/08/accessible_imag.html'>Accessible Image Replacement</a></li></ol><p>我推荐你都读一读,并且比较一下。Web 标准推崇 Accessible,因此我建议你使用<em>Accessible Image Replacement</em>所使用的方法,因为它不需要增加额外的 <code>span</code> 标签。我在平时制作中也是使用该方法。方便看见英文就头痛的读者,我举个例子简要说一下:</p><pre class='html'><code class='html'><h2 id="replaceText">这是一个标题文本,我们要用背景图像取代它</h2></code></pre><p>CSS 可以这样写:</p><pre class='css'><code class='css'>#replaceText { height: 图像高度; width: 图像宽度; text-indent: -9000px; background: url(bg.gif); ....;}</code></pre><p>看明白了吗?关键在于 text-indent,它把文本远远地甩在了 9000px 之外,如果你怕哪个 BT 的用户的分辨率可以达到 9000px,你还可以设置大一点的数值,呵呵。</p><p>但是,假如你还像我一样必须备受 IE 5.x 的煎熬的话,你会发现,该方法是用到的 <code>text-indent</code>,不仅仅把 text(文本)indent(缩进)掉,连背景图片也 indent 掉了!我靠,什么世道,这个什么破浏览器啊!呵呵,怨归怨,让我们来终结它吧,先 Kill Bill,然后……对不起,我扯远了。</p><p>怎么办呢?为了不破坏原有的 CSS 规则,我们必须有一套只能让 IE5.x 识别的解释规则。我所知道的方法有两种,一是注释式的 hack,例子如 <code>width /**/: 9px</code>,但是 Fx 也可以识别它,你只能在该条规则前加 <code>width: 12px!important;</code> 来让覆盖(参考阅读 Blueidea 上的 <a href='http://www.blueidea.com/bbs/NewsDetail.asp?GroupName=%CD%F8%D5%BE%D7%DB%BA%CF%D7%A8%C0%B8&DaysPrune=60&lp=5&id=1987632'>!important 和(空格)/\*\*/:的组合技巧及其他</a>和<a href='http://www.blueidea.com/bbs/NewsDetail.asp?GroupName=%CD%F8%D5%BE%D7%DB%BA%CF%D7%A8%C0%B8&DaysPrune=60&lp=5&id=1980631'>求救:/\*\*/是为了兼容那个浏览器的? 还有帮忙测试样式表</a>)。这样做起来不是不可以,就是忒麻烦,修行低一点的人难免会晕头转向。所以我的建议是,单独为 IE5 叉引入 CSS,是不是这样的话就得在 HTML 中添加代码?No!我们的 CSS 坦有一位叫做 Tantek 的 sBT,我们使用他的办法吧(参考阅读 <a href='http://jjgod.3322.org/2004/10/17/css-negotiation/'>CSS Negotiation</a>):</p><pre class='css'><code class='css'>/* IE5 */@media tty { i { content: ""; /*" "*/ }}@import "ie5.css"; {} /*";}}/* */</code></pre><p>这样我们只需要再写一个 ie5.css 就可以了,而且不必要打扰 HTML 代码了。唉,总算说到正题了,怎么让 IE5 叉在不改变原有代码的基础上也来 IR 一下?动手吧,看看我们在 ie5.css 中写些什么:</p><pre class='css'><code class='css'>#replaceText { text-indent: 0; font-size: 0; line-height: 0;}</code></pre><p>为了让被 indent 掉的背景回来,我们先把 <code>text-indent</code> 清零。看看后两句,呵呵,这是无耻啊,把字体大小跟行高设置为零,这样他们就,嘿嘿,虽然没有消失但也不见了……慢着,怎么还有几个小点?怎么办?再加一条 <code>color</code> 规则,值跟背景颜色值一致,OK,收工,搞定。</p><p>也许你会问,一开始就这样写不就什么都解决了么……唉,毕竟这是极度恶劣 BT 的方法,只能针对 BT 恶劣的 IE5 叉。对于优秀的浏览器,我们还是使用前辈总结好的优秀方法吧。</p><p>这里有一个例子,是 <a href='http://www.looho.com/'>aTo</a> 做的,IE5 叉的 CSS 是我所做。你可一下载过来研究研究,试着在 eyou_index.ie5.css 文件改名前后分别在 IE5 叉中预览。在这是一个即将上线的 eYou.com 首页,所以不得用于任何用途,呵呵。</p><p>下载:eyou.tar.gz(已失链)。</p>-%88%C2%E31,%FC%EC7MH%B4%18I%CFQIE 5.x 图像替代 hack2005-06-30T10:00:00+08:00https://realazy.comRealazy<p>玩 <a href='http://wordpress.org'>WordPress</a>(以下简称 WP)好久了,它的主题定制功能是很强的,尤其是 1.5.x 版,简直到了为所欲为的程度。</p><p>我个人的工作就是 web 标准,不为 WP 做个主题(WP 叫 Presentation)就太说不过去了。于是抽个时间,参考(或者叫抄袭吧)<a href='http://surfgarden.de'>surfgarden</a> 做了这个主题。</p><p>喜欢的下载吧,自由使用,自由负责。由于还是 0.1 版,细节处还待细修,我会慢慢修改到 1.0 的,呵呵。</p><p><strong>2005-07-07 更新</strong>:受到作者<a href='http://www.surfgarden.de/journal/2005_07/designklau.php'>指控</a>,即日起不提供下载。此主题涉及到版权问题,特向原作者 <a href='http://surfgarden.de'>Manuela</a> 道歉。Say Sorry to <a href='http://surfgarden.de'>Manuela</a>!</p>(?%12%D23%D0%BCc%1D%04%95r%20NS%F4SurfGarden Theme 0.1 发布2005-06-28T10:00:00+08:00https://realazy.comRealazy<p>最近把工作平台转移到 Linux 下,熟悉的 Slackware current,漂亮的 Dropline Gnome current(似乎我永远是 current 的使用者,呵呵)界面,让我工作效率狂飙,上周五买了个索尼的低档耳机(愤青别扁我啊,我只是路过看见就买了,不是日货支持者),我喜爱的 Beatles 开始为我歌唱,困了的时候让 MC Hot Dog 和 LMF 来吼几声,总之,哈哈哈小笑几声,工作时间不再发困打瞌睡了……</p><p>而我最心爱的(X)HTML, CSS 编辑器,<a href='http://bluefish.openoffice.nl/'>Bluefish</a>,在我饱受 Editplus 的蹂躏下,终于回来了!公司使用 CVS,工作区在某台服务器上。虽然 Bluefish 尚未支持 CVS,但支持存取(access)远程文件。Bluefish 官方网的说法是:</p><blockquote><p>Support for remote files using gnome-vfs (depending on your gnome-vfs setup, you'll have FTP, SFTP, HTTP, HTTPS, WebDAV, Samba and more)</p></blockquote><p>看,是不是比只能存取 FTP 的 Editplus 强悍得多呢?</p><p>如何让 Bluefish 存取远程文件,只要你编译的时候没有去掉 gnome-vfs 的支持,这是在是一件十分简单的事情。以我为例吧:打开 nautilus,Files -> Connect to Server...,选择 FTP(with login),填上所需登录信息,点击 Connect 后,你会发现桌面上有一个连接到 FTP 的图标了,此时填上 FTP 的登录密码,nautilus 已经进入 FTP 的目录中……</p><p>打开 Bluefish,按正常方式打开文件,你会发现 nautilus 的文件选择器(File Selector)有了 FTP 的选择,展开目录,选择你需要的文件编辑吧!</p><p>小技巧:如果要编辑的文件位于目录层深层,不妨设置为快捷方式(不知道表述是不是有问题),在文件选择器选中该目录时,点击左边下方的 Add 即可,下次不必再辛辛苦苦展开了。嗯,Nautilus 的这项功能我喜欢极了……</p><p>本人口才文笔均有限,如果您看不懂,看看这个 Flash 格式的教程吧:<a href='http://www.borgerding.org/dropline/trovao/videos/e-remotely/'>Editing files remotely with bluefish</a>,是以 SSH 为例的。</p>1%97k%5Elg%99%D2A%1C@'%846u(用 Bluefish 编辑远程文件2005-06-27T10:00:00+08:00https://realazy.comRealazy<p>我终于明白了孔子两千多年前的智慧:父母在,不远游。其实父母二字,只是使用了某种修辞手法,它实指你的亲人。</p><p>2005 年 5 月 30 日上午 10 时,其时我尚在北京焦急地等待下午 4 时才开的 T5 列车,就接到爸爸给我的噩耗:爷爷已经安详而去,终没能让我见到最后一面,人生从此多此一憾。只恨列车不能飞,我 31 号晚上 8 点到南宁,太晚,已经没有回去的车,只好花 400 多块请了 Texi 狂奔 200 多公里,终于赶上爷爷斋事的最后时刻,心中才稍感安慰。</p><p>很后悔自己当初没有考虑飞机方案,有点恨自己有钱就花光没有后路,两手空空,才在 29 号晚上接到消息竟一时措手。爷爷已高龄八十有五,我的稍稍犹豫造成了我的终生之撼。</p><p>斯人已去犹忆斯影,爷爷是共产党员,很纯的那种,在当村长时为村民做好事甚多,口碑甚好。一生勤奋,耕种总是快人一步,收成总是多人数石。爷爷最疼得就是我,我跟爷爷生活了很长时间,小时经常帮他放牛,他也总从拮据的手中拿出零钱零食,让我今天牙齿参差不齐。</p><p>天妒好人,1996 年爷爷脑血管意外,半身瘫痪,生活不能自理,我最可敬的奶奶护理了他 10 年。每逢放假回家,我便让奶奶休息,给爷爷喂饭,洗澡,清理排泄物等等,因为奶奶年老体迈,护理之事比不上我的程度,爷爷便每每思念我放假之期,一天一天算得清清楚楚。今年春节确实最后一次了,但爷爷知道我在北京找到了工作,对他一农民来说,总算也是光宗耀祖之事了。这也是他最为关心的事情,当年他希望我能上大学,希望我能做官,所以我就读行政管理,但我性格毕竟不符尔虞我诈之道,辜负了他的一片期望。不过知道我在北京工作,工资对农民来说也算是天文数字了,终于安心而去。我所见到的他最高兴的时刻有四次,分别是上重点初中,重点高中,上大学,找到工作……他一路看着我成长。然而最后一刻竟成撼事……</p><p>我需在家里守孝七天,终因学校论文答辩之故提前离家到校,终于明白,人在江湖身不由己之理。爷爷,你安息吧,我决计不会辜负你,做一个堂堂正正的人。</p>h(N%BC%1E%9F%DA%05%B9G%93/L%96%C5%A4爷爷,请你安息2005-06-07T10:00:00+08:00https://realazy.comRealazy<p>昨天下午下班回家,路经工体,看见蜂拥的人群,方想起,是 Beyond 来开演唱会。我听说,还是告别的。</p><p>我是 Beyond 的歌迷。真正的歌迷,不是那种只听黄家驹那几首经典歌曲就整天哼哼的伪歌迷。我虽然喜欢黄家驹在世时所创作的大部分歌曲,但如果要比较的话,我反而更喜欢 1994-1999 时期三人组合时代的歌曲。解散以后,黄贯中的大部分专辑我也很喜欢。他们在失去家驹以后依然努力,可以说没有放弃理念,也没有放弃理想。看到今天他们开演唱会,大部分只能唱家驹的经典歌曲,难免让歌迷产生他们专吃家驹精神遗产的错觉。这种演唱会我是不去的,因为在 1991 年在香港红勘家驹已经发挥到极致,此后再也无法超越。除非他们也唱没有家驹时所创作的歌曲,比如 1996 年的 Live&Basic,我想我还会去,即使我家徒四壁。</p><p>即使如此,Beyond 在内地开唱,多半是走穴,完全没有在香港的激情,明显就是不把内地歌迷当回事。香港有高水平的歌迷,内地也不少。假如你对比一下 2003 年香港跟北京的演唱会,你能明白我说什么。</p><p>有人说,家驹走了,Beyond 也名存实亡了,只能吃家驹在世所创的名气剩饭了。没错,很多伪歌迷就冲着家驹去的,而不是 Beyond。很遗憾的是,家驹过世后,Beyond 的努力一直得不到市场认可,从唱《较坏细路》不满某电视台,到用蹩脚的国语希望在台湾、内地发展,无一惨败。三人也不好好协作,似乎是各唱各的,没有统一的乐风。此期间,叶世荣也偶尔有作品,再我看来虽不属经典,但也是精心之作,尤其是<em>love</em> 的演唱会版本,吉他的演奏十分动人。三子恨努力,但只有越来越少的老歌迷捧场,Beyond 说的「我们在香港没有什么地位,我们有的是哥迷」是一句不折不扣的大实话。Beyond 已经难以再回到 1991 年作为第一支进占香港红馆的摇滚乐队的辉煌。</p><p>为什么呢?因为家驹实乃天才,可以把音乐做到雅俗共赏,失去家驹后,三子欲俗不能,最后只能剩下我们这般死忠歌迷。估计三子生活拮据,只好拿着家驹的经典之作重新演绎,算是养家糊口吧。我可以原谅这样的做法。</p>%8B%06%83%1A*%A5f%19%AFn%9E%9C@%F6%91%A3Beyond 的谢幕2005-05-28T10:00:00+08:00https://realazy.comRealazy<p>Gnome <a href='http://developer.gnome.org/projects/gup/hig/'>HIG</a>,Gnome Human Interface Guidelines,国内有人译作人性化界面指南(<a href='http://www.gnome-cn.org/newsitems/news_item.2004-07-29.4772696863/view?searchterm=HIG'>http://www.gnome-cn.org/newsitems/news_item.2004-07-29.4772696863/view?searchterm=HIG</a>),现在版本是 2.0。Gnome 桌面系统一直遵循这个原则,这是我至今一直使用它而不是其他桌面坏境如 KDE 的原因。</p><p>我打算通读 HIG,用以指导我的 web 标准工作。我建议所有做 web 设计的都来读一读。HIG 第一章 Usability Principles 阐述的就是我要努力的方向:</p><ol><li>为人设计:用户是谁,希望他们能干什么</li><li>不要局限你的用户基群:可用性,国际化和本地化</li><li>为你的程序和真实世界建立纽带</li><li>程序保持一致性</li><li>告知用户</li><li>简洁而完美</li><li>让用户有控制权</li><li>宽恕用户</li></ol><p>我认为 HIG 跟 web 标准是一脉相承的,aaa,508 等都是这些原则的具体化。web 标准对传统的设计的挑战之一就是:优化的人性化界面,或者说,人机界面。</p>%91%07%FF%A0%FE%DB%CF.%19L%E7%FC%86E%B2+用 Gnome HIG 原则指导你的设计2005-05-18T10:00:00+08:00https://realazy.comRealazy<p>记住,在设计的时候不要给 id 赋予 <code>day</code> 的名字,在 Firefox 下正常,但 IE 却解析错乱。至今发现的问题是,会导致连接不正常。比如这段代码:</p><pre class='html'><code class='html'><li><a href="calendar.php" id="day">日</a></li></code></pre><p>本来连接应该是 <code>calendar.php</code>,但是,在 IE 下,我的测试是奇怪的 <code>32</code>,不知何故,只能解读为 <code>day</code> 是 IE 的保留字,或者说是一个 bug。谁能帮我向 MS BugZilla 一下呢?我希望 IE7 不会出现这个问题。</p>H%97%FD%C3%C5%AA$%E3%FC%F0%F6%F2P%CD%E3%18day 是保留字2005-05-18T10:00:00+08:00https://realazy.comRealazy<p>CTO 去美国,叫 <a href='http://dev.eyou.com'>qyb</a> 托他从美购回两本书,<a href='http://www.amazon.com/exec/obidos/tg/detail/-/0321303474/qid=1115688243/sr=8-1/ref=pd_csp_1/104-9251932-6743162?v=glance&s=books&n=507846'>The Zen of CSS Design : Visual Enlightenment for the Web (Voices That Matter)</a> 和 <a href='http://www.amazon.com/exec/obidos/tg/detail/-/0596005253/qid=1115688243/sr=8-2/ref=pd_csp_2/104-9251932-6743162?v=glance&s=books&n=507846'>Cascading Style Sheets: The Definitive Guide, 2nd Edition</a>,这下可以好好研究一番了。qyb 还跟我说要合作翻译出一个地下版本,呵呵。</p><p>在此对 qyb 表示感谢!我要努力工作了,多出精品!</p>5%C1%1B%18%1F2,%9C%0C%10~V%DA%60%94%BD公司买了两本书2005-05-10T10:00:00+08:00https://realazy.comRealazy<p>上午无意中得知 Google PR 又「异动」,上去看看我的「[传承标准]」,呵呵,我的 Firefox 扩展 <a href='https://addons.update.mozilla.org/extensions/moreinfo.php?id=262'>Google Pagerank Status</a> 显示还是 n/a。</p><p>以前我很在意这个东西,原因当然是想提高访问量了。为什么要提高访问量呢?因为上面有 <a href='https://www.google.com/adsense/?hl=zh_CN'>Google AdSense</a> 啦。为什么有 Google AdSense?想赚点小费买域名很空间啦(呵呵,别笑我 60 元的域名都买不起哦,穷学生啦。现在好了,有工作了,一些必须靠钱的事情慢慢进行中……)。到处宣传,结果 Google 在我的帐户为 49 美元的时候封杀掉,心痛了好几天呢!只好在 51 上的空间过期后转移到 omemo.net 上(感谢 <a href='http://www.omemo.net/porklog/'>PorkFat</a>),过一段寄人篱下的日子先。</p><p>自此之后,我对 PR 不在意了,Web 标准这东西,怎么说都是一个小圈子,你能巴望什么访问量?有人访问,有人留言就好,随缘啦。</p><p>可是,可是,在看我的 Blog 的时候,PR 却是 3 啦?不信,应该是 cache 吧?在 IE 中装上 Google Toolbar 验证,呵呵,是真的。也没有多大的兴奋,成就感还是有一点点的(老了,不轻易激动了)。证明我的 Blog 已经被关注,以后得注意点才对得起读者了。为别人而写作是一件特累的事情,对不起,我还是保持我的风格。</p><p>在此感谢各路朋友,没有你们就没有我今天。谢谢!</p>t%A6%E5%9B%8E%B7%8E%7FM,,%91%F5~%EBVBlog 的 PR 值由零升三2005-04-22T10:00:00+08:00https://realazy.comRealazy<p>不好意思用了英文标题,这样给人会很玄的感觉。我一直是极力反对乱用英文的,因为有人跟我说过,你在中文上面写英文干什么呢?中国人看不懂英文,老外看不懂中文,何苦呢?今次使用,以便能使这篇 blog 能够有个调侃的开头,下不为例。</p><p>做 web 标准工作,我的建议是,不要考虑浏览器差异,先保证 Firefox 的解析正确再细调其他浏览器的差异。所以我推荐几个我比较顺手的有关 web 设计与开发(Designing & Developing )Firefox 扩展( Extensions)。</p><ol><li>我做网页,因为是属于重构工作,所以得经常从别人的网页或者图片中取色,这就要用 <a href='https://addons.update.mozilla.org/extensions/moreinfo.php?id=271'>ColorZilla</a>,一款十分强大的取色器,前景、背景颜色或者图片颜色不再话下,操作也很方便,而且提供了五种格式的颜色命名,呵呵,直接可以烤到 CSS 文件中去了。很容易上手,使用我就不多说了。</li><li>你还每次到 <a href='http://validator.w3.org'>W3C Validator</a> 去校验?还不如装上 <a href='https://addons.update.mozilla.org/extensions/moreinfo.php?id=249'>Html Validator (based on Tidy)</a> 呢。在预览页面的时候,<code>CRTL</code> + <code>U</code> 一下,错误(error)或者警告(warnings)一目了然,还提供帮助(help)让你参考,还爽不歪你?::D</li><li><a href='https://addons.update.mozilla.org/extensions/moreinfo.php?id=60'>Web Developer</a>,这个不用多介绍了吧?</li><li><strong>2005-07-14 更新</strong>:<a href='https://addons.mozilla.org/extensions/moreinfo.php?id=539'>MeasureIt</a>,测量页面距离,忒实用。</li></ol><p>题外,我还使用的扩展有:</p><ol><li><a href='https://addons.update.mozilla.org/extensions/moreinfo.php?id=398'>ForecastFox</a>,天气助手,呵呵,出门就靠它了。</li><li><a href='https://addons.update.mozilla.org/extensions/moreinfo.php?id=173'>Gmail Notifier</a>,我的 Gmail 一有邮件就提醒,点击直接进邮箱,呵呵,比即时聊天工具(IM)还管用,恰好公司不允许上 IM,但我上邮件没有问题吧?:D</li></ol><p>你还用什么很酷的扩展呢?不妨介绍一个让我爽爽,谢谢了!</p>%FF%ED%F5%18%86o%10%C7%BB%BD%E5%CE%91%A3%CF%D5Designing & Developing with Firefox Extensions2005-04-20T10:00:00+08:00https://realazy.comRealazy<p>来北京一个多月,首次在京遭遇「乞助」——回家路上,即人大东门对面,一年轻女孩,衣裳并不褴褛,上前请求我是否可以带她到哪儿吃点东西,说已经日不进米了……如果换成两年前,不,一年以前我都如其所愿,或者给其一顿饭钱——一年前我在南昌就做过这样的事情,不止一件,是两件。</p><p>现在我没有犹豫多少,我叫她打 110 找警察,她头也不回迅速离去。我不明白事情到底是不是真的,如果是真的,我或许,在最轻程度上给人一顿饱,最重程度上救活一个人……决不应该吝惜十几块人民币,就算是假的,就当不小心遗失好了。是的,这么多年来我都是这样想的,人家已经放下自尊,真假都只是十几块人民币而已。可现在,我不知道怎么了,我的同情心喂狗去了?</p><p>可是我受到的骗局不止一两次了。2002 年暑假返校,我在南宁火车站的一次受骗经历让我终生难忘。一个 15 岁左右的女孩,说是湖南来南宁就读中专,下火车时被人偷窃,身无分文,让我支助她一晚。很清纯的眼神,我压根不会把她想象为骗子。她给了一个所谓叔叔的电话让我打过去以示受害,谁想到这也是骗局呢?可我就信。我带她到一间旅社开了房间给她,还帮她垫了身份证登记,并带她吃了饭,走时还买了一瓶饮料给她。可以说世间好事我都做得差不多了。第二天上火车之前我特意跑去找她,担心她「叔叔」是否已经找到她。谁知道,到达那个旅社,服务员泼给冷水一盆:"你走了不久她就退房拿钱走了……"</p><p>我没有为那上百块钱伤心,虽然那时我还是拿农民父母钱的穷学生。我心痛我的同情心,比喂狗了还惨。</p><p>此之前,我还是一个很好很好的人,我上街总不忘给老乞丐的乞食「衣钵」投零钱,以致于受到 BBS 上网友的嘲笑:你每次上街最好多找些零钱……</p><p>此之后,我还是一个很好的人,因为我还是不忘投零钱,但次数越来越少,尤其是,我发现,很多人不惜冬天光膀子乞食、毁容等自虐行为来达到某一种目的。我越来越心痛,不是那区区几块钱,是我的心,我甚至怀疑,我是不是在纵容某种自虐行为,纵容某种不劳而获(在此我不是指那些失去劳动力者),纵容一种伤害自己同情心的行为。我越来越麻木。</p><p>或许我在找借口,我也无法完全原谅自己的行为。</p><p>我想,面对这么多的乞讨者(南昌实在太多,北京似乎少点),为何不从政府、社会的角度来思量一下解决方案?当政府一概不理或者把乞讨者从一个地方运到另一个地方,我知道,不是我没有同情心,是政府没有同情心。政府的同情心喂狗去了——这是正解。</p><p>越写越离题了,我好像是在写骗子问题。归根,还是政府的社会保障做得不够,一部分生活在底层的人,由于各种原因自己无法养活自己,政府又不理他们,只好乞讨,只好行骗。写到这,我忽然明白,骗子应该骗钱,一顿饭应该不屑。我开始为今晚的草率决定懊恼,她可能真的就需要一顿饭而已,我不应该拒绝。我已经叫她报警,可是,就算她找到了警察,警察会帮助她吗?</p>aj%9F#5%8E5%FE同情心喂狗去了?2005-04-15T10:00:00+08:00https://realazy.comRealazy<p>先来看我一个简单 XHTML/HTML 文件代码(部分),我们的目的是让 <code>#container</code> 水平居中。</p><pre class='html'><code class='html'><body> <div id="container"> <h1>content</h1> <p> Lorem ipsum dolor sit amet, consectetuer adipiscing elit.Phasellus varius eleifend. </p> </div></body></code></pre><h2 id="auto-margin">使用自适应边界(auto margin)</h2><p>水平居中任意元素的首选办法是使用边界(<code>margin</code>)性质(property),并把左右之值设置为 <code>auto</code>。但你必须为 <code>#container</code> 指定一个宽度。</p><pre class='css'><code class='css'>div#container { margin-left: auto; margin-right: auto; width: 168px;}</code></pre><p>这个方案在任何<strong>当代</strong>浏览器上都有效,即使是 IE6,前提是在 web 标准兼容模式下(compliance mode)。不幸的是,它不会在先前版本的 IE/Win 中工作。我们为此列一个表格:</p><pre class='html'><code class='html'><table> <thead> <tr> <th colspan="3">浏览的自适应边界支持一览表</th> </tr> </thead> <tbody> <tr> <th>浏览器</th> <th>版本</th> <th>支持</th> </tr> <tr> <td>Internet Explorer</td> <td>6.0, compliance mode</td> <td>是</td> </tr> <tr> <td>Internet Explorer</td> <td>6.0, quirks mode</td> <td>否</td> </tr> <tr> <td>Internet Explorer</td> <td>5.5 Windows</td> <td>否</td> </tr> <tr> <td>Internet Explorer</td> <td>5.0 Windows</td> <td>否</td> </tr> <tr> <td>Internet Explorer</td> <td>5.2 Macintosh</td> <td>是</td> </tr> <tr> <td>Mozilla</td> <td>所有当前版本</td> <td>是</td> </tr> <tr> <td>Mozilla Firefox</td> <td>所有版本</td> <td>是</td> </tr> <tr> <td>Netscape</td> <td>4.x</td> <td>否</td> </tr> <tr> <td>Netscape</td> <td>6.x+</td> <td>是</td> </tr> <tr> <td>Opera</td> <td>6.0, 7.0 Macintosh and Windows</td> <td>是</td> </tr> <tr> <td>Safari</td> <td>1.2</td> <td>是</td> </tr> </tbody></table></code></pre><p>尽管受到浏览器支持的限制,大部分设计师还是提倡你尽可能这样做。但我们依然可以使用 CSS 应付一切情况。</p><h2 id="text-align">使用文本排列(<code>text-align</code>)</h2><p>此方案需要使用到 <code>text-align</code> 性质,应用给 <code>body</code> 元素并且赋予 <code>center</code> 的值。</p><pre class='css'><code class='css'>body { text-align: center;}</code></pre><p>它公正地对待各种浏览器,十分彻底,唾手可得。然而,这是赋予文本的性质,它使 <code>#container</code> 中的文本也居中了。所以,在布局上我们还得做一些额外工作:</p><pre class='css'><code class='css'>div#container { text-align: left;}</code></pre><p>这样才可以把文本的对齐方式返回默认状状态。</p><h2 id="">综合边界和文本排列</h2><p>因为文本排列向后兼容,当代浏览器也支持自适应边界,很多设计师把他们结合起来,实现跨浏览器使用。</p><pre class='css'><code class='css'>body { text-align: center;}#container { margin-left: auto; margin-right: auto; border: 1px solid red; width: 168px; text-align: left;}</code></pre><p>唉,依然不完美,因为还是一个黑客技巧 (hack)。你不得不为文本排列写下多余的规则。但现在,我们可以使用更完美的跨浏览器的方案。</p><h2 id="">负边界解决方案</h2><p>此方案得结合使用绝对定位(absolute positioning )。首先,把 <code>#container</code> 绝对定位并左偏移 50%,这样,<code>#container</code> 的左边界就是页面分辨率的一半。下一步,把 <code>#container</code> 的左边界设置为负值,值大小为 <code>#container</code> 宽度(width)的一半。</p><pre class='css'><code class='css'>#container { background: #ffc url(mid.jpg) repeat-y center; position: absolute; left: 50%; width: 760px; margin-left: -380px;}</code></pre><p>看,没有任何黑客技巧(no hacks)!连 Netscape 4.x 都支持!</p><p><strong>2005-08-12 更新</strong>:此方法在 IE 下会导致不能使用鼠标选择某个区段的元素,注意注意!</p><p>抄袭文献:<a href='http://www.amazon.com/exec/obidos/tg/detail/-/0321303474/ref=pd_wt_2/104-5778529-9599942?coliid=IBZP0900M3B9T'>The Zen of CSS Design</a></p>+h%DE%8D%11%08%F7Ud%92%C4%12%CE%E0P%9B元素水平居中方案总结2005-04-13T10:00:00+08:00https://realazy.comRealazy<p>终于等到了这一天。Web 标准爱好者抓紧时间下载哦。</p><p>在此只是提供一个下载地址,害怕侵权,大家不要太张扬 :)</p><p>(我已把下载地址去掉,并在多年后认识到自己传播盗版,是非常错误的行为)</p>%22%AB%EA%AD%1A%CE%8C%AA~%EBhi%DE&%9D%85激动人心,The Zen of CSS Design 下载!2005-04-11T10:00:00+08:00https://realazy.comRealazy<p>进入 <a href='http://dev.eyou.com'>eYou.com</a> 后,马上就给新版的邮件界面转化成 XHTML+CSS 的工作,还好平时基本功还够扎实,有条不紊的干了下来。当然会遇到新的问题,比如,平时做网页,因为没有跟程序打过什么交道,较少使用表单。还好,世界还有 Google,让我可以轻松应对新挑战。一些经验,写出来大家分享。</p><p>基于易用性(accesibility)的考虑,表单的标准写法应该在 <code><form></code> 和 <code></form></code> 之中包含 <code>fieldset</code> 和 <code>legend</code>(说明),让用户明白该表单域的内容概要。简单的结构如下:</p><pre class='html'><code class='html'><form> <fieldset> <legend></legend> …… </fieldset></form></code></pre><p>在某些场合或许你不愿意让也许 <code>fieldset</code> 和 <code>legend</code> 影响你的设计方案中的美观,好办,在 CSS 中把 <code>fieldset</code> 的 <code>border</code> 设置为 <code>0</code>,<code>legend</code> 的 <code>display</code> 设置为 <code>none</code> 就行了。</p><p>在绝大多数情况下,表单的布局分两列,左边是标记(<code>label</code>),右边是输入框(<code>input type="text"...</code>)。如此简单的两列布局,我强烈建议不要使用表格。参考 <a href='http://stylephreak.frogrun.com/uploads/source/cssform.php'>http://stylephreak.frogrun.com/uploads/source/cssform.php</a> 和 <a href='http://www.aplus.co.yu/css/forms/?css=1'>http://www.aplus.co.yu/css/forms/?css=1</a>(绝对有价值的两个参考,你已经可以不必往下看了),我们发现,Web 标准通用的解决方法是,为 <code>label</code> 和 <code>input type="text"...</code> 的外围加上一个 <code>div</code>,并把把该 <code>div</code> 的 <code>display</code> 设置为 <code>block</code>。把 <code>label</code> 设为 <code>float: left;</code>(这也是要把 <code>div</code> 设置为 <code>display: block;</code> 的原因)之后就可以让标记跟输入框同一行上了。让 <code>label</code> 对齐的一个小窍门是,固定 label 的宽度,然后根据需要使用 <code>text-align</code> 向左或者向右对齐。设定宽度的小窍门是,使用单位 <code>em</code> 根据标记的最大字数来定宽度,不必辛苦测试 <code>px</code>。</p><p>为了使我的阐述更容易理解,我简单写些代码:</p><p><strong>XHTML</strong>:(部分)</p><pre class='html'><code class='html'><form> <fieldset> <legend>表单实例</lengend> <div><label for="name">姓名:</label><input type="text" id="name" /></div> <div><label for="etc">其他等等:</label><input type="text" id="etc" /></div> <div class="submit"><input type="submit" value="提交" /></div> </fieldset></form></code></pre><p><strong>CSS</strong>:(部分)</p><pre class='css'><code class='css'>body { /*跟表单无关,设置页面的显示效果*/ width: 400px; margin: 20px auto; font: 14px/1.5 Serif;}fieldset { border: none; border-top: 1px solid #ccc;}legend { padding: 2px; border: 1px solid #ddd; background: #ececed;}div { display: block; padding: 5px 0;}label { float: left; width: 6em; text-align: right;}.submit { margin-left: 6em;}.submit input { padding: 2px; border: 1px solid #ccc; background: #ececec;}</code></pre><p>查看效果(已失链)。这只是一个极其简单的例子,你完全可以根据这样的思路来做出各种复杂的效果。我上面列举的两个连接本身就是极好的演示。</p>%9Cyb%5E%1E%D7y%C7%EE%9E%B1?D%8F%03%7F表单的 Web 标准解决方案2005-04-07T10:00:00+08:00https://realazy.comRealazy<p>一直想买 The Zen of CSS Design : Visual Enlightenment for the Web,无奈买了个手机以后手头骤然紧张,这个月吃饭都成了问题……只好等下一轮工资周期再作打算了。</p><p>Google 一番,找到了这本书的两个章节,呵呵,正在工作中,还没有时间细看。先提供给各位下载,一睹为快。有时间的话说不定我会翻译送上的哦……</p><p>突然发现 omemo.net 的 FTP 我登陆不上了,只好先传到 51.net 的空间上,这个空间 4 月 10 日到期,大家抓紧时间啊 :)</p><p><a href='http://neoone.51.net/jump.pdf'>Door to my garden</a>,还有 <a href='http://neoone.51.net/DesignType.pdf'>Blood Lust</a>。</p>%DBkOt%3E%CB0%A9%E5%A8%14%14%A2l%C93The Zen of CSS Design 的两个章节下载2005-04-05T10:00:00+08:00https://realazy.comRealazy<h2 id="head">表头(head)</h2><p>在构建数据表的时候利用表头是很重要的。不使用直觉的标签而代之以如 <code><b></code> 这样的过时标签来在视觉上暗示用户这个单元格是重要的,反之我们可以利用 <code><th></code> 标签的优点,跟我们使用 <code><h1></code> 这类的标题很类似。</p><p>可视化浏览器可能会以粗体来处理包含在 <code><th></code> 标签中的内容,但我们仍然可以用 CSS 样式化这个唯一的 <code><th></code> 标签,凸显该重要的单元格与其它包含在<td>中的表格数据的不同之处。</p><p>我们的表格例子中表头分为 4 组:Year, Opponent, and Season Record (W-L)。我们使用正确的头来取代先前表现性的标记:</p><pre class='html'><code class='html'><table summary="This table is a chart of all Boston Red Sox World Series wins."> <caption> Boston Red Sox World Series Championships </caption> <tr> <th>Year</th> <th>Opponent</th> <th>Season Record (W-L)</th> </tr> <tr> <td>1918</td> <td>Chicago Cubs</td> <td>75-51</td> </tr> <tr> <td>1916</td> <td>Brooklyn Robins</td> <td>91-63</td> </tr> <tr> <td>1915</td> <td>Philadelphia Phillies</td> <td>101-50</td> </tr> <tr> <td>1912</td> <td>New York Giants</td> <td>105-47</td> </tr></table></code></pre><p>使用 <code><th></code> 标签来标记头部单元格可以得到跟使用 <code><b></code> 相同的效果。让我们来看看为什么这是首选的方法:</p><ul><li>我们终结使用额外的表现标签来标记与其他普通单元格不同的头部单元格。</li><li>默认上,大多数可视化浏览器将把 <code><th></code> 里边的内容加粗和居中——让容易地区分表头和数据。</li><li>因为与普通 <code><td></code> 不同的唯一性,我们可以样式化表头,从而把它同其它的表格单元格区分开来。</li></ul><p>当然还有其它使用表头的理由,以后我们还会进一步讨论……</p>I?%BA%3CI%A6J%EB%3Bs%8F%12%CC&%1C%08表格的 web 标准解决方案(三)2005-03-30T10:00:00+08:00https://realazy.comRealazy<p><code>max-width</code>、<code>max-height</code>、<code>min-width</code> 和 <code>min-height</code> 这四个性质(property)分别表示最大宽度、最大高度、最小宽度和最小高度。它们在 CSS 中有着很重要的作用,比如,它们可以用来很好地协调各种不同分辨率下用户端(client)的显示效果,举个例子,你设计的网页由于侧栏(sidebar)过宽,当用户使用 640*480 或者更小的分辨率浏览网页的话,就会导致内容(content)栏过窄,影响用户的阅读,一个办法是,把内容或者包含内容的 div 的宽度固定一个最小值,姑且是 780px 吧,那么我们可以这样写:<code>min-width: 780px;</code>,这样,当用户端分辨率的宽度在小于 780px 时,会在水平位置上出现滚动条,不至于影响布局,用户稍微移动一下滚动条就可以方便阅读主要内容。</p><p>然后,十分不幸的是,你知道我要说什么,就是 IE,这个世界上使用者最多的浏览器,到目前的版本为止,对这四个性质没有一个能够,哪怕一点点的支持。这可让设计者吃尽了苦头,最明显的就是,当你使用 float 布局时,在 IE 中的一个臭毛病是,顶层 div 的宽度变小的时候(缩小窗口,分辨率低等),浮动的布局(一般是右边那一块跑到了下方)就会被破坏,变得一塌糊涂。这样即使是最开明的主管,也不能容忍你这样的设计……</p><p>天生不足,后天补上。web 设计世界的天才多如牛毛,我们随便 Google 就能找到不少解决方案(solution),在抱怨 IE 的同时请向西半球鞠躬。当我写到这里时,我才开始一一验证我所找到的方案,结果让我大失所望,令另我把标题从「让 max-*, min-*在 IE 中有效的解决方案」改成了「让 max-*, min-*在 IE6 中有效的解决方案」。真如 <a href='http://mezzoblue.com'>Dave</a> 所说:<a href='http://www.onestab.net/a/csscribsheet.html'>别指望 `min-width` 在 IE 中有用</a>。</p><p>我找到的第一种方案,在 <a href='http://www.issociate.de/board/post/154073/min-width.html'>http://www.issociate.de/board/post/154073/min-width.html</a> 中说,可以在同层的 div 中加入例如 <code><div style="width: 300px; height: 0; line-height:0;></div></code>。可惜,这种无异于插入空白占位透明的 GIF 图片(spacer GIF)无异,没有实际的效果和意义。诸君可以亲自一试……</p><p>第二种,请访问 <a href='http://www.svendtofte.com/code/max_width_in_ie/'>http://www.svendtofte.com/code/max_width_in_ie/</a>,此人研究 JavaScript 极为深入,在 CSS 中使用了极为罕用的 <code>expression</code>,虽然这会让 CSS 文件通不过校验(validation),但是我觉得,标准的意义在于分离结构和表现,而且未影响到 XHTML/HTML 的代码合法性,可以接受。我曾欢天喜地啊,因为我用 IE 各种版本测试了他的演示版(demo)均正常无比……我自己写的在 IE 5.x 中也测试通过,最后,在 IE6 中测试的时候,IE6 竟然当掉(crash)了。不解,极为痛苦……仔细检查代码,原来他的 demo 没有任何 DTD 声明(即 <code><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"></code> 这样的声明),莫非此方法只可以在怪癖模式(quirks mode)下起作用?我把我的 HTML 文件中的 DTD 去掉,果如其然。真是一个极大讽刺,想在标准下使用 CSS 却不能在标准下使用 XHTML/HTML……这个方法,嗯,假如你的网页在 quirks mode 下使用,可以使用这个方法……但是,在 IE 5.x 下可以使用,无论是标准模式(standard mode)还是怪癖模式下。具体方法在其网页上,在此不列出。</p><p>第三种方案,恰恰相反,在 IE6 中十分完美,而且也不用担心 CSS 代码的合法性。请访问 <a href='http://www.doxdesk.com/software/js/minmax.html'>http://www.doxdesk.com/software/js/minmax.html</a>。不像作者所说,可以支持非 mac 版的 IE 5 以上版本(make IE5+/Win support CSS min/max-width/height),我的测试结果,只有 IE6 起作用,而且十分完美。如果你的网页已经不再面向 IE5.x,这个方法就是拯救你的灵丹妙药。方法十分简单,请下载 <a href='http://www.doxdesk.com/file/software/js/minmax.js'>http://www.doxdesk.com/file/software/js/minmax.js</a>,在 <code>head</code> 区域添加 <code><script type="text/javascript" src="minmax.js"></script></code> 就可以了。</p><p>从此,在 IE7 出来之前,你可以部分指望 <code>min-width</code> 在 IE 中有用了。还有什么更好的办法,请留言,谢谢。</p>!g%BC%AB%EE%C6WR%FC%C4%0FK4%A0%C4%25让 max-*, min-*在 IE6 中有效的解决方案2005-03-28T10:00:00+08:00https://realazy.comRealazy<h2 id="">添加(表格)摘要</h2><p>此外,我们可以为 <code><table></code> 标签添加 <code>summary</code> 属性(attribute),进一步阐释包含在表格中内容的目的和内容。摘要对使用非可视化工具来阅读信息的人尤其有用。</p><p>以下展示了为我们的表格例子添加摘要属性和值:</p><pre class='html'><code class='html'><table **summary="This table is a chart of all Boston Red Sox World Series wins." **> <caption> Boston Red Sox World Series Championships </caption> <tr> <td align="center"><b>Year</b></td> <td align="center"><b>Opponent</b></td> <td align="center"><b>Season Record (W-L)</b></td> </tr> <tr> <td>1918</td> <td>Chicago Cubs</td> <td>75-51</td> </tr> <tr> <td>1916</td> <td>Brooklyn Robins</td> <td>91-63</td> </tr> <tr> <td>1915</td> <td>Philadelphia Phillies</td> <td>101-50</td> </tr> <tr> <td>1912</td> <td>New York Giants</td> <td>105-47</td> </tr></table></code></pre>%ADx%DC%9123?%9D%E7:%B4%E5%F9%FF%A8?表格的 web 标准解决方案(二)2005-03-25T10:00:00+08:00https://realazy.comRealazy<p>来自 <a href='http://simplebits.com'>Dan Cederholm</a> 的 Web Standards Solutions: The Markup and Style Handbook 的第三章 Tables are Evil?,事关表格在 web 标准下的正确使用。我利用工作的空余时间,将这一章节完全翻译奉上。今天送上 A Table that Everyone Can Sit At,还不是很精彩。</p><p>待翻译完整章后我会整理成传承标准的一个栏目。由于工作关系,大伙不要催我,我会自觉完成的。:)</p><h2 id="">完全表格式</h2><p>完全没有理由不用表格来标记表格式的数据。但是等等,什么叫表格式数据?这是一些例子:</p><ul><li>日历</li><li>电子数据表</li><li>制图</li><li>时间进度表</li></ul><p>这些例子和其他更多的,(不使用表格的话)可能需要很多 CSS 高难度技术来标记以便看起来像是一个表格。你可以想象一下,试着用狡诈的 CSS 规则来浮动和定位每一项,但往往只能以沮丧的结局告终。不要说没有 CSS 就能精确的读出数据是一个恶梦。事实是,我们不应该害怕表格——并且应该在正确的场合使用它们。</p><h2 id="">人人适合的表格</h2><p>表格受到谴责的一个原因是,如果使用不小心,它们会导致易用性的问题。比如,屏幕阅读器要正确读取十分艰难,小屏幕设备常常因用以布局的表格而备受干扰。但是还有一些细小的事情我们可以做到,以提高数据表格的易用性,同时创建了一个苗条的结构,然后可以轻易地用 CSS 来样式化。</p><p>让我们来看看一个简单的表格例子,如图,描述了一支美国棒球队的长期战败。</p><p><img src='/assets/missing.png' alt='' /></p><p>尽管 Red Sox 迷看到会极度失望,但这还是一个极为完美表格式数据的例子。有三个表格<strong>头部(Header),包括 Year, Opponent, and Season Record (W-L)</strong>,跟随着该队四年中表现的数据。表格之上的是说明(Caption),定义表格所包含的内容。</p><p>标记这样的数据表格是很直截了当的,我们可能会这样做:</p><pre class='html'><code class='html'><p align="center">Boston Red Sox World Series Championships</p><table> <tr> <td align="center"><b>Year</b></td> <td align="center"><b>Opponent</b></td> <td align="center"><b>Season Record (W-L)</b></td> </tr> <tr> <td>1918</td> <td>Chicago Cubs</td> <td>75-51</td> </tr> <tr> <td>1916</td> <td>Brooklyn Robins</td> <td>91-63</td> </tr> <tr> <td>1915</td> <td>Philadelphia Phillies</td> <td>101-50</td> </tr> <tr> <td>1912</td> <td>New York Giants</td> <td>105-47</td> </tr></table></code></pre><p>这将会产生跟图十分接近的效果,但是,有些东西我们还可以改进。</p><p>首先,我们马上可以把表格的标题"Boston Red Sox World Series Championships" ,修正成更具语义性的 <code><caption></code> 标签。<code><caption></code> 需要马上跟随 <code><table></code> 起始标签之后,通常包含标题,和/或说明在表格内的内容的本质。</p><p>显而易见地,表格地用意很容易为视力正常的人们所理解,同时能够帮助使用非可视化工具浏览的人。</p><p>让我们用恰当的 <code><caption></code> 来取代开始的段落吧:</p><pre class='html'><code class='html'><table> ** <caption> Boston Red Sox World Series Championships </caption> ** <tr> <td align="center"><b>Year</b></td> <td align="center"><b>Opponent</b></td> <td align="center"><b>Season Record (W-L)</b></td> </tr> <tr> <td>1918</td> <td>Chicago Cubs</td> <td>75-51</td> </tr> <tr> <td>1916</td> <td>Brooklyn Robins</td> <td>91-63</td> </tr> <tr> <td>1915</td> <td>Philadelphia Phillies</td> <td>101-50</td> </tr> <tr> <td>1912</td> <td>New York Giants</td> <td>105-47</td> </tr></table></code></pre><p>用以传递表格内容信息的说明是很重要的。默认下,大部分可视化浏览器将会把 <code><caption></code> 标签内的文字放置在表格上方中间处。我们可以,当然,更改默认的样式——后面我们还有例子演示。现在只需了解,表格本身独特的标签让这更漂亮和简单。</p>%E6p%8E.%E2A%F1%7B%D5O%1B%D0%C4%00%EC%FD表格的 web 标准解决方案(一)2005-03-23T10:00:00+08:00https://realazy.comRealazy<p>今天周六,睡到 8 点就起床,上上网,跟同学聊聊 QQ 以后,开始无所事事。看看地图,计划出游。</p><p>我从人大出发,徒步向北,向中关村进军。北京的一个重要特点就是人多,虽不至于水泄不通,但也到了寸步难行之境地,特别是到了太平洋一带时。途中很多数码店,看见一家商城(忘了叫什么名字了)打的广告叫做「苹果 MP3xxx 中」,纳闷,苹果是什么牌子?细想,原来是 iPod——这个我梦寐以求的随身数码播放器。鉴于我的工资水平,我还不敢把它放进我的 wishlist 中去。我没有进入商城里边,我怕我进去以后找不着出口,我是个路盲,呵呵。</p><p>我一路北进,走过了北大的三个门口。这下子总算知道了北大——我年少时对她有过梦想的地方——的地理位置了。本来我有打算进去瞻仰一下这所正在争创世界一流的百年名校。一番思量,还是没有进去——我对她的所有梦想都已经在上个世纪遗弃,她没有给我留下任何东西,我也不必给她留下点什么——本来打算进去找个厕所留下些什么东西来纪念一下的。继续前行,来到一个叫做清华西门的公交车站,却怎么也找不着写有「清华大学」字样的门,已累,决定放弃寻找清华大学。</p><p>坐上了运通 105 的车,当乘务员问我到哪的时候,我才觉悟,哦,这是「有人售票车」,必须得说出个地点以便收钱,这不是投币的公交,不是上去投了个币就可以无忧无虑无所顾忌地终点站的……乘务员纳闷地等了两分钟,我才有了答案:西直门。在北京,我还没有什么熟悉的地方,除了这个上班必经的倒地铁的地方,也是唯一能够流利说出的地名。我记得我上班的地方叫王府井,之前的一个站叫做天安门东,嘿嘿,我就去天安门吧。</p><p>一出地铁站,就看到了毛主席他老人家慈祥的笑容(对不起,我这里使用了最常见的写作方式与内容,如果你对毛主席有什么非慈祥看法,请别对毛主席的像产生同样的看法,因为这像,确实有够慈祥的)。我随人流进了天安门,左右两侧的大门板,镶嵌了 9x9 矩阵的金黄的椭圆的……唉,我也不知道它们怎称呼,「九」五至尊就是了。我一只往里走,直到故宫,要票,停止前行。老实说,我历史沉淀不够,不能领悟这些古老东西存在的意义,除了拉动 GDP 成长以外(君不见天安门里边商店重重?不见凡是好玩一点的东西都要买票?)。较之,我更喜欢大自然的美景,而不是这些沉淀了千万人民血汗泪哭的「艺术品」。我们每介绍一处历史遗产,都不忘说上「体现了古代劳动人民的智慧」,却从来没有说过「体现了古代劳动人民的悲惨」。来到中山公园,要票,也不进,远远地瞻仰了孙中山先生——这位我最佩服的人——因为我跟他有着一样的理想:三民主义统一中国。</p><p>天安门广场很宽阔,很多人在放风筝。一群老外也在放,风筝就是死活不上天。本来我想上去 "Can I help you?" 的,但决定还是看看老外折腾一下,因为他们本来就够搞笑的。还有一位年轻貌美的妈妈陪着三岁左右的孩子放风筝,观察良久,竟不自觉地产生了我是孩子他爸的错觉——嗯,可能跟孤独过久有关系。每每看到人家成双成对,女的还貌美如花,真是痛不欲生啊……还好我有阿 Q 的本领,好像全世界的美女都在陪我逛街一样。(以上纯属玩笑,如有雷同,实属万幸)。假如有一天你看见一个人,带着眼睛,比南方人还矮的南方人,形单影只,你可以上去问问他是不是叫 Neo。</p>%E3%83%1C%86%09%BF%1B%D5%9E0X%3EY%F4%12%1A独孤游记2005-03-19T10:00:00+08:00https://realazy.comRealazy<p>我的愿望当然很多,希望世界和平,希望人人幸福健康,希望使用电脑的人都支持 GNU/Linux,希望同学们都能找到如愿以偿的工作,等等。</p><p>终于有工作了,这样我也可以写写那些纯粹跟钱有关系的愿望清单了,流行的说法,叫做 wishlist,后面一般还加上个年份什么的,比如 Wishlist 2005。</p><p>第一个,买个手机。其实手机对我没有什么用,我不是一个有人要找的人,除了家人。但是没有手机毕竟太「酷」了,「呆」了也不一定。买一个吧,拨 110 也方便点。月末发工资,估计有两千,上缴 600 房租后拿出 800 卖个索爱 T290C,然后剩 600,用以艰难度过 4 月份。</p><p>第二个,买个二手液晶显示器。在学校那个虽然还可用,但很破,估计 50 块台币也没有人要。这个愿望希望能在 6 月份实现,因为那时我回校毕业论文答辩后,顺便把我的破电脑带过来。</p><p>第三个,这个愿望比较远。我希望我能在 2007 年之前能够剩余 2 万,给家里盖房子。这么多年来,我家的泥砖破瓦依旧,都是我读书花光了钱的直接后果。努力工作!希望加工资!呵呵,这是唯一合法途径。</p><p>第四个,希望我能在 2008 北京奥运的时候拥有自己的笔记本!</p><p>暂且这么多……其实愿望多着呢,不过两手空空,所说的每一句话几乎都等于废话。</p>%F7%D0%EA%1A%5D%02%3E%A4K%3C%C7t%BF%FC%E0%99愿望清单2005-03-16T10:00:00+08:00https://realazy.comRealazy<p>来北京已经 4 天了。</p><p>9 号那天上午 <a href='http://dev.eyou.com'>qyb</a> 亲自在北京西站迎接,尽管一下火车就冷风扑面(真的,还没有吹过这么冰冷的风),但看到 qyb 后心里温暖不已。他还带我到美食街喝了一碗粥作为早餐,午餐又帮我买了单(新来乍到,没有 AA,蒙他一顿,在此感谢!)。</p><p>晚上在回住处的时候,得到同事张唯一的鼎力相助,带我到超市买了床上用品等。很感激他,要是没有他,那天晚上可能会很惨……跟我住的都是南开的牛人,包括 <a href='http://bluetent.org/'>Bluetent</a>,其他两位也是大牛,可惜现在我还不知道他们的网上 id,以后补上。我跟 Bluetent 一个部,每天一起上下班,吃也在一起。周五那天晚上一起坐公交回去的时候他半路下车,结果我在未到住处的战点就下了车,走着走着迷路了……差点跑进了中关村,不过总算知道了人大在哪里,离我住的地方不远,以后有空我去玩一下……</p><p>每天花 40 多分钟公交到西直门,坐地铁上班,中途还得在复兴门转车,加上上班时间人员拥挤,好累……好在我的体积不大,在车上总能找到容得下我的空间。</p><p>北京的上班时间蛮怪,9 点才开始上班,中午一个小时吃饭,下午 6 点放工。我很喜欢这种工作时间,因为我本来就是一个不用午休的人,早上还可以起晚点。</p><p>北京的风,大而且冰冷。在室外不习惯,但室内真的很舒服,比广西的老家还舒服,莫非是暖气的原因?对于我这种之前没有暖气过的人来说,真的很幸福,睡觉的时候可以脱光光,而且盖很薄的被子,在南昌这是无法想象的。我在南昌的时候跟同学说过,由于怕冷,我这辈子可能不会跨过长江半步,想不到造物弄人,而且看起来,我错了,现在得修正一下。我的初步体验是,在北方过冬比在南方过冬要好。</p><p>周末比较无聊,一天到晚就在看书,看到想吐。唉,没有搬我的破电脑上京真是失误。没有电脑,好像一个刚失恋的人,而且似乎要失恋 3 个多月,因为我 6 月份才会学校,才有机会把电脑搬上来。慢慢忍受吧,等待破镜重圆。唯一遗憾的是,我的破网站在 3 个月内可能无任何更新了。</p><p>借用同事张唯一的笔记本草草纪录,再次感谢。</p>%7D:%12U%F0%EAJ%9B%8D%04%F4%E0%FF%82U%5D北京2005-03-13T10:00:00+08:00https://realazy.comRealazy<p>今天去火车站买火车票,匆匆一眼,看中了 T147,当时就买下。售票员一递出票,马上跟我说,T168 更快更便宜……回去上网一查,发现 T147 要绕一圈才上北京,而 T168 直接走京九线!差点没气死!这个售票员也太敬业了,TMD!等卖完票在告诉我!害得我明天得去退票。而退票的手续费是 20%,就是我得损失 50 多元人民币!你说气不气人!</p><p>都怪我没看清楚列车时刻表,为匆忙的一瞬的不充分准备的决定交了学费。而售票员的心态,唉,我都不知道用什么词儿来形容他好。他显然就是想赚我这 50 多元人民币的退票费嘛!这么多年了,铁老大的素质还是没有什么提高。</p><p>不知道用什么华里的词藻来骂铁老大好,记流水账发泄一下,以便有利于心理健康……</p><p>晚上在找火车票,才想起没有从衬衫里拿出来……衬衫已经洗掉了,赶紧找出来,幸好没有揉碎,希望明天还可以退票。今天不知道怎么了,倒霉极了。</p>%FDc%88%F3%3E%17D%1F%A9%E7%89%D0G'%10%81买火车票,中招2005-03-04T10:00:00+08:00https://realazy.comRealazy<p>前天早上 8 点就出发,奔波几乎 30 个小时,昨日下午终于安全抵达学校。尽管北上没有南下挤,但我还是只能在抵达终点前的一个站才有机会上厕所,还好没有憋成膀胱炎。</p><p>因为 <a href='http://dev.eyou.com/'>qyb</a> 的来信和提供的招聘信息,我已经进入 <a href='http://eyou.com/'>eyou.com</a>,工作内容是网页代码重构——这是我自己起的名字——负责将传统的基于表格的网页设计转化成兼容标准的 XHTML+CSS,要求是在 IE6 中完全重现,IE 5.x 也能达到至少八成,其他浏览器没有特殊要求。在此感谢 qyb 的赏识,为我带来了第一份工作。</p><p>看似简单,我今天花了一天,才发现很困难——因为基于表格的设计跟 <code>div</code> 布局的设计方式在思路上不同。今天我做得头晕晕的,看来还是不够熟练。我还得充电。</p><p>有了工作以后,传承标准的一些计划不得不拖延了。至少在近期不会有什么改变,但我不会放弃的。继续跟大家一起努力学习,尽我所能为大家带来一些好东西。</p>%E9%09%B8%E0%D0%18%7D%C4B%E3%D1%9F%A8%97h%5D回校,工作着2005-03-02T10:00:00+08:00https://realazy.comRealazy<p>今天才 2 月 21 日,离我上学还有两个多星期,倒不是因为学校开学晚(其实今天已经是正式上课时间了),因为实在不想跟全国人民打这场遍及大江南北的春运战役。因此,我得在 3 月 5 号春节结束后再做打算。</p><p>在家其实很无聊,主要原因是电脑使用不方便。显示器老化、没有宽带……等等,烦死人了,连在终端练习 C 语言程序都成问题。对,我报了个国家计算机三级考试,4 月份开考的,所以抓紧时间熟悉 C 一遍。一直以来我都没有考过这种东西,但是上个学期看到有一堆垃圾证书的同学在找工作方面抢尽先机,不可能没有一点点地妒火中烧。更无奈的是,我竟然也要装上盗版 Windows 来熟悉一下 MS Office 系列软件的操作了,为了在简历上写上的「精通 Office 操作」做点实务工作。爱好不能当饭吃,还是做点实务工作罢,即使是厌恶之物亦不得不攀附之。</p><p>工作好令人恼火啊。《南风窗》上的这段话:</p><blockquote><p>随便翻阅报章上的招聘启事,频频是黑色幽默。它们清一色都对应聘者的年龄、学历、性别、工作经验等进行限制,对应聘者的社会关系、客户资源的要求屡见不鲜,至于外形条件更堂而皇之地自行其是。按照目前的用工条件,比尔盖茨注定要下岗,中年人基本上属于被淘汰的过期品,女性只剩下做家庭主妇的份,乳臭未干的大学生如不弄虚作假编造工作履历,恐怕永远也满足不了用人单位有关工作经验的要求,而类似形象、气质、身高等方面的限制,则像是寻求性伙伴。</p><p>有谁想到,招聘汽车司机,英语水平要过四级;保安警卫保洁,动辄得熟悉电脑操作。回过头来看我国的就业形势,如此荒诞不经的门槛设置,除了造成人才高消费外,也暴露出用人单位色厉内荏、靠色彩装点门面的阴暗心态。</p></blockquote><p>不知道各位读后有何感想。我在学校本身不是「优秀」学生,因为我只得过一次二等奖学金;没有什么社会实践,因为我十分讨厌做作虚伪社团生活。我当年弃理从文,可能真的走错了路子。更可恶的是,我怎么会选了行政管理这样的专业,我自己本身身材矮小,体型瘦弱,换句话说,没有官相,没有管理的气质,该如何就业是好?家里老爸老妈总是问我签了没有,特别是身边的同学个个都已搞定就业,他们不能不心急如焚。我老是以轻松的口吻说,不用操心,不急……其实我心里也没底。到底谁会要我呢?</p><p>各位,帮帮忙,介绍一份工作给我投投简历……可以先看看我的<a href='/resume/'>简历</a>,到底能干些什么……</p>%C7%C0%B9%C4%22GR%17%0C%86%1B%A2%B9C%C5A漫长……2005-02-21T10:00:00+08:00https://realazy.comRealazy<p>现在 SEO(搜索引擎优化)很吃香,谁不想自己的网站能够在搜索引擎中排名靠前呢?</p><p>搜索引擎蜘蛛在抓取网页的时候绝对是盲人,管你用再漂亮的网页表现,它们都视而不见,只关心网页的内容是什么。如果不是优质的内容,它们可能还爱理不理。</p><p>如何让搜索引擎迅速收录你的网页?出了付钱,当然还有办法,那就是让蜘蛛能够不费力地抓取网页。不费力?对,就像盲人能够不费力地使用你的网站一样。如何能够做到?答案只有,使用 Web 标准。这不仅是道义上的问题,还关系到在搜索引擎中的等级问题。使用 Web 标准,利人利己,何乐不为?</p><p><a href='http://www.456bereastreet.com/'>456BereaStreet</a> 出了这篇 <a href='http://www.456bereastreet.com/archive/200502/basics_of_search_engine_optimisation/'>Basics of search engine optimisation</a>,很大程度上诠释了 Web 标准跟 SEO 的利害关系。我读了也颇有感触,所以翻译奉上。请看《SEO 基础》。</p>%F1Ia%F5%83'~%A64%09%C8%AA%185%E1%5EWeb 标准与 SEO2005-02-15T10:00:00+08:00https://realazy.comRealazy<p>昨天晚上看了一下一年不如一年的央视春节联欢晚会,留给我印象的只有《千手观音》这个节目,如果没有记错的话,她们应该就是在 2004 年雅典残奥会闭幕式上表演过。她们全是聋哑人,在无声无息的世界里能如此协调,实属不易。这难道不是一种不向命运屈服的象征吗?</p><p>我最感动的是,那位聋哑主持人用哑语打出「爱是我们共同的语言」,然后由哑语老师为全场观众示范、教学,再由观众练习一遍。此时此刻,如果有聋哑观众在观看晚会的话,我相信,他们会很感动,这个世界没有抛弃他们,还有很多人跟他们一起,去寻找更阔的梦……</p><p>我已经看了五六年的春节晚会,过去有没有类似的做法我不知道,但这绝对是近年来的第一次,央视第一次在春节联欢晚会上表现出对残障人士的关怀。我希望原因不是聋哑人在残奥会闭幕式上的杰出表现。如果是,我们要感谢这些年轻的聋哑美人们,是她们让我们还记得关怀残障人士。我也希望,央视不仅仅在今年,在明年、在后年、在以后,不仅仅是春节联欢晚会,在所有的节目中,都不要忘记关怀残障人士,他们是人类的一部分,没有任何理由区别对待。</p><p>然后说到我们所关心的 Web 标准。为何要使用 Web 标准?很多网站都列举一大堆理由:结构与设计相分离、节省带宽、降低维护难度等等等,但我认为最为重要的一条是,它是一个没有忘记残障人士的标准。提高易用性、亲和力是该标准始终贯彻的原则。</p><p>当我们看到美国、英国等政府都已制定法律(如美国联邦政府闻名的 Section 508 条款),对岸台湾的「中华民国」教育部都已拟定法案时,我们的国家,中华人民共和国,却没有任何官方的建议,民间的呼吁也少之又少,更不用说在橡皮图章的人民代表大会上提案,然后上升为法律。</p><p>我们知道我们的国家正在「和平崛起」,还有很多其他更重要的事情做……我们不能要求更多,我们要自觉行动。Web 设计者,请您在设计网页的时候,别忘了我们的残障人士好吗?因为「爱是我们公同的语言」!建议您从今天起就用 Web 标准设计网页,不要等明天,也不要等到法律强制您……</p><p>您还没有学习 Web 标准吗?请您马上学习,给残障人士更多的关怀,更多的温暖。当然,有时间的话,我建议您也学习一下哑语,至少得学会打「爱是我们共同的语言」……</p>%B9A%15x%1C/%D4%BE%83%D9%CF%F3%04;%AEC爱是我们共同的语言——央视春节联欢晚会的人道关怀兼谈 Web 标准2005-02-09T10:00:00+08:00https://realazy.comRealazy<p>我的显示器严重老化,散焦现象十分严重,基本上已经不能阅读屏幕上小于 20px 的的文字,现在,我打开电脑,只能抓住开机后显示器尚可以清晰显示的几分钟,迅速阅读所需信息,然后,只能听听歌,打打游戏了。</p><p>游戏?嗯,在 Linux 下的小游戏。我个人不喜欢玩大型游戏,像 PRG,我没有耐心,肯定会被烦死,更不用说耗时更多的网游了。现在我就介绍一下我无所事事时玩的一些小游戏,对,是 Linux 环境下游戏,与君分享。在提到这些游戏的时候,原谅我不能给出相关的?URI,因为我是离线写作的。有麻烦的时候,请使用 Google。</p><p>还记得在任天堂 8-bit Family Computer(人称红白机)时代风靡全球的 Super Mario(超级玛丽)游戏吗?现在 Linux 下也有一个克隆的版本,叫做 Super Tux(姑且翻译为超级企鹅吧 :)),虽然源于 Super Mario,吃蘑菇变大,吃花有炮,操作一样等等,但比它更好玩,总共有 26 关,不像 Super Mario 8 大关,每大关 4 小关,难度均衡,比起 Super Mario 更刺激。我已经打通 26 关,另外,最新的 0.1.2 版的 bonus levels 里的关卡难度很大,值得挑战。</p><p>第二个要介绍的是 Tux Racer,是一个赛车类游戏,不过主角是一个企鹅罢了。这个游戏很普通,对于专职玩家来说简直不值一试,但在乏味时可以用速度来提提神,尤其在 Practice 里边有一关叫 "Who said Tux can not Fly",玩起来很有速度感。</p><p>接下来的是一个类似泡泡龙的游戏,叫做 Frozen Bubble(冰封泡泡?),需要射中相同颜色的泡泡,偶尔还可以用来锻炼一下眼力。双人比赛也比较有意思。在 Gnome 有一个衍生的版本叫做 Monkey Bubble,提供了网络的功能,有条件的话还可以上网找人单挑 :D</p><p>toppler,考验耐力和细心,目的是把一个青蛙安全送达塔顶的小游戏。</p><p>由于在学校时没有什么准备,我现在只玩这么几个小游戏,还有 Gnome 自带的一些,不过不是很好玩。还有很多好玩的,但我没有网络可以搞到,只能安于现状知足常乐了。了解或者想玩 Linux 更多好玩的小游戏,我的建议是,去找 SuSE Linux。这是一个我见过的带最好游戏的 distribution(发行版)。当然,大游戏也是有的,我了解的就有魔兽争霸 3,无冬之夜等,这个,最好是找 Gentoo,它的 ebuild 里面有很多大型游戏哦……可惜我都没有玩过,等我有时间,再试试吧 :P</p>WY%16%C5%FA8ny%BF%7B%5E%3C%88%86w.Linux 下的几个小游戏2005-02-07T10:00:00+08:00https://realazy.comRealazy<p>近来很少使用电脑。</p><p>我整天忙这忙那的,一句话,做清洁工,蚊帐、被套、枕巾、席子等等全部被我清洗干净了,唉,来我家里玩的人都说我像女孩子,他们哪知道,家里除了我,还有谁会洗呢?我老爸不会,我爷爷奶奶已上 80 高龄,不可能让他们来做,我老妈跟妹妹在外地,一个忙着赚钱,一个忙着高考,要年三十才回家……只有我,「越俎代庖」,就等着过年了。</p><p>除此之外,我的显示器已经严重老化,字体已经模糊不清了,看着十分吃力。我回家得从南昌乘火车到南宁,29 多个小时车程,所以没有带显示器,只带主机而已。问题就在这里,我在家这个显示器是前年在一家破产的网吧用人民币 90 块「检」来的,当时还可用,现在已经成了电子垃圾,看看能不能被我榨尽最后一点余热了。</p><p>因为经济条件不允许,除了更新换代以外,我已经想尽办法,在 Google 也搜索了一大堆的 solution,可惜,死马不能当作活马医,我已经尽力了。我现在只有把字体调成 20px,选择了 High Contrast(高对比度)的 Gnome 桌面主题,并使用 XFCE 4.2 的最新功能 Gamma Correction 来调校,在字体依稀可辩(这个 bian 我看不清中间,不知道打错了没有)的情况下离线写下了这篇 2 月份的第一篇 blog。</p><p>我只有看看书了,可惜我的大都是电子版的。我学习 PHP 已经入了一点点门,我写了一个留言板,基本框架已经具备,就等各个功能具体实现了,本来想参考 WordPress 的源代码来处理 post,现在似乎也不行,唯有等开学了。</p><p>人家说贵在坚持,本来打算翻译一些 <a href='http://alistapart.com/'>A List Apart</a> 的一些 Web Standards 的文章,现在也由于客观原因不能坚持了,间歇一个月。</p><p>罗罗嗦嗦流水帐,只因电脑上瘾而已。眼花了,不写了。</p><p>先祝大家春节愉快,给大家提前拜年了。</p>%C2%107%CB%F6%8C%82%92%A4无题2005-02-06T10:00:00+08:00https://realazy.comRealazy<p><strong>Free</strong> 是一种信仰。Free,自由。这是我使用 GNU/Linux 后的信仰。</p><p>我 2000 年就开始学习制作网页了,工具是 FrontPage。那真是一个暴躁或浮躁的年代,我从不少网站抄袭不少的设计,做成了一个专门盗链人家音乐的网站,并且申请了大量的付费广告放置其上。想不到歪打正着,我的那个音乐网站竟然有 4000 独立 ip 访问量每日,也给我带来了丰厚的广告收入(被抵赖不少,只收到 200 多元),也在那时的 8848.net 上赚了不少 E 币,购买了不少东西,可惜,在上大学那年,剩下的 E 币被抵赖了……而也是那时开始,我就停止了网页的制作和学习……</p><p>2002 年国庆过后,我从一个破产的网吧里淘了一台机子,基本配置参数是 Celeron 433 CPU,128M SDRam,54000rpm 20G HD,15 英寸可上 1024 * 768 @ 85hz 的显示器,一直陪伴至今。</p><p>有了电脑,有了让我鼓捣的天地,我什么都整,尽管机子奇慢。2003 年 4 月我开始使用 GNU/Linux,我就深深爱上了她。我开始了解除了 MS 外的另一个世界,一个开放、自由的世界。这个世界充满新奇,充满创意、充满爱。我在这不能展开,因为太丰富了。</p><p>由此我爱上了使用一切自由开放的电脑工具,系统为 Slackware Linux,桌面是 Gnome,使用 Firefox 浏览器,使用 Bluefish 编写网页……</p><p>我是一介区区文科学生,对于数学即使有极大兴趣,也由于能力所限亦不能深入,我的逻辑能力亦受限制,对于计算机技术,更只能盲人摸象。所以即使我使用 Linux 几近两年,我还是一个 USER 而已。</p><p>而今天,我在使用这个稍微复杂的 Linux 系统,积聚的经验应该让我可以不再是一个 Beginner User,而是一个 Intermediate User 了,那么我就可以向 Advanced User 进军了。所以,我想,我得开始学习编程了。</p><p>而我现在最感兴趣的是什么呢?Web。所以我想学 Script Langugae,什么 Script Langugage 是自由开放的?PERL-CGI,PHP,PSP(Python Server Page)等,什么最易学?PHP……所以我决定学习 PHP 了。</p><p>要学习 PHP 了,所以不得不放慢传承标准的一些工作。因为我的目标是,长期看,当然是 Master PHP,近期看,是在三月份前做一个符合 Web 标准的留言板作为学习的作业。我正在看的三本书是如图。</p><p><img src='/assets/missing.png' alt='' /><img src='/assets/missing.png' alt='' /><img src='/assets/missing.png' alt='' /></p><p>做出一个这样的决定,并不意味这我会在 Web 这一行混,我的专业是行政管理,我想还是在这一行吃饭。Web 是我最大的业余爱好,没有其他的原因,只是 Linux 之父 Linus 的一句话:<strong>Just for fun</strong>而已。</p>x%B5%D7%FB8%E4%03%FC(iZ%F4aI%DE%F5做一个 Free 的 Web 设计者和程序员2005-01-30T10:00:00+08:00https://realazy.comRealazy<p>虽然国内似乎没有提供带有 <code>mod_rewrite</code> 功能的 Apache 主机,虽然这篇文章已经发表了 4 年有余,但是,为了学习,我还是翻译出来了。</p><p>四年过去了,国内还是没有多少人关心<strong>用户友好</strong>的 URI;四年过去了,搜索引擎已经有了很大进步,对于检索带有 <code>?</code>、<code>&</code> 等带参数的 URI 也没有什么问题了。但我认为,我们还是有一个<strong>用户友好</strong>的 URI 比较好,至少可以让页面保持优雅,虽然这通常是技术理想主义者的意淫。</p><p>虽然似乎跟 Web Standards 无关,但我从来没有在哪一个 Web Standards 的网站上不看见<strong>用户友好</strong>的 URI(算我的话,要除外),这是不是也说明,Spread Web Standards 的人也是技术理想主义者呢?所以,对于<strong>用户友好</strong>的 URI,我认为是可以并入 Web Standards 的。</p><p>这就是今天的翻译文章《URLS! URLS! URLS!》,介绍如何使用正则表达式来重置 URL,虽然对高手来说微不足道,但是对于不是 Web Master 的 Web Designer 来说,可以在自己架设的 Apache 服务器上,还是那个词,意淫一下。</p><p>我的下一篇要介绍的,可能是 PHP 脚本语言的实现方法。</p>%7C%0E%0F%D0%A9%FF%E9U%000~%EF=?Z)用户友好的 URL,我们可以在 Apache 中重置2005-01-29T10:00:00+08:00https://realazy.comRealazy<p>在<a href='https://www.google.com/search?q=武装body转换器XHTML&ie=UTF-8&oe=UTF-8'>《武装 body 转换器》</a> 中我曾提到它是有局限的,由于在客户端执行的原因。如果能配合服务器端,那就十分完美了。<a href='http://alistapart.com/'>A List Apart</a> 还真的有这么一篇 <a href='http://alistapart.com/articles/phpswitch/'>Build a PHP Switcher</a>,我看了一下,介绍的方法还是满简单的,不过已经足够应用了,对于菜鸟来说,也是一个 5 分钟上手的的 Tip,于是我就翻译出来了:<a href='https://www.google.com/search?q=开发一个PHP转换器&ie=UTF-8&oe=UTF-8'>《开发一个 PHP 转换器》</a>。</p><p>另外,插入两句无关的话(不想另起主题):今日发生的重大事情有,我的 Google AdSense 被停止了,参看邮件:</p><blockquote><p>Xian'an Chen:<br/>您好!</p><p>我们已经注意到您的网页上的广告产生了无效点击。因此,我们停用了您的<br/>Google AdSense<br/>帐户。请您理解,我们采取这一步骤是为了尽力保护 AdWords 广<br/>告客户的利益。</p><p>发布商不得在网站上对任一广告进行无效点击,这包括但不限于由发布商在自己的<br/>网页上产生的点击,使用漫游器、自动点击工具或任何其他欺诈性软件产生的点<br/>击。</p><p>这些做法都有违 Google AdSense<br/>的条款和计划政策,可以在以下网址查看这两<br/>者:</p><p>https://www.google.com/adsense/localized-terms?hl=zh_CN</p><p>https://www.google.com/adsense/policies?hl=zh_CN</p><p>Google 小组敬上</p></blockquote><p>由于事先不了解 Google AdSense 的使用条款,违反了其「激励措施」中的「鼓励用户点击广告或访问广告客户的网站」,我的帐号被停用,两个月辛苦所得的 50 多美元也就泡汤了。我的新服务器空间似乎遥遥无期起来,所以我在首页上号召捐赠,看看能不能延续传承标准的生命了……</p>%DA%82%0B%02~/9%EB%F0%3C%D8%C7%AEE%25%CF简单的 PHP 样式转换器的制作方法2005-01-26T10:00:00+08:00https://realazy.comRealazy<p>只有 serverd as <code>application/xhtml+xml</code> 的 XHTML 1.0 Strict 或者 XHTML 1.1 才算是真正的 XHTML。但是目前最流行、使用最广泛的浏览器——IE 却不支持 <code>application/xhtml+xml</code>,看到 MSN Search Beta 竟然使用 XHTML 1.0 Strict,真是莫大的讽刺。但或许又透露出一个信息:下一代 IE 可能会支持 <code>application/xhtml+xml</code>。</p><p>在我开始使用 XHTML 1.1 的时候,我是盲人摸象,查看为数不多的几个国外使用 XHTML 1.1 网站的源代码,从中推理出一些应然的东西,但竟然也让我歪打正着。看了 <a href='http://www.456bereastreet.com/'>456 BereaStreet</a> 的 <a href='http://www.456bereastreet.com/archive/200501/the_perils_of_using_xhtml_properly/'>The perils of using XHTML properly</a>,我发现自己没有在 XHTML 1.1 做错事。看见我这三篇连续翻译的文章了吗?</p><ul><li><a href='https://www.google.com/search?q=为什么document.write在XML中不工作&ie=UTF-8&oe=UTF-8'>为什么 `document.write` 在 XML 中不工作</a></li><li><a href='https://www.google.com/search?q=使用正确的MIME类型伺服XHTML&ie=UTF-8&oe=UTF-8'>使用正确的 MIME 类型伺服 XHTML</a></li><li><a href='https://www.google.com/search?q=使Google广AdSense在XHTML中工作&ie=UTF-8&oe=UTF-8'>使 Google 广告 AdSense 在 XHTML 中工作</a></li></ul><p>其实就是我在开始使用 XHTML 1.1 的时候碰到的问题用 Google 找到的一些答案。</p><p>然而我不知道的还有很多,多亏了 <a href='http://www.456bereastreet.com/'>456 BereaStreet</a>,让我知道更多,就是这篇 <a href='http://www.456bereastreet.com/archive/200501/the_perils_of_using_xhtml_properly/'>The perils of using XHTML properly</a>,我今天的翻译作品:<a href='https://www.google.com/search?q=正确使用XHTML的冒险&ie=UTF-8&oe=UTF-8'>《正确使用 XHTML 的冒险》</a> 我感觉十分有用,希望对你也是如此,尤其是你要使用真正的 XHTML。</p>%7Cd%F7%DB%3E%20E%AB%A3%E7%CE%D4%9B5DF使用 XHTML2005-01-25T10:00:00+08:00https://realazy.comRealazy<p><a href='http://alistapart.com/'>A List Apart</a> 这篇 <a href='http://alistapart.com/articles/bodyswitchers/'>Invasion of the Body Switchers</a> 介绍了一种可以在多种媒体类型中转换样式的方法,仅仅一个 JavaScript 和 CSS 文件就足够。并且不需要 <code>#</code> 伪连接,也不需要 <code>javascript:</code> 伪协议,让人感觉舒服多了。它的最大优点是,只需一个 CSS 文件就可以包含所有的媒体类型了,并且不需要 <code>alternate stylesheet</code>,像文中所说,可以减轻服务器的访问负担。</p><p>什么方法都是有局限的,尽管这篇文章里声称自己是可以无限扩展的。在一个样式一些细小的变化上,比如仅仅改变字体或背景颜色(这篇文章的例子就是这样而已)当然有很大灵活性,但是如果涉及一个需要「面目全非」的整体改变的样式呢?还是要把它写进单一的 CSS 文件中去吗?那么这个 CSS 文件就会显得过大,所包含的样式也不是全部为用户所使用。这就会浪费带宽,减慢样式显示速度,得不偿失。最好的办法还是使用 server-side 语言,随用户的选择来下载不同的样式表。当然,两者可以结合起来使用。</p><p>不管怎么样,这都比如今流行的样式表转换器优秀得多。这就是我今天奉上的翻译作品:<a href='https://www.google.com/search?q=武装body转换器&ie=UTF-8&oe=UTF-8'>武装 body 转换器</a>。</p><p>翻译花絮:从该文章的<a href='http://alistapart.com/d/bodyswitchers/iotbs.html'>演示样本</a>可以看到,该文章的标题灵感来源电影 Invasion of the Body Snatchers,其中 Invasion 的意思是入侵、侵略,假如照直翻译,会让人不知所谓。为了达到原文之意境,于是我把它译为武装。希望不会曲解原文。</p>r%19~#l%CCq%98%B4~.%25%909%E2%A6一个强大的样式转换器2005-01-22T10:00:00+08:00https://realazy.comRealazy<p>作为一个 blogger 或者 blog 读者,你一定对垃圾评论(comment spam)并不陌生。Google Blog 的 <a href='http://www.google.com/googleblog/2005/01/preventing-comment-spam.html'>Preventing comment spam</a> 里认为,这些垃圾制造者无非是要提高他们的 PageRank。Google 的高招是,从今以后,凡是在连接里出现 <code>rel="nofollow"</code> 的属性,Google 不会再在搜索结果里给这些连接加分,以便从根本上杜绝这些垃圾。(在中国制造垃圾肯定不止这么简单的原因,中国么,呵呵,电子乌贼一样多的人口,啥都有。)</p><p>Google 经已联合 MSN Search 和 Yahoo!,看来 Blog 世界一场规模宏大的打扫垃圾的战役就要开始了。这何尝不是 Blog 世界的福音,感谢他们,让 Blog 产业可以健康成长。</p><p>来自 Google Blog 的 <a href='http://www.google.com/googleblog/2005/01/preventing-comment-spam.html'>Preventing comment spam</a>,以下是防止垃圾评论的一些指南。</p><p><strong>问:连接会怎么变?</strong>答:网站上用户能够创建的任何连接都会加上新的"<code>nofollow</code>"属性。所以假如一个 blog 垃圾评论制造者先前加入一条如此的评论 <code>Visit my <a href="http://www.example.com/">discount pharmaceuticals</a> site</code>,它将会变成 <code>Visit my <a href="http://www.example.com/" rel="nofollow">discount pharmaceuticals</a> site</code>。</p><p><strong>问:什么类型的连接应该加上这个属性?</strong>答:我们鼓励你在用户自己能够加入连接的任意地方加上 <code>rel="nofollow"</code> 属性,包括评论、trackback 和 referrer 列表。评论区域是高危区,但保卫每一个某些人可以加入连接的地方可以让垃圾制造者走投无路。</p><p><strong>问:我应该在我的评论页面的连接里加上 <code>rel="nofollow"</code> 吗?</strong>答:不一定,因为很多有趣的讨论会在这儿发生。同时,如果其他人连接到你的评论页面,spider 还是可以跟踪该连接并且找到潜伏在评论页面的任意垃圾。</p><p>加上这个属性的最佳位置是可以由他人可以直接创建的实际连接上。举个例子,在这个<a href='http://www.google.com/url?sa=D&q=http%3A%2F%2Fweblog.herald.com%2Fcolumn%2Fdavebarry%2Farchives%2F012729.html'>页面</a>上,只有在评论内的连接和直接跟随 “Posted by:” 的连接才需要 <code>rel="nofollow"</code> 属性。</p><p><strong>问:独立 blogger 需要做什么吗?</strong>答:大概不需要。升级生成页面的软件就可以确保大部分的 blogger 自动获得这些改变。</p><p><strong>问:仅仅是 blog 的事吗?</strong>答:不。我们认为任何允许其他人在作者网站(包括留言本,访问统计,或者 referrer 列表)内创建连接的软件都可以加上这个属性。现在我们主要与 blog 软件制作者一起工作因为 blog 是普遍受到攻击的目标。</p>%FF%DF%D0%E8%E1%82%8D%9BAWy%C1z%B8%BFT防止垃圾评论的战役2005-01-21T10:00:00+08:00https://realazy.comRealazy<p>我回家已经多天了,除了跑到小镇上的网吧,我还有一条上网途径:在家电话拨号上网。估计 Modem(调制解调器)已经进入博物馆,新一代亦不知拨号为何物……但在农村,似乎只有这一条上网途径,如果哪一天也像电话一样,村村通宽带就好了。尽管电话上网奇贵,奇慢,像一条在海中遨游的鱼进了水族馆,也像一群被圈养的鸽子,尽管失去了宽阔,但还是可以苟且偷生。</p><p>我使用的是 <a href='http://kernel.org/'>Linux</a> 操作系统,<a href='http://slackware.com/'>Slackware</a> 发行版,版本号为 current,桌面环境是 <a href='http://gnome.org/'>Gnome</a>,<a href='http://dropline.net/gnome'>Dropline</a> 发行版。如果在 KDE 下,我们可以有 kppp 来拨号,但 Gnome,直到最近,才出现了 Gnome PPP,原谅我不能给你连接,因为这是我离线写作的,麻烦你 <a href='http://google.com/'>Google</a> 一下,进入它的官方网站查看详情。</p><p>在 Slackware 的 Dropline Gnome 下如何拨号上网?</p><ol><li>首先,如果是内置 Modem,型号必须是 Conexant 的,ESS 的不能成功。从 <a href='http://linuxant.com'>Linuxant</a> 下载 hsfmodem-7.18.00.02full.tar.gz,解开,<code>make install</code>,跟着提示设置就可以了。由于我们没有美元送给作者,所以最高速度只能是 14.4kbps,呵呵。</li><li>其次,安装 gnome-ppp,二进制包可以从 slackware 的 pkgs 大本营 <a href='http://linuxpackages.net'>LinuxPackages</a> 上搜索找到,或者到官方网站下载源代码自己编译。此外,gnome-ppp 依赖于 wvdial 和 wvstreams,也可以在 LinuxPackages 上找到。</li><li>第三,不知道为何普通用户不能拨号成功。进入系统,普通用户没有使用/dev/modem 的权限,即使 <code>chmode 666 /dev/modem</code>,在拨通的一刹那会自动 crash 掉。办法是有的,我们的 Gnome 不是有一个 gnomesu 吗?运行它,使用 root 的身份来运行 <code>gnome-ppp</code> 即可。没有的话,在 terminal 用 su 来运行好了,不过 Dropline Gnome 是自带的,在 System tools 菜单里面,名字叫做 Run Program as Root。</li></ol><p>不过如果不是为了在 Linux 下工作,你还是到 Windows 去拨号吧,因为毕竟简单点,假如你是双+系统的话。</p>W%EEZ6R%B2%DF%18%86PU%CEje%C7%DB在 Gnome 下拨号上网2005-01-20T10:00:00+08:00https://realazy.comRealazy<p>对于在中文世界的 web standards 的传道者来说,最困难的莫过于一些术语的翻译。Molly 的 <a href='http://www.molly.com/2004/12/18/web-design-world-cool-down/'>web design world cool-down</a> 中就有相关术语误用的问题。还好,对于英文用户来说,由于是 meta language(元语言)吧,他们可以十分迅速地分辨、了解以及投入使用,只要有高人那么一指点。中文用户就十分不幸了,历经传道者的层层翻译,在翻译中又有所曲解,初学者不像经验老到的我们,往往会对于一些术语莫名其妙。</p><p>举个简单的例子吧,在 <a href='http://www.molly.com/2004/12/18/web-design-world-cool-down/'>web design world cool-down</a> 中也提到的,就是 attribute 跟 property,一个用在 HTML 或者 XHTML 中,一个用在 CSS 中,但中文书籍或文献一般都把他们翻译成属性,这就造成了初学者的混淆,尽管我们可以在「属性」后边用括号标上 attribute 或 property(说一句,我就是这么做的)。CSS Cook Book 的台湾版本《CSS 速查手册》在翻译中也注意到了这个问题,他们把 attribute 翻译成属性,把 property 翻译为性质。我觉得,再找不到第三个词之前,性质确实是最佳选择。然而我还是不敢在我的 CSS 教程中使用性质,因为中国电力出版社翻译的 Eric Meyer 的 Cascading Style Sheets: The Definitive Guide 中文版《CSS 权威指南》中使用的也是「属性」,尽管该书是第一版,而且是在折价出售,但在初学者中应该影响还是不小,为了不造成更大的混淆和混乱,我依然使用「属性」来指代 “property”。</p><p>说到这个更令人恼火的是,Zeldman 的 Designing with Web Standards 中文版中,把一向翻译为标记语言的 markup language 翻译成了置标语言,我初次看的时候简直不知道是讲什么,因为我确实没有看过置标语言这个词。尽管无关痛痒,但还是给读者带来不少困惑,原因就在于翻译的混乱。</p><p>还有许多可以造成混乱的词语,比如 tag,可以是标签也可以是标记,但是既然已经把 markup 翻译成标记,tag 还是翻译成标签比较好;还有 CSS 中的 margin,padding,border,国内的翻译惯例是边界,补白和边框,初次看上去,你能分辨出边界跟边框的区别吗?</p><p>为中文用户传播福音不是一件容易的事情,任重道远。为结束混乱,笔者呼吁,无论是出版界也好,还是我们这些福音传播者也好,都应该尽快订立一个「标准」的中英词汇对照表。web 需要标准,我们也需要标准。</p>;%07%03%AE2G%D9%9C%CC%1A%A3K%5E%BD%13cattributes V.S properties,属性还是性质?2005-01-19T10:00:00+08:00https://realazy.comRealazy<p>到了网吧,打开 Email 检查信件,到 blueidea 的 bbs 的网站综合专栏逛逛,打开我的 Blog……这基本上是我一贯的上网风格。如果是我自己的电脑的话,我还会在 Firefox 的<a href='http://www.onestab.net/forecastfox-zh-cn-0.5.8.xpi'>天气插件</a>看看天气,到 linuxsir.org 逛逛,到 linuxpackages.net 检查有没有新的 pkg 放出,以便及时更新我的 slackware-current,还要去 gnomedesktop.org 看这个我最喜欢的桌面环境的新闻……可惜是网吧,一切都不习惯,用者我最不喜欢的 IE,连 MSPY 这个输入法都没有,害得我用智能拼音一个一个字检……</p><p>我回家已经 4 天了,就是说我 4 天没有更新了……之前 Google 还可以搜索到传承标准,我停止更新后竟然,搜不到了……</p><p>前两天在 <a href='http://jjgod.3322.org/'>Web4C</a> 上的 <a href='http://jjgod.3322.org/2004/11/14/mime-type-oyouaaana/'>MIME Type 引出的两难困境</a> 上发了评论而已,今天上去发现他的 Domestic 上竟然出现了我的 Blog 连接!真是吃惊不小呢……对于一个非专业人士来说,能得到肯定无疑是最大的鼓励。这也让我,在现时突然停止更新感到不安。待以后改版好后一定也把 <a href='http://jjgod.3322.org/'>Web4C</a> 连接上。</p><p>slackware 终于缓慢更新了,自从 <a href='http://slackware.com/~volkerdi/PAT-NEEDS-YOUR-HELP.txt'>Pat 病了</a> 之后,更新频率很低,slackware 10.1 似乎遥遥无期?不管怎么样,我还是最喜欢这个发行版。原因之一,我最喜欢的 Gnome,只有 <a href='http://dropline.net/gnome'>Dropline</a> 做得最好。</p><p>当我打开我的 Blog 时,发现上面竟然显示 Not Found!起初我怀疑我被谁 hack 了,我于是登入后台,一切安然无恙。我 google 了一下其他使用 51.net 空间的 blog,也出现这样的情况,看来是 51.net 出问题了。没有办法,如此便宜的空间……</p><p>不过后台一点问题都没有,于是我可以写下了这篇 Blog,虽然我写的时候 51.net 还没有好……于是你可能就是第一个读者了……我自己都没有办法预览……</p><p>所以希望能够在 AdSense 的帮助下买个更好的空间,呵呵。</p><p>这篇 Blog 什么都说,应该是 misc 的,可是我一直不想有一个这样的 categories 来打乱我的写作方向,我的 Blog,目前就三个类:Free/GNU/OpenSources,Life and Thoughts 和 Web Standards。于是在写下这篇 Blog 时,我把所有的类都勾上了。</p>%B8%FA%02%A1%AC$%17%8E-%AF%5D%BA%5B%EE)f今天上网及其他2005-01-17T10:00:00+08:00https://realazy.comRealazy<p>通常我都会在凌晨的时候写作 Blog,所以对于一天的概念比较敏感,我绝对不会犯把一天凌晨发生的事情当作是昨天发生的错误。现在我会说,今天我就要回家了,而不是明天。</p><p>是的,下午两点的火车。我估计要在早上 9 点起床,然后去报名计算机三级等级考试,呵呵,这个时髦啊,我为了以后的糊口着想,终于放下三年半的架子,报考去了。</p><p>我昨天凌晨翻译 <a href='http://www.stopdesign.com/articles/design_process/'>A Design Process Revealed</a> 得晕乎乎,翻译完一半就睡觉了。</p><p>昨天中午才起床,然后去逛沃尔玛,给外公外婆各买一件衣服当作春节礼物。我已经一年没有回家,两年没有见过他们了。还得感谢我的好友免谈,是的,外号叫免谈,他借了 200 块人民币给我,才让我有一个孝顺的机会。</p><p>其实我在昨天 23 点时候就更新了首页,放上了了翻译的成品<a href='tech/design_process.php'>设计过程解密</a>,不过只有一个小时就 12 号了,呵呵,就让它的生日是 12 号好了。这篇文章我自己感觉说得虽然不详尽,但对于整个设计过程,我们可以有了一个概貌,然后可以发展出有自己特色的设计过程。我自己也从中领悟不少东西。我们都是初学者,得多多向大师看齐。</p><p>我就要回家了,广西北海市合浦县的一个农村里。我会把电脑带回去的,但是,没有网络可用。所以在整个寒假期间,我翻译文章的速度会大大减慢。喜欢我的读者,说声对不起先!</p><p>我会在寒假恶补其他知识,进一步完善我的网站。</p>%BE%FE%92%F70+%8Fp%85%5Bd%94%60%7F%ED%8C暂别网络2005-01-12T10:00:00+08:00https://realazy.comRealazy<p>发挥你的极限!<a href='http://stopdesign.com'>StopDesign</a> 推出了一个用 XHTML、CSS 和 JavaScript 构建的幻灯(我想起了 Eric Meyer 的 S5 系统,不知道这个幻灯是不是基于此技术):<a href='http://www.stopdesign.com/present/2004/sydney/limits/'>Pushing Your Limits -- and other secrets of designing with CSS</a>,里面有很多实用的 CSS 技术。我本想把整个幻灯翻译发布,但鉴于该幻灯的授权是<em>Reproduction in any form, in portion or in whole, is prohibited without prior written permission from Stopdesign</em>,我不能侵犯人家版权,呵呵。我从中选择两个比较有价值的技术,即翻转技术和用网页标准构建网站的步骤,截图,并且把里面的因为英文翻译成了中文。请看:(请分别点击图片查看大图)</p><p><img src='/assets/missing.png' alt='双重翻转的概念' /></p><p><img src='/assets/missing.png' alt='' /></p><p>上面的 Golden Mean 是指一个为 CSS Zengarden 设计的<a href='http://www.csszengarden.com/?cssfile=017/017.css'>例子</a>,它有一个设计的过程 <a href='http://www.stopdesign.com/articles/design_process/'>A Design Process Revealed</a>,我打算把它翻译出来。可是我在 12 号就回家了,没有网络了,呵呵,不知道还来得及么。</p>%0C%D1%05%83%CAFPushing Your Limits2005-01-10T10:00:00+08:00https://realazy.comRealazy<p>我撰写这篇 Blog 的时候,已是凌晨四点。前天学校为了让同学们复习应考,已经实行不在晚间 11 点拉闸关电。这个似乎不是理由,如果真的要复习,11 点关电能影响吗?不会有谁真的会在凌晨爬起来看教科书吧?所以学校的制度,理由找得很烂。</p><p>不关电,对于到凌晨就思如泉涌的我不能不说是一件大好事情。我白天晕忽忽,晚上则灵感精力不断,似乎已经养成了夜猫子的习惯,就是说,我已经提前进入 IT 状态?我在昨日 23:50 开始翻译今天发表的<a href='https://www.google.com/search?q=为什么document.write在XML中不工作&ie=UTF-8&oe=UTF-8'>为什么 `document.write` 在 XML 中不工作</a>,加上整理做成网页,花了一个小时左右。感觉我的翻译是越来越顺了,看来语言就是得多练。呵呵,我现在会什么语言?中文的话,包括方言在内,能够流利的说我的母语客家话和<strong>正宗</strong>广州话(粤语),还能熟悉地说两种粤语的变种……</p><p>说到英语,下午我要考六级去。自从轻松地过了四级以后,六级我已经考了三次,成绩分别是 56.5,54,58,真是死不瞑目。这次也没有底,毕竟从来没有看过、做过做过题。做题?对,这可是过级的途径。考试就是做题,中国的教育,说来真让人丢脸,你没有听说过,到了美国以后,凡是托福,GRE 等考得太高分的中国学生都得重考吗?人家不相信你的实力,人家不需要考试机器,考试机器只在中国有市场。</p><p>更可恶的是,中国的绝大部分本科学历是跟四级挂钩的,四级过了给你学士证书,否则只给毕业证书。这到底是谁规定的?教育部没有规定,教育法也没有。虽然像我不用为学士证书担忧,但是你得看看我们班,已经大四了,还有将近一半的人没有过四级。最后,每年交上四千学费,每年花费上万生活费,最终还得不到学校的承认。</p><p>那该怎么办?买答案,请枪手……养肥了不少人,无论是官方的还是 NG 的,损害了多少尚未有收入的大学生。</p><p>那我又考个六级干什么?时髦啊,如今找工作,不是得英语,计算机证件齐全吗?我丝毫没有怀疑我计算机能力,我没有报过什么计算机等级考试,可如今,那些连网卡是什么都不知道的过了 N 级的人却在找工作中占尽优势,使我不得不重新评估,我是不是也报过计算机三级?我只有四级证书,但我比那些已经过了六级的人,在英语方面走得更远,翻译了这么多技术文献就是一个例证。现在的公司,企业招聘人,怎么也会落人一张张各种证书废纸的圈套呢?</p><p>不管怎么说,还是祝福我,能过掉今次的六级,俗话说,事不过三,看来这次我是逃不了 :~)</p><p>过掉吧,以免我的工作被哪个有六级证书的人抢掉了……</p>hVUa%F8%F7%D3B%AE%9D%09%ADs%B3%E5%06今天下午考六级2005-01-08T10:00:00+08:00https://realazy.comRealazy<p>我已经受够了 WordPress 1.2,原因不在于 WP 自身,而在于我,为了实现自己的设计要求,胡乱地修改了大部分的文件,造成与官方版本的不协调,许多 plugin 无法使用,改版也变得十分困难。痛定思痛,还不如丢掉 WP 1.2,安装最新的 WP 1.5 unstable,因为这个版本的把表现进一步分离,自定义能力十分强大,以后我可以在不修改系统文件的情况下制作自己的主题(theme)了。:~)</p><p>另外一个原因是,老 Blog 很大程度上是为了参加学校的网页设计比赛用的,上面的文章和分类很多都是 Copy 过来的,简直就是国内的那些新闻跟屁虫,不像是一个 Blog,为了发展,去除烂肉,长痛不如短痛。也好让我提高我的 Blog 的质量。</p><p>我一时难以找到升级的方法,干脆把数据库都删除干净,重新来过,所以,你看到的这篇文章,是第一篇……</p>%1A%14js%D7Z%05%FA%0B%AD%01%F5%10%0CB%8A2005!新的 Blog2005-01-07T10:00:00+08:00https://realazy.comRealazy<p>我开始使用 XHTML 1.1 的时候,发现 Google 的 AdSense 广告在 IE 上正常,在 Firefox 和 Opera 中则一片空白,纳闷,对着代码检查了良久,终究无法得知问题于何处。幸亏还有 Google,我终于找到了解决方案。我也不能不说,推广标准,还真的是任重道远,连 Google 都无法保证它的代码会在任何的 (X)HTML 上工作。</p><p>这个解决方案就是这篇今天出炉的翻译文章:<a href='https://www.google.com/search?q=使Google广告AdSense在XHTML中工作&ie=UTF-8&oe=UTF-8'>使 Google 广告 AdSense 在 XHTML 中工作</a>,使用 PHP 来实现。对于 ASP,相信难不倒聪明的你(呵呵,我不会 ASP,是在对不起了,要怪,你怪原作者好了,谁叫他不写 ASP,嗯……)。</p><p>我原本打算参考这篇文章,结合 Ian Hickson 的 <a href='http://ln.hixie.ch/?start=1091626816&count=1'>Why document.write() doesn't work in XML</a> 来写一篇原创文章,无奈实在是太懒,或者文笔不行,又或者会遥遥无期,还是直接翻译出来给大家一睹为快,尽快解决碰到的问题吧。</p><p>这其实是一个 JavaScript 的问题,假如你发现你在 XHTML 1.0 Stric 或者 XHTML 1.1 中无法使用 JavaScript 的调用,或许,这篇文章对你也有启示。</p>%84%E4%FE%10%D0%C7%1A%84fL%B2%8B%DE;%EE%F7如何使 Google 的广告在严格的 XHTML 中工作2005-01-07T10:00:00+08:00https://realazy.comRealazy<p><a href='https://github.com/fsprojects/fantomas'>Fantomas</a> is a code formator for F#, just like the <a href='https://prettier.io'>prettier</a> for JavaScript if you have known about it.</p><p>You can use Fantomas with your favorite editors by following the official guide. But what about VS Mac? The guide shows nothing.</p><p>VS Mac provides an option for using external tools, just setup Fantomas as follow:</p><p><a href='/assets/til/2021-04-19-fantomas.png'><img src='/assets/til/2021-04-19-fantomas.gif' alt='Fantomas Setup' /></a></p><p>(Click to view high resolution)</p><p>Notice, you need to fill full path for the <code>Command</code> due to VS Mac refuses <code>$PATH</code>. And make sure <code>Save current file</code> is checked, to avoid VS Mac asking you to reload the file each time you formated.</p>%10s%96J%80%1C%F8V)l%DF%0D%7Fm%87%B1VS Mac with Fantomas2021-04-19T10:00:00+08:00https://realazy.comRealazy<p>In ObjC, <a href='https://developer.apple.com/documentation/objectivec/nsobject/1418815-load?language=objc'><code>+load</code></a> or <a href='https://developer.apple.com/documentation/objectivec/nsobject/1418639-initialize?language=objc'><code>+initiliaze</code></a> of <code>NSObject</code> is a nice place to put some code that executes only once, e.g. register some dependency injections.</p><p>There is no such concept in F#, but can be archived with the <a href='https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/lazy-expressions'><code>lazy</code> expressions</a>:</p><pre class='fsharp'><code class='fsharp'>module Service = let private _getDef = lazy ( DicService.registerHttpMessageHandler (new NSUrlSessionHandler ()) (fun word -> DicService.getDefinition word platformSpecFunc)) let getDef = _getDef.Force()</code></pre><p>By using <code>lazy</code>, our <code>_getDef</code> will execute at the first time when <code>getDef</code> being called, and only once.</p>%1FH=%8B%CBa%F4%A3-%EF~%A5,Hq%D5Load once with `lazy`2021-01-28T10:00:00+08:00https://realazy.comRealazy<p>Asserting exceptions is a feature of FsUnit, but it's hard to do it right at first try:</p><pre class='fsharp'><code class='fsharp'>[<Fact>]let ``Test bool for keypath with exn`` () = let jdoc = JsonDocument.Parse """{ "a" : {"b": 1}}""" jdoc.RootElement |> JPath.bool "a.b" |> should throw typeof<InvalidOperationException></code></pre><p>The correct way, is a little tricky, you must wrap the function or method that may throw exceptions inside a function:</p><pre class='fsharp'><code class='fsharp'>[<Fact>]let ``Test bool for keypath with exn`` () = let jdoc = JsonDocument.Parse """{ "a" : {"b": 1}}""" (fun () -> jdoc.RootElement |> JPath.bool "a.b" |> ignore) |> should throw typeof<InvalidOperationException></code></pre>%94%A8%B4EZ%BC%FC%B3%EB&%5EA%01%C7%AC%A7Asserting Exceptions in FsUnit2021-01-17T10:00:00+08:00https://realazy.comRealazy<p>最近发现有个设计风格(不知道是不是趋势),如下图,是一种浸入式的底边框。</p><p><img src='/assets/til/2020-12-07.png' alt='浸入式边框' /></p><p>直接用 <code>border-bottom</code> 肯定不行,来个 <code>:after</code> 伪元素细调,比较麻烦。使用 <code>background</code> 来平铺纯色背景是一个不错的主意,但如果定死图片高度,不够 responsive。通过 CSS 来画背景,具体要显示多少,我们则可以拜托 <code>background-size</code>:</p><pre class='css'><code class='css'>display: inline-block; /* 让背景宽度跟随元素自身宽度 */background: linear-gradient(#6ee7b7, #6ee7b7) left bottom no-repeat;background-size: 100% 40%; /* 划重点 */</code></pre><p>访问 <a href='/assets/til/2020-12-07.html'>demo</a>。</p><p><code>backgroud-size</code> 可参考 <a href='https://developer.mozilla.org/en-US/docs/Web/CSS/background-size'>https://developer.mozilla.org/en-US/docs/Web/CSS/background-size</a>。</p>r%0F%E9%B6j%92%B0%AFv%D1%91%B7%19%B1?:CSS `background-size` 妙用一则2020-12-07T10:00:00+08:00https://realazy.comRealazy<p>经常看到一些字段和值排列的代码对齐非常整洁,即便字段长度和值长度不一。举个例子:</p><p><img src='/assets/til/2020-12-06.png' alt='(js-mode . eglot-ensure)(html-mode . eglot-ensure)(css-mode . eglot-ensure)(json-mode . eglot-ensure)(svelte-mode . eglot-ensure)' /></p><p>在 Emacs 里,这类对齐有内置支持,使用命令 <code>align-regexp</code>,输入需要对齐的字符(此处为 <code>.</code>),立马见效。</p>H&%04V%FC%04g%9Cq%E3*%E1%B7%7B%7FPEmacs 特定字符对齐2020-12-06T10:00:00+08:00https://realazy.comRealazy<p>我最近喜欢用 <a href='https://www.marksimonson.com/fonts/view/anonymous-pro'>Anonymous Pro</a> 这款字体作为代码字体,不过可能是年老昏花,只喜欢它的粗体,就是说我希望把粗体当常规体用。这样的话麻烦很多,英文是粗体,系统提供的中文字体,常规体不够粗,粗体又太粗了,无法搭配。于是我从<a href='http://www.foundertype.com'>方正字库</a>找到一款相对般配的字体「方正方俊黑」(自用免费),寻思着怎么把它们合成一个新字体,英文部分用 Anonymous Pro,中文部分使用方正方俊黑。一番搜索,参考 <a href='http://7thgen.info/blog/2008/07/merging-font-with-fontforge/'>http://7thgen.info/blog/2008/07/merging-font-with-fontforge/</a>,使用 <a href='https://fontforge.org'>fontforge</a> 搞定。</p><p>首先将所需字体拷贝到某个目录下,保存以下脚本为 merge.pe:</p><pre class='sh'><code class='sh'># 先处理中文字体,生成一个临时 tmp.ttfOpen("FZFangJHJW_Zhong.TTF")SelectAll()ScaleToEm(1024)Generate("tmp.ttf")Close()# 打开英文字体,合并上面生产的 tmp.ttfOpen("Anonymous Pro Minus B.ttf")SelectAll()ScaleToEm(1024)MergeFonts("tmp.ttf")SetFontNames("AnonymousProFZFangJH", "AnonymousPro FZFangJH", "AnonymousPro FZFangJH Regular", "Regular", "")# 必须覆盖原来字体的 name tableSetTTFName(0, 1, "AnonymousPro FZFangJH")SetTTFName(0, 2, "Regular")SetTTFName(0, 3, "AnonymousProFZFangJH:Regular")SetTTFName(0, 4, "AnonymousPro FZFangJH Regular")SetTTFName(0, 6, "AnonymousProFZFangJH-Regular")SetTTFName(0, 16, "AnonymousPro FZFangJH")SetTTFName(0, 17, "Regular")SetTTFName(0x409, 1, "AnonymousPro FZFangJH")SetTTFName(0x409, 2, "Regular")SetTTFName(0x409, 3, "AnonymousProFZFangJH:Regular")SetTTFName(0x409, 4, "AnonymousPro FZFangJH Regular")SetTTFName(0x409, 6, "AnonymousProFZFangJH-Regular")SetTTFName(0x409, 16, "AnonymousPro FZFangJH")SetTTFName(0x409, 17, "Regular")# 生成最终字体Generate("AnonymousPro FZFangJH.ttf")Close()</code></pre><p>然后运行 <code>fontforge -script merge.pe</code>,最终安装生成的字体即可。</p><p>如果你想生成斜体(并不推荐,逼不得已的下下策),可以在 <code>SelectAll()</code> 之后添加:</p><pre class='sh'><code class='sh'>Italic(-10, 0, 2, 2)SetItalicAngle(-10)</code></pre><p>附效果图一张(点击看原图):</p><p><a href='/assets/til/2020-12-03.png'><img src='/assets/til/2020-12-03.gif' alt='效果图' /></a></p>PL%14%86%9E%1E%DC.k=%9Cm%CE@u!合并字体2020-12-03T10:00:00+08:00https://realazy.comRealazy<p>2021-02-07 更新:</p><p>Homebrew <a href='https://brew.sh/2021/02/05/homebrew-3.0.0/'>已支持 M1</a>,以下内容过期,请参考官方安装方案。</p><h2 id="Homebrew-ARM-M1">安装 Homebrew (ARM/M1)</h2><p>由于 Homebrew 尚未完全支持 ARM 架构的 Macbook Air/Pro M1,安装时不要用默认的安装脚本,那是默认给 Intel macOS 准备的。可以按此步骤:</p><pre class='sh'><code class='sh'>sudo mkdir /opt/homebrewsudo chown $(whoami) /opt/homebrewcurl -L https://github.com/Homebrew/brew/tarball/master | tar xz --strip 1 -C /opt/homebrew</code></pre><p>别忘了把 <code>/opt/homebrew/bin</code> 添加到你的 <code>PATH</code> 里(如果你没概念,打开主目录的 <code>.zshrc</code> 文件,添加 <code>export PATH=/opt/homebrew/bin:$PATH</code>)。</p><p>这样,Homebrew 安装或编译的软件都将是 ARM 架构的,对 M1 来说也就是 native 的。</p><p>如果你发现某些程序无法用 Homebrew 编译通过,那么你还需要安装一份 Intel 架构的 Homebrew,使用罗塞塔来运行。</p><h2 id="Homebrew-Intel">安装 Homebrew(Intel)</h2><pre class='sh'><code class='sh'>arch -x86_64 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)"</code></pre><h2 id="Homebrew">使用 Homebrew</h2><p>为例避免两个 brew 相冲突,我的做法是:</p><pre class='sh'><code class='sh'>alias xbrew="arch -x86_64 /usr/local/bin/brew"alias abrew="/opt/homebrew/bin/brew"</code></pre><p>我优先使用 <code>abrew</code>(即支持 <strong>A</strong>RM 架构),当我发现 <code>abrew</code> 无法安装时(毕竟 Homebrew 还有很多软件未兼容 ARM),我再使用 <code>xbrew</code>。</p><p>(2020-11-25 更新)</p>=%C4%B6@Uy_%D4%D1q%E4%8B%FEF%8D%14在 MacBook Pro M1 上安装使用 Homebrew2020-11-21T10:00:00+08:00https://realazy.comRealazy<p>了解 <code>sudo</code> 后,大家伙可能会毫不犹豫地使用类似的命令:</p><pre class='sh'><code class='sh'>sudo echo "127.0.0.1 realazy.com" >> /etc/hosts</code></pre><p>duang!一般你会收到类似 <code>permission denied: /etc/hosts</code> 的反馈。瓦特,权限不足?不是已经 <code>sudo</code> 了吗?!</p><p>还真是权限不足,在这里,<code>sudo</code> 管的是 <code>echo</code>,像 <code>>></code>、<code>></code>、<code><</code> 和 <code>|</code> 等操作符,是由 shell 掌管的,而不是 <code>echo</code> 等具体程序。<code>echo</code> 及其他的 shell 程序,是不知道 <code>|</code> 等操作符存在的,只管读输入,写输出。因此,你需要将 <code>echo</code> 的输出交给其他程序如 <code>tee</code>,<code>sudo</code> 权限也赋予 <code>tee</code>,才能成功完成相关操作:</p><pre class='sh'><code class='sh'>echo "127.0.0.1 realazy.com" | sudo tee -a /etc/hosts</code></pre><p>详情请参阅:<a href='https://missing-semester-cn.github.io/2020/course-shell/#一个功能全面又强大的工具'>https://missing-semester-cn.github.io/2020/course-shell/#一个功能全面又强大的工具</a></p>%02MO%60%F6%98s%94%03CV+mU%8D%8FShell 输入输出与 `sudo`2020-11-18T10:00:00+08:00https://realazy.comRealazy<p>说到 HTTP method,平常我们打交道最多的莫过于 <code>GET</code> 和 <code>POST</code> 了。看看<a href='https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods'>这里</a>列出的 methods,是不是有些根本无用武之地?</p><p>比较有意思,也可能会用得上的,应该是 <code>HEAD</code> 这个 method。它只检查 HTTP head,不会下载 body。在一些场景,如只想检查某个资源是否存在/在线,或只想了解这个资源的大小(读取 <code>Content-Length</code>),会非常有用。</p><p>以 <code>fetch</code> 来举个例子:</p><pre class='javascript'><code class='javascript'>(async () => { let resp; try { resp = await fetch("/resource", { method: "HEAD" }); } catch (e) { // 无法 fetch,可能是网络原因 return; } if (resp.ok) { // 好的,`/resource` 可用,让我们看看它有多大 const len = resp.headers.get("Content-Length"); console.info(len); } else { // Uh-oh }})();</code></pre><p>Discovered in <a href='https://twitter.com/codepo8/status/1319691862270222338'>https://twitter.com/codepo8/status/1319691862270222338</a></p>%3B%3C%86%CB%18%D5%B7%B4x%19j%B8:%1A%8D%F5HTTP HEAD method2020-11-17T10:00:00+08:00https://realazy.comRealazy<p>看到类似以下的一段 <a href='https://www.compositional-it.com/news-blog/yielding-options-in-list-comprehension-expressions'>F# 代码</a>:</p><pre class='fsharp'><code class='fsharp'>let opt = Some "C"let lst = [ "A" "B" match opt with | Some v -> v | None -> () ]</code></pre><p>直觉这是错的,一眼看上去,如果这个列表是 <code>string list</code> 类型,<code>opt</code> 为 <code>None</code> 时值是 <code>unit</code>,类型匹配不上。但是到 <code>fsi</code> 里一跑,竟然通过了。一番研究,发现这其实等同于:</p><pre class='fsharp'><code class='fsharp'>let lst = [ yield "A" yield "B" match opt with | Some v -> yield v | None -> () ]</code></pre><p>这是 implicit yields,<a href='https://docs.microsoft.com/en-us/dotnet/fsharp/whats-new/fsharp-47#implicit-yields'>F# 4.7 新增的特性</a>,<a href='https://github.com/fsharp/fslang-design/blob/master/FSharp-4.7/FS-1069-implicit-yields.md'>初衷</a>是让 Fable, Fabulous 等框架的模版写法更简洁。</p>%12%A6H%05%AC%EE-v%90RQ%FE%CE/%5E%95F# Implicit yields2020-11-16T10:00:00+08:00https://realazy.comRealazy<h2 id="">格式</h2><pre class='js'><code class='js'>// 必须使用 u flag/\p{UnicodePropertyName}/u/\p{UnicodePropertyName=UnicodePropertyValue}/u/\p{UnicodeBinaryPropertyName}/u// 大写 P 则反向匹配/\P{UnicodePropertyValue}/u</code></pre><h3 id="UnicodePropertyName-UnicodePropertyValue"><code>UnicodePropertyName</code>/<code>UnicodePropertyValue</code> 取值参考:</h3><ul><li><a href='https://unicode.org/reports/tr18/#General_Category_Property'>https://unicode.org/reports/tr18/#General_Category_Property</a></li><li><a href='https://unicode.org/reports/tr24/#Script'>https://unicode.org/reports/tr24/#Script</a></li><li><a href='https://tc39.es/ecma262/#table-binary-unicode-properties'>https://tc39.es/ecma262/#table-binary-unicode-properties</a></li></ul><h2 id="">备忘</h2><ul><li>匹配中日韩越表意字符(汉字):<code>/\p{Ideo}/u</code></li></ul>%5C%A7%B2%85z%98p%126Y%CD1%EE~%AExUnicode 字符属性正则匹配2020-11-15T10:00:00+08:00https://realazy.comRealazy<p>升级 macOS Big Sur 后,<a href='https://github.com/koekeishiya/yabai'>yabai</a> scripting-addition 无法再直接运行,需手动或在 <code>.yabairc</code> 里执行 <code>sudo yabai --load-sa</code>。涉及到 <code>sudo</code>,默认的帐户配置是需要输入密码的,如果在 <code>sudo</code> 在背景执行的脚本里,没有机会输入密码导致启动失败。手动执行每次输入也会非常的繁琐。因此需给当前帐户加上免密执行 <code>sudo</code> 的权限。</p><p>运行:</p><pre class='sh'><code class='sh'>sudo visudo -f /etc/sudoers.d/sudoers</code></pre><p>添加:</p><pre class='sh'><code class='sh'># 替换 `your-user-name` 为你的帐户名your-user-name ALL=(ALL) NOPASSWD: ALL</code></pre>%00%B5%9A%EB%90%00%9Dq%0B%7D6%D6%E8n%05%FBsudo 免密码2020-11-14T10:00:00+08:00https://realazy.comRealazy