Front End Development Guidelinesの読書メモ
2011/05/15(日) 24:29 Web親記事へこのエントリーをはてなブックマークに追加

Front End Development Guidelinesを読みながら書いたメモ
(翻訳ではないですよ、少なくても訳だと思って読んではいけない)

簡単な内容紹介

Accessibility/HTML/CSS/JavaScript/jQuery/について基本的な事や知っておくべき、意識しておくべき事などについて書かれたドキュメントです。
あちこち触れている感じなので、一つ一つは余り濃くない内容が多めです。

Accessibility

What's Up, DOCTYPE?

DOCTYPEがないのは論外として、今までは以下のようなスニペットを使っていた人もいるだろう。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
HTML5ではもっとスリムにしたスニペットを利用できます。
このスニペットはモダンなブラウザはサポートしていますし、IE6とIE7も標準モードとして動作します。
<!DOCTYPE html>

Write Valid Semantic Markup

見出しは<h1>などから階層的に作られるべきだし、パラグラフはいつも<p>タグ内にあるべきだし...などなど。
セマンティックなHTMLを書けば、自ずと綺麗で軽くて、検索エンジンロボットがパースしやすいものができるだろう。
Which do you think looks cleaner, this?:
<span class="sectionHeading">A Heading</span>
<br /> <br />
Lorem ipsum dolor sit amet. ...
<br /> <br />
どっちか綺麗だとおもいますか?
<h1>A Heading</h1>
<p>
    Lorem ipsum dolor sit amet. ...
</p>

Use Microformats

Microformatsはコンタクト情報を機械的に読みやすくする方法です。

Images Need ‘Alt’ Text

<img>タグはvalidate と アクセシビリティガイドラインのためにaltテキストを必要とします。
alt属性はその画像が何を表示しているか、または何を取得しようとしているかを説明するテキストが入ります。

箇条書きで些細なアイコンを<img>で表示する時、alt属性は空にすることを推奨します。(属性をなくすわけではない)
空にすればスクリーンリーダーはそれを無視できるため、"bullet"を何回も読まずにすみます。
<img src="dog.gif" alt="Fido and I at the park!" />
<!-- good, descriptive -->
 
<img src="bullet.gif" alt="bullet" />
<!-- bad, as silly as it seems -->
 
<img src="bullet.gif" alt="" />
<!-- good -->

Use Tables for Tabular Data Only

テーブルは表形式のデータ表示に使用されるべきです。
例外としてHTML emailの場合は他の用途に使うのもありかもしれない。
アクセシビリティとしては、テーブルの見出しはいつも<th>タグを使って表示するべきです。
<table cellpadding="0" cellspacing="0" border="0">
    <thead>
        <tr>
            <th>
                Cell Header
            </th>
        </tr>
    </thead>
    <tbody>
        <tr>
            <td>
                Cell Item
            </td>
        </tr>
    </tbody>
</table>

Use jQuery & jQuery UI Widgets

jQuery UIはWAI WCAG 2.0とWAI ARIAの規格に対応したデザインがされているので、そういうフレームワークを使うことで不確実な要素は減ると思います。

JavaScript

Whitespacing & Formatting

括弧内のホワイトスペースの配置をどうするかというルールの問題。
既存のドキュメントをいじる場合は、元のドキュメントを尊重して維持するべきです。(文章全体の書式を変える時は除く)複数の書式の混在は混乱させる原因なので統一しましょう。
(原文にはないけどこういうイメージ)
TabとSpaceを混在させた末路
一貫性あるコードは読みやすいので、コーディングルールを定める事が大事で、
今回紹介するルールはJQuery Core Style Guidelines - jQuery JavaScript Libraryと比較的似ている。
Character Spacing
// Bad
if(blah==="foo"){
    foo("bar");
}
 
// Good :)
if (blah === "foo") {
    foo("bar");
}
Same Line Braces
(デバッガー的にも無駄な行数は少ない方がいい)
// Bad
if (foo)
{
    bar();
}
 
// Good :)
if (foo) {
    bar();
}
Always Using Braces
常に中括弧を省略しない
// Bad
if (foo)
    bar();
 
// Good :)
if (foo) {
    bar();
}
String Handling
文字列はいつも"ダブルクオート"で囲む。

Commenting

一行コメントはできるだけ短く、簡潔に説明する。
もっと説明文が多く必要になるなら、ブロックコメントを使用する。
var zeroAsAString = "0";
 
// Checks if zeroString is 0 and then runs function doHeapsOfStuff()
if (zeroAsAString === 0) {
    doHeapsOfStuff(param1, param2)
}
 
/*
    Initiated When: zeroAsAString === 0
    Parameters: param1, param2
*/
function doHeapsOfStuff() {
     
}

Always Use === Comparison

==等価演算子はバグの原因になりやすいので、===厳密等価演算子を使うべき。
これはJavaScript Gardenを見ると詳しく紹介されている。
var zeroAsAString = "0";
 
if (zeroAsAString == 0) {
    // これがtrueになってしまう
}
 
if (zeroAsAString === 0) {
    // こっちはfalse
}
The Exception
例外としてnullとの等価比較をする際は==を使ってもいい。
なぜならfoo == nullはnull or undefinedであるかを判定できるためである。
ちゃんと理解しているならば==を使える場合もある、そうでないなら===を使いましょう。
(ここのコード例、何か間違ってたので勝手に修正)
var foo = null;
var bar;
 
// foo は null, bar は undefined
if (foo == null && bar == null) {
    // ここに入る
}

Always Specify the Second ‘radix’ Parameter When Using .parseInt()

parseIntではいつも第二引数に'radix'パラメーターを指定しましょう。
0などか始まる数字と意図しない動作になることがあり得るため
alert( parseInt("08") ); // alerts: 2 <= bad
 
alert( parseInt("08", 10) ); // alerts: 8   <= good
strict modeを使うとかもありなのかもね。

Avoid Comparing to true and false

比較演算子を使ってtrueやfalseと直接比較するのは不要だと思います。
次のようにして簡略化した形で書くのも良いでしょう。
if (foo === true) {
    // good 厳密比較演算子でやるのは悪くないですが冗長
}
 
if (foo) {
    // yay!
}
 
if (!bar) {
    // the opposite
}

Avoid Polluting the Global Namespace

グローバルな名前空間は一つのオブジェクトにまとめて、グローバル名前空間を使うのはできるだけ避けましょう。
// polluted global name space
var settingA = true;
var settingB = false;
var settingC = "test";
 
// a settings namespace

var settings = {
    settingA: true,
    settingB: false,
    settingC: "test"
}

Camel Case Variables

変数はキャメルケースで宣言するのが一般的です。
(定数なんてものは無いけど、そのような変数は大文字_大文字のルールでやるのいいじゃないかな)
var MOUSE_TIMEOUT = 30; // bad
var mouseTimeout = 30; // good :)

Readable Milliseconds

ミリ秒は読みやすい形式で書きましょう。
(どうみても好み…)
// is this 3, 30 or 300 seconds?
var timeout = 30000;
 
// an extra calculation, but easier to read and modify
var timeout = 30 * 1000; 
// これもあり?
var timeout = 3e4; 

Loop Performance - Cache Array Length

ループ時に毎回lengthを参照するのは無駄だからキャッシュしてループを回す
var toLoop = new Array(1000);
 
for (var i = 0; i < toLoop.length; i++) {
    // BAD - the length has to be evaluated 1000 times
}
 
for (var i = 0, len = toLoop.length; i < len; i++) {
    // GOOD - the length is only looked up once and then cached
}
The Exception
例外としては、ループを回してそのアイテムを削除したり、アイテムを追加する場合はlenghtが変化するため、
意図しない動作になることがあるため、lengthを毎回参照する事もある。

Don't Send Too Many Function Parameters

あまりたくさんの引数をもった関数を作らない
function greet(name, language, age, gender, hairColour, eyeColour) {
    alert(name);
}
オブジェクトをとるようにして、プロパティを参照するようにするとスマート
function greet(user) {
    alert(user.name);
}
 
greet({
    name: "Bob",
    gender: "male"
});

Remap ‘this’ to ‘self’

オブジェクト指向なJavaScriptを書く際にはthisのスコープを理解すべきです。
thisは呼び出し元によって異なるため、次のような場合同じfindFriend内でもthisの参照先が異なります。
Bob.findFriend("Barry");
 
Person.prototype.findFriend = function(toFind) {
    // this = Bob
    $(this.friends).each(function() {
        // this = Bob.friends[i]
        if (this.name === toFind) {
            // this = Barry
            return this;
        }
    });
}
難しく考えずにthisをselfにリマップするという解決方法が
(うーん、何かこの辺ちょっと意図がよく分からない)
Bob.findFriend("Barry");
 
Person.prototype.findFriend = function(toFind) {
 
    // the only time "this" is used
    var self = this;
     
    $(self.friends).each(function(i,item) {
        if (item.name === toFind) {
            return item;
        }
    });
     
}

CanIHaz Boolean?

真偽値を持つものはprefixにis,can,hasなどをもった名前にするべきです。
isEditing = true; 
obj.canEdit = true;
user.hasPermission = true;

Feature Sniff, Don't Browser Sniff

ブラウザ判定を使用しないで、機能を持っているかで判定するべきです。
if ($.browser.msie) {
    // 使うべきではない
} 
ブラウザの変化は早く、以前のバージョンではだめだったものが現在のバージョンだと使える機能もあるため、
ブラウザ(UA)で判定すると、機能があるのに動作しないと行ったことが起こりえる。
Modernizrという広く使われているライブラリがあり、これを使用すると機能テストの結果をModernizrオブジェクトに持つため、以下のようにサポートしている機能チェックをシンプルに書くことができます。
// old way of detecting canvas support
if (!!document.createElement('canvas').getContext) { ... }
 
// with Modernizr
if (Modernizr.canvas) { ... }

jQuery Specific

Chain Like a Sick Bitch

jQueryの良いところとしてメソッドチェーンがある。
これは.end()についても知るとより便利になる。
$(".quote")
    .hide()
    .find("a").text("Click here").bind("click",doStuff).end() // $(".quote")に
    .parent().removeClass().addClass("testimonial").draggable().end() // $(".quote")に
    .fadeIn("slow");
.end()を使う事でメソッドチェーンをつなげる事ができる(ただこれデバッグしにくいのであんまり好きじゃない)

‘.stop()’ Collaborate & Listen

jQuery animationsをマウスイベントに付けるとき、注意点がある。
この記事では、1個目例で高速にメニューを移動すると気持ち悪い動きになることが紹介されている。
これは$.animateの前に$.stopを呼び出す事で解決できます。
.animate()を使う際にqueue: falseのパラメーターを指定することで、連鎖反応を防止することもできます。$.fadeIn や $.slideDownなどにはqueueの設定がないため、アニメーションさせる前に$.stopメソッドを実行して、現在動作中のアニメーションを止める必要があります。
$.stop()で止めるだけだと次回時にアニメーションしたときにおかしくなることがあるので、.stopのドキュメントを見て分かるようにclearQueue と jumpToEndの引数についても知っておく必要があるでしょう。
$("selector").stop(true,true).fadeOut();
 
$("selector").animate({
    property: value
}, {
    duration: 1000,
    queue: false
}

Optimise Your Selectors

jQueryはSizzleセレクタエンジンを使用している。
セレクタをもっとよくする戦略としては次の二つがある。
  • セレクタをキャッシュ
  • 効率のいいセレクタを使う
Caching Selector Results
何かする度にセレクタで検索するのは非効率です。
// before
$(".quote a").bind("click", doStuff); // DOM query eww
 
// now
$(".quote a").addClass("quoteLink"); // DOM query eww
 
// later
$(".quote a").fadeIn("slow"); // DOM query eww
これをもっとよくするには、一度変数にセレクタ検索の結果を格納して使うのがいい。
// before
var $quoteLinks = $(".quote a");  // the only DOM query
$quoteLinks.bind("click", doStuff);
 
// now
$quoteLinks.addClass("quoteLink");
 
// later
$quoteLinks.fadeIn("slow");
Using Efficient Selectors
jQuery/sizzleJSはブラウザネイティブのdocument.querySelector()が使えるならそちらを使い、
そうでないなら自力でセレクタをパースしている。
(何かこの辺も文章と例の関係が…)
先にcontextを絞れるように書いた方がコストは低くてすむ。
// an ID search is the quickest possible query, then it just takes a list of the childNodes and matches the class
$("#quoteList").children(".quotes");
 
// looks for the "foo" class only in the pre-defined bar element
// fooクラスをbarをcontextにして検索する
$(".foo",bar); 
参考(JavaScriptの所は微妙だけど)

A ‘for’ Loop is Always Quicker Than a ‘each()’ Loop

jQueryの$.each()よりもネイティブforループの方がいつも早いだろう。
ネイティブの方が早いのは常だが、それは実行速度とオーサリング速度とのトレードオフである。
forループのようなものは一度に何度も実行するため、幾つかの面でパフォーマンスに影響を与える事があるので、使いどころには注意しよう。
  • Mouse movement
  • Timer intervals
  • Loops within loops

CSS

Whitespacing

CSSでホワイトスペースを入れて見やすくする。
Proper Spacing
/* BAD */
.selector {display:none;background:#ff0000;color:#000000;}
 
/* GOOD - SINGLE LINE */
.selector { display: none; background: #ff0000; color: #000000; }
 
/* GOOD - MULTI-LINE */
.selector {
    display: none;
    background: #ff0000;
    color: #000000;
} 
Same Line Braces
.selector {
    display: none;
    background: #ff0000;
    color: #000000;
}
Indenting Child Elements
個人的に使用する目的、一行で宣言できるという場合に子要素にインデントを付けるのもありかもしれない
(Sass使いましょう)
.selector { display: none; background: #ff0000; color: #000000; }
    .selector a { text-decoration: none; }
    .selector span { font-weight: bold; }
Grouping & Indenting Vendor Prefixes
ベンダープレフィックスは見やすいようにインデントする。
.selector {
    background: #FFF; border: 1px solid #000; color: #EAEAEA;
        -webkit-border-radius: 3px;
        -moz-border-radius: 3px;
        border-radius: 3px;
}

CSS Shorthand

CSSの簡潔な表現方法について
paddingやmarginなどは時計回りにまとめて指定でき、さらに縦横でまとめて書くこともできる。
/* LONG CODE IS LONG */
padding-top: 1px;
padding-right: 2px;
padding-bottom: 1px;
padding-left: 2px;
 
/* BETTER */
padding: 1px 2px 1px 2px;
 
/* BEST */
padding: 1px 2px;
From 0px to Hero
プロパティ値に0を割り当てるときにtypeは冗長である。
/* BAD */
padding: 0px 10px;
/* GOOD */
padding: 0 10px;

Clearing Floats

いわゆるclearfix系
The HTML:
<div class="parentElement">
    <div class="childElement">
        I'm floated left!
    </div>
    I'm normal text that wraps around the float
</div>
The CSS:
.parentElement {
    width: 100%;
    overflow: hidden;
}
.childElement {
    float: left;
}
最近

Feature Sniff, Don't Browser Sniff

Modernizrを使えばそのCSS機能をサポートしてるかもわかるよ。
.my_elem {
   border: 1px inset #666;
}
 
.borderimage .my_elem {
   border: none;
   -webkit-border-image: url(fancy-border.png) 5 5 5 5 round;
   -moz-border-image: url(fancy-border.png) 5 5 5 5 round;
   border-image: url(fancy-border.png) 5 5 5 5 round;
}

You're Not !important

要素のstyle属性、!importantはとても強力だが使うのは避けましょう。
CSS selector の優先順位を理解して、プロパティの上書きをしましょう。
p { font-size: 12px; }
p.bio { font-size: 14px; }
CSS selector の優先順位については以下が参考になります。

Aggressive Degradation

CSS3などに対応していない古いブラウザに対してどうするか。
CSS3ボタンのようなものの場合、fallbackとして画像で表示するか、filterなどを使って表現するなどの方法もあるが、
それらを無視して貧弱なものを表示する手もある。
個人的なものならそういう方法もあり、古いブラウザに依存したものや大きな製品などは余りそういう手を使うべきではないが、詰まるところ "if your browser can't render a gradient or a box shadow, tough luck." ですね。

CSS3 & HTML5

Feature Sniff with Modernizr

さんざん紹介してるけど、ModernizrはHTML5やCSS3のfeatureが使えるかどうかを判定することができる。

@font-face Use and Abuse

カスタムフォントはそのフォントのEULA などをチェックして埋め込みに使用して良いものなどを調べる。
サーバーにフォント置くとそれを直接コピーすることができてしまうから、気をつける必要がある。

そのような事を慎重に検討した後Font Squirrel | Create Your Own @font-face Kitsなどを使って埋め込みフォントを使うといい。
@font-face KitsはFurther Hardening of the Bulletproof Syntax | Fontspringで必要とされているようなフォントファイルの種類をまとめて生成してくれる便利ツール。

Degradation

"CSS3の効果はモダンなブラウザを使っているユーザーへの報酬であるべき。"
リソースのセクションでは古いブラウザの範囲でHTML5/CSS3の機能を補助するものが含まれています。

Resources

Support and Suggestions

おわり

このドキュメントを作成したTait Brown (@taitems)さんに感謝を。
taitems/Front-End-Development-Guidelines - GitHubで疑問や問題などの報告を受け付けてるそうですよ。

感想

最初に言ってたけど、それぞれが薄めなのでJavaScriptについてももっと知りたい場合は本文中にもでてきたJavaScript Gardenを見た方がいいかもしれない。
多分、常日頃からWebをいじってる人には既知の事がほとんどで目新しい感じはしないと思います。体系的に幅広くまとめてみた感じはあって、ちょっとだれる部分もある。
一通り目を通してみて気になった部分を(もちろん原文ですよ)読むのがいいではないかと思います。
こういうまとまった文章をWebで公開してくださるのはとてもありがたいものです。

名前:  非公開コメント   

  • TB-URL  http://efcl.info/adiary/0117/tb/