CSSとJavaScriptどちらでも実装できるけど、どのように使い分けるのがよいかの解説

CSSでもJavaScriptでもできるけどどちらを使おうか、CSSとJavaScriptどのように使い分ければいいのだろうか、二つのうまい関係を構築するテクニックを紹介します。

サイトのキャプチャ

Building A Relationship Between CSS & JavaScript

下記は 各ポイントを意訳したものです。

はじめに

JavaScriptには数多くのライブラリ、jQuery, Prototype, Node.js, Backbone.js, Mustacheなどあり、非常に人気が高いです。これらは実際に非常に多く利用されており、時間をかければもっとよい方法があるかもしれないところでもそれらを使ってしまうこともあるでしょう。

JavaScript, CSS, HTMLはそれぞれ異なるものですが、ほとんどのライブラリではこれら3つ全てをからませたコンセプトとなっています。これはJavaScriptの機能は非常に強力なので、ドキュメント構造をつくるHTMLとスタイル情報をつくるCSSの役割にかぶってしまうためです。

ここではどれか一つのJavaScriptのライブラリを取り上げるものではありません。現状を把握し、どのようなゴールを目指してCSSとJavaScriptを再整理させることができるかについてを提供します。

スタイルの定義はCSSで:JavaScriptからCSSを遠ざける

CSSはさまざまなセレクタを使うことで、HTMLのフックとして利用することができます。これは新しいことではありません。
id, classだけでなく、あらゆるHTMLの要素を指定し、そのエレメントをスタイルするためにアクセスできます。また、JavaScriptでも同じことができます。これはJavaScriptから、そしてCSSからHTMLにアクセス可能であるということは、開発モデルとして成功した理由の一つです。インタラクティブなエレメントを使ってアプリケーションを構築する時、ドキュメントにスタイル情報を定義する前に、HTMLをJavaScriptから切り分けるのは難しくなります。

多くのフロントエンド開発者は、HTMLをクリーンな状態にすることを望みます。クリーンでスタティックなHTMLであることは素晴らしいことです。しかし、JavaScriptによって生成されたHTMLがセマンティックでないマークアップだらけになってしまったらそれは本当によいことでしょうか?

HTMLをクリーンに

jQueryを例にHTMLをクリーンにしてみます。
JavaScriptからCSSを遠ざけるのに便利なのは、jQueryのhide();メソッドです。インラインのスタイルを定義したコードをみてみましょう。

HTML

<div class="content-area" style="display:none;"></div>

これはdiv要素を非表示にするもので、スクリーンリーダーでも非表示にしてしまいます。jQueryのhide();のようなメソッドを使っても簡単に実現できます。目標とするエリアにスタイルを設定し、displayプロパティにnoneを与えます。簡単に実装できますが、アクセシビリティ的には良くありません。これはコンテンツがスクリーンリーダー上では実在しないということになってしまいます。
JavaScriptで直接スタイルを与えることは理想的ではないので、classを与えるようにしてみます。

CSS

.hide {
   display: none;
}

JavaScropt

$('.content-area').addClass('hide');

この状態ではまだdisplay:none;にアクセシビリティの問題がありますが、jQueryで組み込んだものではありません。CSSでコンテンツが正確にどのように非表示にするかコントロールができます。

CSS

.hide {
   position: absolute;
   top: -9999px;
   left: -9999px;
}

.remove {
   display: none;
}

この2つのclassはどちらも非表示の状態ですが、アクセシビリティの見地からは異なる機能です。このようなコードを見ることで、スタイル情報をCSSファイルで取り扱っていることを明確にします。
こういったユーティリティのclassを使うことは、JavaScriptをすっきりさせることに役立つだけでなく、オブジェクト指向のCSS開発モデルとして二重の役割を持たせることになります。フロントエンド開発のアプローチとし作成し、プロジェクト全体にも浸透させるとよいでしょう。

ユーザエクスペリエンスを犠牲にしないで使い分ける

アニメーションもJavaScriptとCSSのどちらでも実装できます。クリックするとカラーがアニメーションで変更するdiv要素を例に見てみましょう。

CSSアニメーション非対応ブラウザも考慮して、下記のライブラリを使用します。

HTMLは、カラーを変更するdiv要素とトリガーとなるbutton要素を配置します。

HTML

<body>
    <button type="button">Run Transition</button>
    <div id="cube"></div><!--/#cube-->
</body>

まずは、CSSを用意します。

CSS

#cube {
   height: 200px;
   width: 200px;
   background: orange;
   -webkit-transition: opacity linear .5s;
      -moz-transition: opacity linear .5s;
        -o-transition: opacity linear .5s;
           transition: opacity linear .5s;
}

.fade-out {
   opacity: 0;
}

JavaScriptに入る前に、Modernizrを使ったフローを把握しておきましょう。

  1. Modernizrは、ブラウザのCSSアニメーションのサポートをチェックします。
  2. サポートしていたら、
    1. ボタンにクリックのイベントを設定し、#cubeに.fade-outを加えます。
    2. DOMから#cubeを取り除く機能をアニメーションが終了する時に加えます。
  3. 非サポートなら、
    1. ボタンにクリックのイベントを設定し、#cubeに手動でjQueryのanimate();メソッドを加えます。
    2. DOMから#cubeを取り除くコールバック関数を実行します。

このプロセスはCSSアニメーションが終わった時に実行するtransitionendというイベントを導入します。これをJavaScriptにセットします。

JavaScript

(function () {

   // set up your variables
   var elem = document.getElementById('cube'),
       button = document.getElementById('do-it'),
       transitionTimingFunction = 'linear',
       transitionDuration = 500,
       transitionend;

   // set up the syntax of the transitionend event with proper vendor prefixes
   if ($.browser.webkit) {
       transitionend = 'webkitTransitionEnd'; // safari & chrome
   } else if ($.browser.mozilla) {
       transitionend = 'transitionend'; // firefox
   } else if ($.browser.opera) {
       transitionend = 'oTransitionEnd'; // opera
   } else {
       transitionend = 'transitionend'; // best guess at the default?
   }

   //... rest of the code goes here.

})(); // end wrapping function

transitionendイベントにベンダー接頭辞を付与するために、ブラウザの検出をしています。
では、次にModernizrでブラウザのサポートを検出し、イベントを設定してみます。

JavaScript

// detect for css transition support with Modernizr
if(Modernizr.csstransitions) {

    // add our class on click
    $(button).on('click', function () {
       $(elem).addClass('fade-out');
    });
    
    // simulate a callback function with an event listener
    elem.addEventListener(transitionend, function () {
       theCallbackFunction(elem);
    }, false);
       
} else {

   // set up a normal click/animate listener for unsupported browsers
   $(button).on('click', function () {

       $(elem).animate({
           'opacity' : '0'
       }, transitionDuration, transitionTimingFunction, function () {
           theCallbackFunction(elem);
       });

   }); // end click event

} // end support check

最後に、アニメーション完了後に実行する2つのアクションを定義する必要があります。ここではtheCallbackFunction()を設定します。これはエレメントをDOMから取り除き、それが機能したことを知らせるメッセージをコンソールで表示します。

JavaScript

// define your callback function, what happens after the transition/animation
function theCallbackFunction (elem) {

   'use strict';

   // remove the element from the DOM
   $(elem).remove();

   // log out that the transition is done
   console.log('the transition is complete');

}

これで、モバイルSafariやChromeのようなハイエンドなブラウザでも、IE7のようなローエンドのブラウザでも動作します。実行する中身は異なりますが、ユーザーからの見た目は変わりません。
これはユーザエクスペリエンスを犠牲にしないで、最先端のテクニックを使うできる方法です。そして、CSSをJavaScriptから遠ざける方法でもあります。これがわたし達の本当のゴールです。

CSSとJavaScriptの使い分けの大切なポイント

責任がある制作者として、アクセシブルなコードで制作することはわたし達の義務であると言えるでしょう。確かに、きれいなコードを保持することは根気が必要で、簡単なことであるとは言いません。しかし、それを無視するよりは、ずっと素晴らしいことです。

もし、制限された帯域のデバイス向けコンテンツを作成するのであれば、JavaScriptよりCSSの方がずっと軽いです。JavaScriptで実装することをCSSでも可能であるなら、それは非常に大きな利益となります。画像の回転や振動やホバーのエフェクトなどは、CSSアニメーションを使った方がよいでしょう。
わたし達はブラウザ上で機能を効率よく発揮する一番強力な言語はCSSであるということに到達しました。もちろん、マクロなインタラクションにJavaScriptを使うことには問題がありません。

メンテナンスが簡単で、軽量で、一貫したエクスペリエンスを提供するのであれば、CSSとJavaScriptの使い分けに気をつけるべきです。

top of page

©2017 coliss