WebDesign Dackel

【初心者向け】jQueryでツールチップのプラグインを自作しよう – Part3

【初心者向け】jQueryでツールチップのプラグインを自作しよう – Part3

Hatena0
Google+0
Pocket0
Feedly0

はじめに

これまでPart1、2とやってきましたが、今回で「jQueryでツールチップのプラグインを自作しよう」シリーズも最後です!
今回は、前回のPart2でプラグイン化したコードを更に発展させて、プラグインとしての完成度を高められるように調整をしていきたいと思います。

シリーズ

  1. 【初心者向け】jQueryでツールチップのプラグインを自作しよう – Part1
  2. 【初心者向け】jQueryでツールチップのプラグインを自作しよう – Part2
  3. 【初心者向け】jQueryでツールチップのプラグインを自作しよう – Part3

最終的なファイルはGitHubの下記リポジトリでも確認できます。

tsuyoshiwada/sample-jquery-tooltip

今回の目標

最終的な動作サンプル

動作サンプル

Part1の最初にも同じものを載せましたが、上記の様な動作が最終目標です。

  • プラグイン内で使用するイベントには名前空間を使用しよう
  • より柔軟に使えるように設定項目を増やす
    • ツールチップを上下左右に出せるように
    • 位置の微調整を出来るように
    • マウスが離れてから、ツールチップが非表示になるまでの遅延処理が出来るように
    • ツールチップ用のCSSクラスを変更できるように
  • 全体のコードを整理して完成させる

冒頭でも書いた通り、プラグインとしての完成度を高めることが目標になっています。これまでの内容をなんとなく理解出来ていればあまり難しくは無い気がします…!

完成に向けて調整していく!

Part3ということもあって、だいぶjQueryを使ったプラグラミングに慣れてきたと思います。(そうでもないですかね?)
なので、これまでと違って最初に全体のコードを載せてからあとでポイントを抑えていく方針で行きたいと思います。
前回のコードとさほど変わりないのでご心配なく。

;(function(factory){
  // AMD
  if( typeof define === "function" && define.amd ){
    define(["jquery"], factory);
  // CommonJS
  }else if( typeof exports === "object" ){
    module.exports = factory(require("jquery"));
  // Default (browser)
  }else{
    factory(jQuery);
  }

}(function($){

  // 渡せるオプションのデフォルト
  var defaults = {
    baseClass: "tooltip",
    direction: "top",
    duration: 300,
    hideDelay: 300,
    offsetY: 0,
    offsetX: 0
  };

  // プラグインで使用するイベント名
  // ※ここでは詳しくは書きませんが、イベントに名前空間を指定することで
  //  他の処理との思わぬ競合を防ぎます
  var MouseEvent = {
    ROLL_OVER: "mouseenter.tooltip",
    ROLL_OUT: "mouseleave.tooltip"
  };

  // 指定した要素の上下左右の座標+サイズを取得する
  function getElementInfo($elem){
    var offset = $elem.offset();
    var width = $elem.outerWidth();
    var height = $elem.outerHeight();

    return {
      top    : offset.top,
      right  : offset.left + width,
      bottom : offset.top + height,
      left   : offset.left,
      centerY: offset.top + height / 2,
      centerX: offset.left + width / 2,
      width  : width,
      height : height
    };
  }


  // jQueryに "tooltip" というプラグインを登録する
  $.fn.tooltip = function(options){
    var $body = $("body");
    var _options = $.extend(true, {}, defaults, options);

    // 実行したセレクタ(要素)全てに処理を行う
    // 要素自体を "return" することで、メソッドチェーンを可能にする
    return this.each(function(){
      var $this = $(this);
      var title = $this.attr("title");
      var baseClass = [
        _options.baseClass,
        _options.baseClass + "--" + _options.direction
      ].join(" ");
      var bodyClass = _options.baseClass + "__body";

      // ツールチップ本体を生成
      // 生成されるHTMLは下記のような感じ
      // <span class="tooltip tooltip--{方向}">
      //   <span class="tooltip__body">{内容}</span>
      // </span>
      var $tooltip = $([
        "<span class='" + baseClass + "'>",
          "<span class='" + bodyClass + "'>",
            title,
          "</span>",
        "</span>"
      ].join(""));

      // ブラウザ標準で出るツールチップを防止する
      $this.attr("title", "");

      // 最初はツールチップを非表示にしておく
      $tooltip.hide();

      // ロールオーバー/ロールアウトそれぞれにイベントを設定
      $this
        .on(MouseEvent.ROLL_OVER, function(){

          // <body> へツールチップを追加
          $body.append($tooltip);

          // 位置を調整
          var elemInfo = getElementInfo($this);
          var width = $tooltip.outerWidth();
          var height = $tooltip.outerHeight();

          // オプションで指定した方向に従って座標を設定
          var cssParams = {
            top: 0,
            left: 0
          };

          switch( _options.direction ){
            case "top":
              cssParams.top = elemInfo.top - height + _options.offsetY;
              cssParams.left = elemInfo.centerX - width / 2 + _options.offsetX;
              break;

            case "right":
              cssParams.top = elemInfo.centerY - height / 2 + _options.offsetY;
              cssParams.left = elemInfo.right + _options.offsetX;
              break;

            case "bottom":
              cssParams.top = elemInfo.bottom + _options.offsetY;
              cssParams.left = elemInfo.centerX - width / 2 + _options.offsetX;
              break;

            case "left":
              cssParams.top = elemInfo.centerY - height / 2 + _options.offsetY;
              cssParams.left = elemInfo.left - width + _options.offsetX;
              break;
          }

          // 座標を反映して、アニメーション表示
          // `jQuery.fn.stop(true,false)` で、現在のアニメーションキューをクリア、
          // なおかつ現在の状態で止めることで自然な動きに調整します。
          $tooltip.stop(true, false).css(cssParams).fadeIn(_options.duration);
        })
        .on(MouseEvent.ROLL_OUT, function(){
          // ロールアウトで、フェードアウトして
          // アニメーション完了時に要素を削除
          $tooltip.stop(true, false).delay(_options.hideDelay).fadeOut(_options.duration, function(){
            $tooltip.remove();
          });
        });
    });
  };

}));

大きくは変わっていませんが、デフォルトの設定が増えたり、使っている関数が増えたりしています。
それぞれコメントアウトを記載しているので、大丈夫かと思いますが次からポイントの解説をしていきたいと思います。

プラグイン内で使用するイベントには名前空間を使用しよう

上記のコードで言うと、下記の部分に該当します。

var MouseEvent = {
  ROLL_OVER: "mouseenter.tooltip",
  ROLL_OUT: "mouseleave.tooltip"
};

これまでだったら、「マウスを乗せたら…」という時にmouseenterを直接書いていました。

$this.on("mouseenter", function(){
  // マウスが乗った時の処理
});

MouseEvent.ROLL_OVERに入っている値をみると、いつものmouseenterの最後に.tooltipという文字が追加されています。.tooltipの部分を名前空間と呼びます。

別に普通にmouseenterを書けばいいじゃんってなりそうですが、前回書いた「他のプラグインと競合しないように意識」というところに繋がってきます。
長くなってしまうので今回は説明を省略してしまいますが、プラグイン内ではとりあえず付けておくという感じでも良い気がします。

興味のある方は下記のサイトがとっても参考になりそうです!

jQuery.fn.onに名前空間をつけることができる – Qiita

ツールチップの出す位置を柔軟に変更できるように

これまではリンクテキストの上部分にしか出せていませんでしたが、先ほどのコードではdirectionというオプションに対して、下記のいずれかを指定できるようにしています。

  • "top"
  • "right"
  • "bottom"
  • "left"

例えば、"right"を指定した場合だと、.tooltip--rightというクラスが当てられるようになりました。

<span class="tooltip tooltip--right">
  <span class="tooltip__body">{内容}</span>
</span>

CSS

指定した方向に応じてクラスが割り当てられるようになったので、吹き出しの位置を調整するためにプラグイン用に作成した、./css/jquery.tooltip.cssを編集します。

@charset "utf-8";

/* ツールチップ本体 */
.tooltip {
  position:absolute;
  z-index:9999;
  display:block;
  color:#fff;
  font-size:14px;
  line-height:1.2;
}

/* ツールチップの内容 */
.tooltip__body {
  position:relative;
  padding:10px;
  background:#222;
  -webkit-border-radius:3px;
     -moz-border-radius:3px;
      -ms-border-radius:3px;
          border-radius:3px;
  -webkit-box-shadow:0 2px 4px rgba(0, 0, 0, .4);
     -moz-box-shadow:0 2px 4px rgba(0, 0, 0, .4);
      -ms-box-shadow:0 2px 4px rgba(0, 0, 0, .4);
          box-shadow:0 2px 4px rgba(0, 0, 0, .4);
}

/* 方向によって位置を微調整する */
.tooltip--top .tooltip__body {
  top:-15px;
}

.tooltip--right .tooltip__body {
  right:-5px;
}

.tooltip--bottom .tooltip__body {
  bottom:-15px;
}

.tooltip--left .tooltip__body {
  left:-5px;
}

/* ツールチップらしく矢印をつける */
.tooltip__body:after {
  content:"";
  position:absolute;
  display:block;
  width:0;
  height:0;
  border-width:5px;
  border-style:solid;
  border-color:#222;
}

/* 表示方向によって、矢印の方向と位置を調整 */
.tooltip--top .tooltip__body:after,
.tooltip--bottom .tooltip__body:after {
  left:50%;
  margin-left:-5px;
  border-right-color:transparent;
  border-left-color:transparent;
}

.tooltip--right .tooltip__body:after,
.tooltip--left .tooltip__body:after {
  top:50%;
  margin-top:-5px;
  border-top-color:transparent;
  border-bottom-color:transparent;
}

.tooltip--top .tooltip__body:after {
  bottom:-5px;
  border-bottom-width:0;
  border-bottom-color:transparent;
}

.tooltip--bottom .tooltip__body:after {
  top:-5px;
  border-top-width:0;
  border-top-color:transparent;
}

.tooltip--right .tooltip__body:after {
  left:-5px;
  border-left-width:0;
  border-left-color:transparent;
}

.tooltip--left .tooltip__body:after {
  right:-5px;
  border-right-width:0;
  border-right-color:transparent;
}

座標の計算について

吹き出しの位置を調整は出来たので、次は実際に表示する位置を調整します。
コードでいうと下記の部分です。

// 位置を調整
var elemInfo = getElementInfo($this);
var width = $tooltip.outerWidth();
var height = $tooltip.outerHeight();

// オプションで指定した方向に従って座標を設定
var cssParams = {
  top: 0,
  left: 0
};

switch( _options.direction ){
  case "top":
    cssParams.top = elemInfo.top - height + _options.offsetY;
    cssParams.left = elemInfo.centerX - width / 2 + _options.offsetX;
    break;

  case "right":
    cssParams.top = elemInfo.centerY - height / 2 + _options.offsetY;
    cssParams.left = elemInfo.right + _options.offsetX;
    break;

  case "bottom":
    cssParams.top = elemInfo.bottom + _options.offsetY;
    cssParams.left = elemInfo.centerX - width / 2 + _options.offsetX;
    break;

  case "left":
    cssParams.top = elemInfo.centerY - height / 2 + _options.offsetY;
    cssParams.left = elemInfo.left - width + _options.offsetX;
    break;
}

対象となる要素の位置とサイズを元にして、ツールチップの位置を決めています。
中央座標はよく使うので、ここでは位置(中央含む)とサイズを一気に取得するgetElementInfoという関数を用意してコードを簡略化しています。


以上でPart1~3に渡るjQueryの自作ツールチッププラグイン作成は以上になります。
お疲れ様でした!

ぜひこんなカスタマイズに挑戦してみて下さい

一応の完成はさせましたが、実はまだまだ改善の余地がありそうです。
これから先は自分なりに実装を進めて、より使い勝手の良いプラグインへカスタマイズしてみてください!

僕なりにこんな風にしたらどうか?というものを幾つか挙げたいと思います。

クリックしたら表示する・閉じる

マウスが乗ったら・離れたら、というタイミングでツールチップを操作していましたが、これをクリックにした場合にも対応できるようになっているとより多くの場面で活用できそうです。

data属性を使ったオプション渡し

作成したプラグインだと、テキストによってツールチップを出す方向を変更したい場合は別に指定しなければなりません。

$(".left-tooltip").tooltip({direction: "left"});
$(".right-tooltip").tooltip({direction: "right"});

これだとせっかくプラグインとして機能をまとめたのにコードが少し煩雑です。
data属性を使ってHTML側で設定できれば柔軟に対応できそうです。
例えば下記の様にdata-directionという属性で設定するイメージ。

<a href="#" class="my-tooltip" title="左に出したい!" data-direction="left">テキストリンク</a>
<a href="#" class="my-tooltip" title="右に出したい!" data-direction="right">テキストリンク</a>
$(".my-tooltip").tooltip();

コードが単純になりましたね!

アニメーションをかっこよく

今回扱ったツールチップは単純にフェードイン/アウトだけでしたが、フェード+上下左右の動きが加わるともう少しリッチな感じになりそうです。

イメージ的にはこんな感じ。

アニメーションを変更したサンプル

シリーズを振り返って

記事を書いている最中、「これ本当に初心者向けか?」と何度も思いましたがなんとか3記事書き終わりました。笑

今回扱った内容がjQueryのプラグインでしたが、これに限らず一度作ったものを汎用的に使いまわせるようにしていく+より使い勝手よく調整をしていく、ということへ積極的にチャレンジしてもらえたらなと思います!


もし分からない点などありましたら出来る限りのフォローをしたいと思いますので、コメントか@vandalTsuyoshiまで言ってくれたらと思います。

  1. 【初心者向け】jQueryでツールチップのプラグインを自作しよう – Part1
  2. 【初心者向け】jQueryでツールチップのプラグインを自作しよう – Part2
  3. 【初心者向け】jQueryでツールチップのプラグインを自作しよう – Part3