ちょっと話題の記事

HTML5 × CSS3 × jQueryを真面目に勉強してみる – #1 jQuery再入門

2012.02.02

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

そんな訳で、ここらでHTML5 × CSS3 × jQueryに真面目に取り組んでいきたいと思います。
「今更なんだか恥ずかしい…」なんて怖じ気ずかずに勇気を持っていきます。

今からでも間に合います。

「人生に挑戦するのに年齢なんて関係ない。もともとこの世には時間などない。それは人間が勝手に作ったものだ。私は時計師だからそのことがよく分かる。」
-フランク・ミュラー

大丈夫だ、問題ない。

とりあえずの予備知識

それぞれの基礎的な予備知識については、当ブログでは割愛します。こちらを参照するのが手っ取り早いです。

とりあえず今回はjQueryから手を付けるとします。
上で紹介したサイトにある情報の補足として、以下の内容を知っておくと後の苦労が減ります。

jQueryについて最低限押さえておきたい基礎知識

  1. ノーマルなJavaScriptでは、document.getElementById('セレクタ名')等を使って、DOMオブジェクトを選択する
  2. 指定されたオブジェクトは、jQueryオブジェクトとして返される
  3. DOMオブジェクトそのものが返されるわけではない
  4. jQueryオブジェクトはDOMオブジェクトを配列のような形式で持っている選択セットである
  5. そのため、jQueryオブジェクトはjQueryメソッドしか実行できない
  6. なのでDOMのプロパティやメソッドを参照したいなら、jQueryオブジェクトの選択セット(配列)からDOMオブジェクトを呼び出す必要がある

jQueryを使ううえで必ず目にするであろう$('セレクタ名')とかjQuery('セレクタ名')というのは、
DOMオブジェクトを取得するというjQueryの関数です。なので、

$('p').innerHTML = "Hello Mr.Yamada!";

なんて書いてもエラーになるだけです。

$('p').text('Hello Mr.Yamada!');

みたいにjQueryの関数を使ってやる必要がある訳です。

なぜこんな目に遭ってしまうかというと…

jQueryオブジェクトは複数の要素を持つことを前提に設計されています。
たとえ$('p')で取得できた要素が一つであっても、配列形式でしか持ってくれません。
なので$('p').innerHTMLという記述では、配列に格納されたDOMオブジェクトまでアクセスできていないのです。

そのため、DOMのプロパティやメソッドを直接参照するには、
jQueryオブジェクトからこれらの単一要素のDOMオブジェクトを取得する必要があります。
取得方法はいろいろとありますが、とりあえず以下の2種類を押さえておけば充分です。

  • $('p').get(index)
  • $('p')[index] ※こっちがオススメ

よってDOMのプロパティやメソッドを参照したい場合は、

$('p')[0].innerHTML = "Hello Mr.Yamada!";

といったように書いてあげればOKな訳です。

知っていればどうってことない知識ですが、知らないと割と簡単に泥沼にはまったりします。 (ソースは山田)

とりあえず何か作ってみた

Hello Worldとか文字列の選択とかでは味気ないので、もう少しマシなサンプルを作ってみます。
一応HTML5とCSS3も使ってそれなりにナウい感じのモノを目指すとします。

そんな訳で、プルダウンメニューを作ってみます。

要件

  • メインとなるメニュー項目は水平方向に並んだものとする
  • メインメニューラベルにカーソルをあわせると、サブメニューが垂直下にプルダウン表示される
  • カーソルを外すと、サブメニューが非表示となる
  • HTML5でマークアップし、ビジュアルデザインはCSS3、プルダウン機能はjQueryでそれぞれ実装する

かなり大雑把ですが、まぁこんな感じでしょうか。
なるべくシンプルな実装を目指したいので、必要最低限の機能にとどめました。

まずはHTMLのマークアップから。

<nav id="topNav">
      <ul class="clearfix">
        <li><a href="#" title="Nav Link 1">Nav Link 1</a></li>
        <li>
          <a href="#" title="Nav Link 2">Nav Link 2</a>
          <ul class="clearfix">
            <li><a href="#" title="Sub Nav Link 1">Sub Nav Link 1</a></li>
            <li><a href="#" title="Sub Nav Link 2">Sub Nav Link 2</a></li>
            <li><a href="#" title="Sub Nav Link 3">Sub Nav Link 3</a></li>
            <li><a href="#" title="Sub Nav Link 4">Sub Nav Link 4</a></li>
            <li><a href="#" title="Sub Nav Link 5">Sub Nav Link 5</a></li>
          </ul>
        </li>
        <li><a href="#" title="Nav Link 3">Nav Link 3</a></li>
        <li><a href="#" title="Nav Link 4">Nav Link 4</a></li>
        <li><a href="#" title="Nav Link 5">Nav Link 5</a></li>
      </ul>
    </nav>

     <div>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ......</div>

サンプルページをひらく:dropdown_sample1(Chrome / Safari / Firefoxブラウザにて閲覧ください。)

非常にシンプルに書けました。余計なクラス指定等はありません。素晴らしい。

続いてCSSです。

/*  =00 Basic setting
-----------------------------------------------------*/
/* =00-1 Clearfix --------------------------*/
.clearfix:after{
  content:".";
  display:block;
  height:0;
  clear:both;
  visibility:hidden;
}
.clearfix{display:inline-block;}
* html .clearfix{height:1%;}
.clearfix{display:block;}


/*  =01 base nav style
-----------------------------------------------------*/
nav {
  display: block;
  margin: 0 auto 20px;
  border: 1px solid #222;
  position: relative;
  background: #6a6a6a;
  font-size: 16px;
}

nav ul li {
  position: relative;
  float: left;
}


  nav ul li a {
    display: block;
    padding: 10px 20px;
    border-left: 1px solid #999;
    border-right: 1px solid #222;
    color: #eee;
    text-decoration: none;
  }

    nav ul li a:focus {
      outline: none;
      text-decoration: underline;
    }

  nav ul li:first-child a {
    border-left: none;
  }

  nav ul li:last-child a {
    border-right: none;
  }

  nav ul li a span {
    display: block;
    float: right;
    margin-left: 5px;
  }
  
  nav ul li:hover ul {
    display:block;
  }
  
nav#topNav ul ul {
  display:none;
  width: 100%;
  position: absolute;
  left: 0;
  background: #6a6a6a;
}

  nav ul ul li {
    float: none;
  }

  nav ul ul a {
    padding: 5px 10px;
    border-left: none;
    border-right: none;
    font-size: 14px;
  }

    nav ul ul a:hover {
      background: #555;
    }

まだこの段階では、CSS3の機能は使っていません。ひとまずは動きだけ実装してからとしましょう。

続いてjQueryです。

$(document).ready(function() {

  $('#topNav').find('li').each(function(index) {
    //子リストを持つliを全て選択し、「^」印を追記する
    if ($(this).find('ul').length > 0) {
      $('<span></span>').text('^').appendTo($(this).children(':first'));

      //ホバー時にサブメニューを表示する
      $(this).mouseenter(function() {
        $(this).find('ul').stop(true, true).slideDown();  //アニメが実行途中だった場合を考慮して、停止させてからアニメ処理を実行する
      });

      //マウスが離れた時にサブメニューを隠す
      $(this).mouseleave(function() {
        $(this).find('ul').stop(true, true).slideUp();  //アニメが実行途中だった場合を考慮して、停止させてからアニメ処理を実行する
      });
    }
  });

});

サンプルページをひらく:dropdown_sample2(Chrome / Safari / Firefoxブラウザにて閲覧ください。)

処理の流れとしては、こんな感じ。

  • $('#topNav').find('li')で、<nav>内にある<li>要素をすべて取得する。
  • 取得した<li>セットをeach()関数でループにかける。ループ内での処理は以下の通り
    • <ul>要素を子に持っているかどうかチェックする
    • <span>^</span>要素を生成し、appendTo($(this).children(':first'))で<a>要素内に挿入する
    • ホバー時にサブメニューをプルダウン表示する処理を定義する
    • マウスアウト時にサブメニューを非表示にする処理を定義する

特に難しいことはしてないです。each()関数は、for inループみたいなモノと考えていいでしょう。
<li>セット内の<li>要素に$(this)で順番にアクセスすることが出来ます。
マウスホバー時とマウスアウト時とで、それぞれサブメニューのプルダウンおよびプルアップの処理を定義しています。

ポイントとして押さえておきたいのが、slideDown()やslideUp()メソッドの直前にstop()メソッドを呼び出していることです。
jQueryのアニメーションの仕様として以下のものが挙げられます。

  • デフォルトではアニメーション処理は、キューに配置される
  • キューには複数のアニメーション処理を配置することが出来る
  • 配置されたアニメーションは順番にすべて実行されます。

つまりマウスホバー、マウスアウトを素早く繰り返すと、サブメニューが狂ったように上下運動してしまうため、
これらを制御しなくてはなりません。
そこで、stop()関数で一度すべてのアニメーションを強制停止してから、改めて実行するようにしているという訳です。
ちなみにstop()関数の第一引数は、キューに配置されているアニメーション処理を全削除するかどうかのフラグで、
第二引数は実行中のアニメーションの終了地点に強制ジャンプするかどうかのフラグとなります。

これでとりあえずプルダウンメニューの機能実装は出来ました。最後にCSS3を使って、ナウいデザインにしてみます。

nav {
  background-image:linear-gradient(0% 22px 90deg, #272c32, #020202);
  background-image:-webkit-gradient(linear, 0% 0%, 0% 70%, from(#020202), to(#272c32));
  background-image:-moz-linear-gradient(0% 22px 90deg, #272c32, #020202);
  background-image:-o-linear-gradient(0% 22px 90deg, #272c32, #020202);
  border-bottom: 4px solid #46aace;
}

  nav ul li a {
    border-left: 1px solid #34373c;
    border-right: 1px solid #222;
    transition: text-shadow 0.5s linear 0;
    -o-transition: text-shadow 0.5s linear 0;
    -moz-transition: text-shadow 0.5s linear 0;
    -webkit-transition: text-shadow 0.5s linear 0;

  }
    nav ul li a:hover {
      text-shadow: 0 0 4px #46aace;
    }

nav#topNav  ul ul {
  background-image:linear-gradient(0% 22px 90deg, #343a42, #272c32);
  background-image:-o-linear-gradient(0% 22px 90deg, #343a42, #272c32);
  background-image:-moz-linear-gradient(0% 22px 90deg, #343a42, #272c32);
  background-image:-webkit-gradient(linear, 0% 0%, 0% 70%, from(#272c32), to(#343a42));
  box-shadow: 1px 1px 5px rgba(0,0,0,0.5);
  -webkit-box-shadow: 1px 1px 5px rgba(0,0,0,0.5);
  -moz-box-shadow: 1px 1px 5px rgba(0,0,0,0.5);

}

nav ul ul a {
  padding: 10px;
  border: none;
}

  nav ul ul a:hover {
    background: none;
    border-left: 4px solid #46aace;
    padding-left: 6px;
  }

nav ul a span {
  -moz-transform:rotate(-180deg);
  -webkit-transform:rotate(-180deg);
}

サンプルページをひらく:dropdown_sample3(Chrome / Safari / Firefoxブラウザにて閲覧ください。)

グラデーションと、テキストシャドウ、ボックスシャドウを使ってみました。

まとめ

入門篇ということで、簡単なプルダウンメニューを作ってみました。
この手のプラグインはググればどっさりと出てきますが、自力で必要最小限の処理を実装することで、
パフォーマンスチューニングにも繋がったり、自分がプラグインを作る側に回れるための第一歩を踏み出せるのではないかと思います。