アイキャッチ画像: デスクの上でパソコンを使用している

これまでいくつか Drupal 関連のコーディングスタンダードの日本語訳を試みてきました。

これまでのものはいずれも Drupal のベースとなる PHP のコーディングスタンダードでしたが、今回は少し趣向を変えて JavaScript のライブラリである「 jQuery 」 のコーディングスタンダードをご紹介したいと思います。

jQuery と Drupal の関係をあまりよく知らない方のために少し解説を。

jQuery とは 2014 年現在世界で最も広く使われている JavaScript ライブラリのひとつです。 Drupal では標準の JavaScript ライブラリとして Drupal のコアに同梱される形での使い方がされています。 Drupal の次期メジャーアップデートである Drupal 8 では jQuery の他にもいくつかの Javascript ライブラリがコアに同梱される方向で話が進んでいるようですが、 Drupal 7 の場合は jQuery が実質唯一の標準 JavaScript ライブラリ、というような位置づけとなっています。

そんな jQuery のコーディングスタンダードが Drupal.org で提案されているので、今回はそれを翻訳してみました。

以下がその翻訳文となります。 2013 年 8 月 13 日更新版をもとにしています。なお、前回までと同様、わかりやすさを心がけて、ところによっては直訳、ところによっては意訳としています。原文の正確なニュアンスを知りたい方はぜひ原文の方にあたってみてください。

jQuery オブジェクトを表す変数の先頭にはドルマーク ($) をつけましょう

コードのあらゆる部分において、どの変数が jQuery オブジェクトでどの変数がそうでないかをわかりやすくするべきです。

まちがい

var foo = $('.foo');
var object = { bar: $('.bar') };

正解

var $foo = $('.foo');
var object = { $bar: $('.bar') };

互換性の問題を回避しましょう

jQuery は素早く進化していますが、以前のバージョンに対する後方互換性を保とうとしています。 公式にはひとつの Drupal メジャーリリースでサポートされるのはひとつのバージョンの jQuery だけですが、多くのサイトは jQuery update モジュールを使って jQuery の新しいバージョンが提供するパフォーマンスやバグフィックスのアドバンテージを得ています。 jQuery を使用するカスタムモジュールでは最新のシンタックスのベストプラクティスを使って、更新バージョンとの衝突を防ぐようにしましょう。

チェイニング

チェイニングのセレクタについては、 CSS メソッド $(' a b > c') と JavaScript チェイニングの $('a').find('b').children('c') のどちらを使ってもかまいません。 後者の方がわずかに高速です(テスト)。 どちらの場合でも、(もし可能なら)セレクタの重みを減らすようにしましょう。 .children() と .find() が同じ結果を返す場合は .find() メソッドを使いましょう(テスト)。

まちがい

   this.$el
       .find('.contextual-links')
       .prop('hidden', !isOpen);

正解

    this.$el.find('.contextual-links')
      .prop('hidden', !isOpen);

訳注:上記の例では、「まちがい」と「正解」のコード例が改行の位置を除き同一の内容となっています。おそらくこれはまちがいで、本来はセレクタの重みが異なる 2 つの例が示されたりすべきところかと思います。

イベントの委譲

JavaScript におけるイベント( click や mouseover など)はすべて親要素の方向に DOM ツリーを「バブル」アップします。 これはたくさんの要素に対して同じ関数を適用したい場合に信じられないほど役に立ちます。 すべての要素にイベントリスナ関数をバインドする代わりに、それらの親要素に一度だけバインドしてどのノードがイベントをトリガしたのかを把握させる、ということができます。

jQuery コードの例で、イベントのデフォルトのふるまいを止めるためにイベントがシンプルに return false; とやっているのをよく目にするかもしれません。 このタイプの防止策はよくまちがって使われますし、イベントの「バブル」伝播も防いでしまいます。 この効果は必ずしも望ましいものではありません。 デフォルトのふるまいを止めたり伝播を止めたりしたい場合は、それらを明示的に記述するようにしましょう。

まちがい

$('.item').click(function(event) {
  ...
  // event.preventDefault() と event.stopPropagation() の両方を止める。
  return false;
});

正解 (Drupal 7)

$menus.delegate('.item', 'click', function(event) {
  ...
  // デフォルトのふるまい( click )を止める。
  event.preventDefault();
  // 伝播(「バブリング」)を止める。
  event.stopPropagation();

});

正解 (Drupal 8)

$menus.on('click', '.item', function(event) {
  ...
  // デフォルトのイベントのふるまい( click )を止める。
  event.preventDefault();
  // Prevents the event from propagating (ie: "bubbling").
  // イベントの伝播(「バブリング」)を止める。
  event.stopPropagation();
});

関数

関数を別個の関数に分けて、呼び出しが必要なときに呼ぶようにしましょう。

まちがい

// これはクリックのときだけ呼び出せる。
$('.btn').click( function() { ... });

正解

// これはどこでも呼び出せる。
function clickFunction() { ... };
$('div').click( function() {clickFunction() });

コンテクスト

常にセレクタにはコンテクストを与えるようにしましょう。 デフォルトのコンテクストはページ全体の DOM を探索します。 コンテクストがキャッシュされたセレクタの場合は find() を使いましょう。

まちがい

// これはコンテクストを持っておらず非常に低速です。
var $element = $('.element');

改善版

// #sidebar の中だけで探します。
var $element = $('.element', '#sidebar');

正解

var $sidebar = $('#sidebar');
var $element = $sidebar.find('.element');

#id と .class の使用

タグ ID での要素の探索はクラス名での探索よりずっと高速です(テスト)。 ターゲットとする要素がページにひとつだけ存在する場合は、 #id の形で選択しましょう。 要素が 2 つ以上ある場合は #id で絞り込んだうえでクラスを使いましょう。 上のコンテクストコードの例を見てください。

jQuery.attr()

これは D6 と D7 にのみ適用されるものです。 というのは、 D8 は jQuery 1.9+ とともに配布されており、 jQuery 1.9+ ではプロパティの取得に .prop() を使うからです。 .attr のまちがった使い方は、多くの互換性の問題のもととなります。 プロパティをセットするときは空文字列ではなく論理値を使いましょう。 また、戻り値として返されるプロパティの値がいつも論理値であると仮定しないようにしましょう。 jQuery のバージョンとマークアップによって、 .attr('checked') は true を返したり 'checked' を返したりします。

まちがい

$element.attr('disabled', '');
if ($element.attr('checked') === true) { ... }

正解

$element.attr('disabled', false);
$element.attr('disabled', true);
if ($element.attr('checked')) { ... }

jQuery.each()

このメソッドは、インスタンス化された jQuery オブジェクトに繰り返し処理を行うときにたいへん強力で(そして適切なもので)す。

これはよく、通常のシンプルな JavaScript の配列やオブジェクトに対して繰り返しが必要なときにまちがって使われています。 ネイティブな JavaScript の for ループは jQuery.each() の 300 〜 1000 倍高速です(テスト)。

まちがい

var array = [ ... ];
$.each(array, function(i, item) { ... });

正解

var array = [ ... ];
for(var i, len = array.length; i < len; i += 1){
  var element = array[i];
}

・・・以上です。

いかがだったでしょうか?

ポイントは比較的少ないですが、いずれももっともなポイントが指摘されているかと思います。

PHP の場合と同じように JavaScript や jQuery においても、人にやさしく自分にやさしい「リーダブルコード」「ビューティフルコード」を目指していきたいものです。