jquery jQuery で Lightbox のようなモーダルウィンドウを表示

2013年6月24日

jQuery を使って簡単に Lightbox のようなモーダルウィンドウを表示する方法のメモ。

基本的な構造

  • サムネイル画像にモーダルウィンドウで表示する拡大画像のソースへのリンクを設定する。
  • ページの適当な個所に半透明のモーダルウィンドウと拡大画像を表示する領域を設定し、CSS で非表示にしておく。
  • 半透明のモーダルウィンドウの領域:id=”bg_layer” の div 要素
  • 拡大画像を表示する領域:id=”over_layer” の div 要素
  • 拡大画像:class=”lightboxlike” の img 要素(src 属性は空だと文法的にエラーになるので「#」と指定。また alt 属性も指定しておく)
  • 「閉じる」ボタンの画像:class=”close_button” の p 要素

サムネイル画像の HTML

<p class="thumbnail">
  <a href="images/photo_Large.jpg">
    <img src="images/thumbnail_1.jpg" height="100" width="200" />
  </a>
</p>

モーダルウィンドウの HTML。

<div id="bg_layer"> <!--半透明の領域-->
<!--/#bg_layer--></div>
<div id="over_layer"> <!--拡大画像と「閉じる」ボタンを表示する領域-->
  <img class="lightboxlike" src="#" alt=""  /><!--拡大画像-->
  <p class="close_button"><img src="images/close-trans.png"/></p>
<!--/#over_layer--></div>

CSS

  • #bg_layer(半透明の領域)をブラウザいっぱいに表示するため、親要素の body 要素と html 要素を height: 100% にする。
  • #bg_layer(半透明の領域)に、height: 100%; width: 100%; を指定し、position: fixed; top: 0; left: 0; でブラウザの左上を基点とする。
  • 背景色(黒)を指定し、 opacity で半透明にする。opacity に対応していない IE には filter プロパティを指定。
  • #over_layer(拡大画像を表示する領域)も、position: fixed; を指定。top: 50%; left: 50%;を指定しておく。スクリプトで margin-top, margin-left を指定して画面中央に表示するようにする。
  • 両方とも最初は非表示にしておく。
  • IE6 は position: fixed に対応していないため、スターハックで「absolute」に変更し、スクリプトで対処。
  • 「閉じる」ボタンも最初は非表示にしておく。
  • それぞれ必要に応じて z-index を指定。

CSS

<style>
html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}

#bg_layer {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  height: 100%;
  width: 100%;
  background: #000;
  opacity: 0.60;
  filter: alpha(opacity=60);
  z-index: 10;
}

#over_layer {
  display: none;
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: 15;
}

/*---IE6 対策---*/ 
* html #bg_layer {
  position: absolute;
}
/*---IE6 対策---*/ 
* html #over_layer {
  position: absolute;
}

p.close_button {
  display: none;
  position: absolute;
  top: -10px;
  right: -10px;
  cursor: pointer;
  z-index: 20;
}
</style>

jQuery

  • サムネイル画像の a 要素に click イベントを設定。
  • 拡大画像が表示されていなければ、以下を実施する。
  • モーダルウィンドウ(半透明の領域)を表示する。
  • 拡大画像の位置を margin-top, margin-left (拡大画像の半分のネガティブマージン)で指定する。
  • 拡大画像(img.lightboxlike)の src 属性にサムネイル画像の a 要素の href 属性を設定。
  • 画像と「閉じる」ボタンを表示する。
  • 半透明の領域か「閉じる」ボタンがクリックされたら、すべて非表示にする。
  • IE6 対策では、擬似的に position: fixed を実現。 IE 独自の setExpression を使用。setExpression は JavaScript の命令なので、get() でjQuery の要素から JavaScript で扱える要素に変換。

jQuery

var full_size_height = 400;    //拡大画像の高さ
var full_size_width = 800;    //拡大画像の幅

$('p.thumbnail a').click(function() {    
    if($('#over_layer').css('display') == 'none') {      
      $('#bg_layer').show();
      $('#over_layer').css({
        marginTop: '-' + full_size_height/2 + 'px',
        marginLeft: '-' + full_size_width/2 + 'px'
      });
      $('img.lightboxlike').attr('src', $(this).attr('href'));
      $('#over_layer, p.close_button').show();
    }
    return false;
  });

  //非表示にする(閉じる)処理  
  $('#bg_layer, p.close_button').click(function() {
    $('#over_layer,#bg_layer ').hide();
  });

  //IE6 対策  
  if($.browser.msie && $.browser.version<7) {
    $(window).scroll(function() {
      $('#bg_layer').get(0).style.setExpression('top', "$(document).scrollTop()+'px'");
      $('#over_layer').get(0).style.setExpression('top',"($(document).scrollTop()+$(window).height()/2)+'px'");
    });
    
  }
&#91;/code&#93;

<h3>スクロールされた場合元の位置に戻す</h3>

場合によってはモーダルウィンドウを表示中にスクロールをして、モーダルウィンドウを開いた位置から移動してしまい、それが問題になる場合は、以下のようにして元の位置に戻せる。

<ul>
  <li>モーダルウィンドウを開いた際に、その時点でのスクロールの値を取得して、data() を使って「#overlayer」にその値を保持。</li>
  <li>閉じる際に、その値とその時点での値が異なれば、animate() で元の位置にスクロールする。</li>
  <li>animate() でスクロールさせることができる要素が html か body かを判定する。</li>
</ul>

$('p.thumbnail a').click(function() { //その時点でのスクロールの値を取得 $('#overlayer').data('scroll_top', $(window).scrollTop()); if($('#over_layer').css('display') == 'none') { $('#bg_layer').show(); $('#over_layer').css({ marginTop: '-' + full_size_height/2 + 'px', marginLeft: '-' + full_size_width/2 + 'px' }); $('img.lightboxlike').attr('src', $(this).attr('href')); $('#over_layer, p.close_button').show(); } return false; }); // スクロールさせることができる要素が html か body かを判定 var isHtmlScrollable = (function(){ var html = $('html'), top = html.scrollTop(); var elm = $('<div/>').height(10000).prependTo('body'); html.scrollTop(10000); var rs = !!html.scrollTop(); html.scrollTop(top); elm.remove(); return rs; })(); //非表示にする(閉じる)処理 $('#bg_layer, p.close_button').click(function() { //値が異なれば、animate() で元の位置に戻す if($(window).scrollTop() != $('#overlayer').data('scroll_top')){ $(isHtmlScrollable ? 'html' : 'body').animate({ scrollTop: $('#overlayer').data('scroll_top') }, 500); } $('#over_layer,#bg_layer ').hide(); });

表示する画像の大きさを調整する

以下は、ウィンドウのサイズによって表示する画像の大きさを変える場合。

  • ウィンドウの高さの値($(window).height())が「画像 + 30px」より小さい場合は、画像を縮小して表示する。
  • 「画像 + 30px」の 30px は「閉じるボタン」等のサイズを考慮。
  • 縮小倍率は以下の例では「 0.87」と設定。(「閉じるボタン」が見えるように)

var full_size_height = 600; //拡大画像の高さ
var full_size_width = 400; //拡大画像の幅

$('p.thumbnail a').click(function() {
if($('#over_layer').css('display') == 'none') {
$('#bg_layer').show();
var wh = $(window).height(); //現在のウィンドウの高さを取得
if(full_size_height - (-30) > wh && wh){ //+30 だと文字列になるので「- (-30)」とする
var fixedHeight = wh * 0.87;
var fixedWidth = full_size_width * (fixedHeight / full_size_height);
$('#over_layer').css({
marginTop: '-' + fixedHeight/2 + 'px',
marginLeft: '-' + fixedWidth/2 + 'px'
});
$('img.lightboxlike').attr({src: $(this).attr('href'), height:fixedHeight, width:fixedWidth});
}else{
$('#over_layer').css({
marginTop: '-' + full_size_height/2 + 'px',
marginLeft: '-' + full_size_width/2 + 'px'
});
//高さと幅が変更されている可能性があるので、元の値に戻す
$('img.lightboxlike').attr({src: $(this).attr('href'), height:full_size_height, width:full_size_width});
}
$('#over_layer, p.close_button').show();
}
return false;
});

//非表示にする(閉じる)処理
$('#bg_layer, p.close_button').click(function() {
$('#over_layer,#bg_layer ').hide();
});

//IE6 対策
if($.browser.msie && $.browser.version<7) { $(window).scroll(function() { $('#bg_layer').get(0).style.setExpression('top', "$(document).scrollTop()+'px'"); $('#over_layer').get(0).style.setExpression('top',"($(document).scrollTop()+$(window).height()/2)+'px'"); }); } [/code]

関数にする

また、関数にしたほうが使いやすい場合は以下のようにすることも可能。

  • show_modal_window() を作成。
  • パラメータは以下のとおり。
    this$ :$(this) を渡す。(必須)
    img_width :拡大画像の幅 を渡す。(必須)
    img_height :拡大画像の高さ を渡す。(必須)
    my_options :アニメーション表示の速さや要素の指定(オプション)
  • ウィンドウのサイズにより、画像を縮小して表示(但し、条件分けはもう少し検討する必要あり)

function show_modal_window(this$, img_width, img_height, my_options) {
//$.extendでパラメータをマージ
var settings = $.extend({
zoom_h: 0.87, //高さの縮小倍率
zoom_w: 0.9, //幅の縮小倍率
extra_h: 30, //高さを比較する際のマージン(ピクセル)
extra_w: 10, //幅を比較する際のマージン(ピクセル)
duration: 700, //アニメーション表示の速さ(ミリ秒)
over_layer: '#over_layer', //画像を表示する領域
background_layer:'#bg_layer', //背景を表示する領域
img_element: 'img.lightboxlike', //表示する画像要素
close_element: 'p.close_button' //閉じるボタンの要素
}, my_options || {});

if($(settings.over_layer).css('display') == 'none') {
var image_width = img_width;
var image_height = img_height;
$(settings.background_layer).show();
var wh = $(window).height();
var ww = $(window).width();
//画像の高さがウィンドウの高さより大きい場合
if(image_height - (-settings.extra_h) > wh && image_width - (-settings.extra_w) <= ww){ var fixedHeight = wh * settings.zoom_h; var fixedWidth = image_width * (fixedHeight / image_height); $(settings.over_layer).css({ marginTop: '-' + fixedHeight/2 + 'px', marginLeft: '-' + fixedWidth/2 + 'px' }); $(settings.img_element).attr({src: this$.attr('href'), height:fixedHeight, width:fixedWidth}); //画像の幅がウィンドウの幅より大きい場合 }else if(image_width - (-settings.extra_w) > ww && image_height - (-settings.extra_h) <= wh){ fixedWidth = ww * settings.zoom_w; fixedHeight = image_height * (fixedWidth / image_width); $(settings.over_layer).css({ marginTop: '-' + fixedHeight/2 + 'px', marginLeft: '-' + fixedWidth/2 + 'px' }); $(settings.img_element).attr({src: this$.attr('href'), height:fixedHeight, width:fixedWidth}); //画像の高さと幅がウィンドウの高さと幅より大きい場合 }else if(image_width - (-settings.extra_w) > ww && image_height - (-settings.extra_h) > wh){
if((image_width - (-settings.extra_w)) - ww > (image_height - (-settings.extra_h)) -wh) {
fixedWidth = ww * settings.zoom_w;
fixedHeight = image_height * (fixedWidth / image_width);
$(settings.over_layer).css({
marginTop: '-' + fixedHeight/2 + 'px',
marginLeft: '-' + fixedWidth/2 + 'px'
});
$(settings.img_element).attr({src: this$.attr('href'), height:fixedHeight, width:fixedWidth});
}else{
fixedHeight = wh * settings.zoom_h;
fixedWidth = image_width * (fixedHeight / image_height);
$(settings.over_layer).css({
marginTop: '-' + fixedHeight/2 + 'px',
marginLeft: '-' + fixedWidth/2 + 'px'
});
$(settings.img_element).attr({src: this$.attr('href'), height:fixedHeight, width:fixedWidth});
}
}else{
$(settings.over_layer).css({
marginTop: '-' + image_height/2 + 'px',
marginLeft: '-' + image_width/2 + 'px'
});
$(settings.img_element).attr({src: this$.attr('href'), height:image_height, width:image_width});
}
$(settings.over_layer + ',' + settings.close_element).fadeIn(settings.duration);
}
}

attachment_links$.click(function() {
show_modal_window( $(this), 400, 600, {duration: 500 }) ;
return false;
});

if($.browser.msie && $.browser.version<7) { $(window).scroll(function() { $('#bg_layer').get(0).style.setExpression('top', "$(document).scrollTop()+'px'"); $('#over_layer').get(0).style.setExpression('top',"($(document).scrollTop()+$(window).height()/2)+'px'"); }); } $('#bg_layer, p.close_button, p.close_overLayer').click(function() { $('#over_layer,#bg_layer ').fadeOut(500); }); [/code]