芝生やDIY等のライフハックやWeb制作情報を発信するメディア

メニューを開く

【jsonファイル化!】レスポンシブWebデザインに対応したカルーセルjsをjsonファイル読み込みに対応

Web2017年7月5日

レスポンシブWebデザインに対応したカルーセルjsですが、さらに改良し、情報をjsonファイルで外部ファイル化する機能を加えました。これでメンテナンス性が向上しさらに使いやすくなったと思いますので、ご紹介させていただきたいと思います。

jsonで外部ファイル化

先日ご紹介させていただいたスクリプトではhtmlに直接内容を記述していましたが、以下の情報についてjsonファイルで管理できるように改良しました。

  • 画像のファイルパス
  • リンク先
  • target="_blank"の有無

サンプルコード

html

htmlは大きく変わっていません。唯一各アイテムのソースだけ削除されています。

<div id="carousel">
<div class="carousel-view">
<div class="carousel-contents">
</div>
<p class="prev"><a href="#"><img src="https://placehold.jp/80x80.png?text=前" alt="前"></a></p>
<p class="next"><a href="#"><img src="https://placehold.jp/80x80.png?text=次" alt="次"></a></p>
</div>
<ul class="list-indicator">
</ul>
</div><!--carousel-->

json

今回のサンプルでは以下のような書式のjsonファイルを扱えるようにしてあります。

[
{
"img": "http://placehold.jp/333333/ffffff/960x450.png?text=1",
"link":"https://yahoo.co.jp/"
},
{
"img": "http://placehold.jp/333333/ffffff/960x450.png?text=2",
"link": "https://yahoo.co.jp/",
"target": true
},
{
"img": "http://placehold.jp/333333/ffffff/960x450.png?text=3",
"link": "https://yahoo.co.jp/",
"target": true
}
]

css

cssについては以前ご紹介させていただいたソースそのままです。

*{margin:0;padding:0;}

img{
    max-width:100%;
    border:none;
}

#carousel{
    position:relative;
    max-width:960px;
    margin:0 auto;
    font-size:0;
    line-height:0;
}

#carousel .carousel-view {
    overflow:hidden;
    position:relative;
}

#carousel .carousel-view .carousel-contents{
    position:relative;
    top:0;
}

#carousel .carousel-view .carousel-contents:after{
    clear:both;
    content:"";
    display:table;
}

#carousel .carousel-view .carousel-contents .item {
    float:left;
    display:inline;
}

#carousel .carousel-view p.prev{
    position:absolute;
    top:50%;
    left:0;
    transform: translateY(-50%);
}

#carousel .carousel-view p.next{
    position:absolute;
    top:50%;
    right:0;
    transform: translateY(-50%);
}

#carousel ul.list-indicator{
    text-align:center;
}

#carousel ul.list-indicator li{
    display:inline-block;
    margin-right:20px;
    margin-top:20px;
}

#carousel ul.list-indicator li a{
    display:block;
    height:20px;
    width:50px;
    background:#ccc;
}

#carousel ul.list-indicator li.current a{
    background:#ff0000;
}

js

$(function(){
    'use scrict';
    
    //jsonファイルのパス
    var $jsonPath = '読み込みたいjsonファイルのパスを記述';
    
    //アニメーションスピード
    var $speed = 500;
    
    //自動再生までの時間
    var $interval = 3000;
        
    //カウント初期値
    var $currentNum = 1;
    
    $.ajax({
        type: 'GET',
        url: $jsonPath,
        dataType: 'json'
    })
    .then(
    
        /*読み込み成功時*/
        function (json) {
            var $jsonLength = json.length;
            var $target;
            for(var i=0; i < $jsonLength; i++){
                //別窓対応
                if(json[i].target === true){
                    $target = ' target="_blank"';
                }
                else{
                    $target = '';
                }
                $(".carousel-contents").append('<div class="item"><a href="'+json[i].link+'"'+$target+'><img src="'+json[i].img+'" alt=""></a></div>');
            }
        },
        
        /*読み込み失敗時*/
        function () {
            alert("読み込み失敗しました");
            
    }).done(function(){
        
        //セレクタを変数に格納
        var $view = $('.carousel-view'),
            $viewContents = $view.find('.carousel-contents'),
            $viewContentsItem = $viewContents.find('.item'),
            $viewContentsItemImg = $viewContentsItem.find('img'),
            $indicator = $('#carousel ul.list-indicator'),
            $btnPrev = $('p.prev a'),
            $btnNext = $('p.next a');
        
        //要素の数を変数に格納
        var $viewContentsItemLength = $viewContentsItem.length;
        
        //カルーセルのwidthを変数に格納
        var $viewContentsItemWidth = $viewContentsItem.width();
        
        //画像のwidthを変数に格納
        var $viewContentsItemImgWidth = $viewContentsItemImg.width();
        
        //処理実行
        $viewContents.each(function() {
            
            var autoTimer;
            
            //自動スライド関数
            var autoLoad = function(){
                autoTimer = setInterval(function(){
                    rollNext();
                }, $interval);
            };
                                    
            //1個以上の場合のみ
            if($viewContentsItemLength > 1){
                
                //アイテムのdivにwidthを設定
                $viewContentsItem.css('width', $viewContentsItemImgWidth);
                
                //インジケーター生成
                for(var i = 1;i <= $viewContentsItemLength;i++){
                    $indicator.append('<li><a href="#">'+ i +'枚目</a></li>');
                }
                $indicator.find('li').first().addClass("current");
                
                //ループ用のクローンを前後に複製
                $viewContentsItem.first().clone().addClass("clone-f").appendTo($viewContents);
                $viewContentsItem.last().clone().addClass("clone-l").prependTo($viewContents);
                
                //初期カレントclassを付与
                $viewContents.find('.clone-l').next().addClass("current");
                
                //アイテムの数で全体のwidthを設定(クローン分を含む)
                $viewContents.css('width', $viewContentsItemWidth * ($viewContentsItemLength + 2));
                
                //leftをクローン分ずらして1個目の位置に
                $viewContents.css('left', - $viewContentsItemWidth);
                
                //自動再生
                autoLoad();
            }
            
            //1個の場合(インジケーター削除、クローン削除、cssリセット)
            if($viewContentsItemLength === 1){
                $btnPrev.parent().remove();
                $btnNext.parent().remove();
                $indicator.remove();
            }
            
            //右回転
            var rollNext = function(){
                clearInterval(autoTimer);
                if(!$viewContents.is(":animated")){
                    $currentNum++;
                    $viewContents.find('.current').removeClass('current').next().addClass('current');
                    $indicator.find('.current').removeClass('current');
                    $indicator.find('li').eq($currentNum - 1).addClass('current');
                    if($currentNum > $viewContentsItemLength){
                        $indicator.find('li').eq(0).addClass('current');    
                    }
                    $viewContents.animate({ 'left': - $viewContentsItemWidth * $currentNum,
                    }, $speed, function() {
                        if($currentNum > $viewContentsItemLength){
                            $viewContents.find('.current').removeClass('current');
                            $viewContents.find('.clone-l').next().addClass("current");
                            $viewContents.css('left', - $viewContentsItemWidth);
                            $currentNum = 1;
                        }
                    });
                }
                autoLoad();
            };
                
            //左回転
            var rollPrev = function(){
                clearInterval(autoTimer);
                if(!$viewContents.is(":animated")){
                    $currentNum--;
                    $viewContents.find('.current').removeClass('current').prev().addClass('current');
                    $indicator.find('.current').removeClass('current');
                    $indicator.find('li').eq($currentNum - 1).addClass('current');
                    if($currentNum > $viewContentsItemLength){
                        $indicator.find('li').eq(0).addClass('current');    
                    }
                    $viewContents.animate({ 'left': - $viewContentsItemWidth * $currentNum,
                    }, $speed, function() {
                        if($currentNum < 1){
                            $viewContents.find('.current').removeClass('current');
                            $viewContents.find('.clone-f').prev().addClass("current");
                            $viewContents.css('left', - $viewContentsItemWidth * $viewContentsItemLength);
                            $currentNum = $viewContentsItemLength;
                        }
                    });
                }
            };
            
            //右クリック
            $btnNext.click(function(){
                rollNext();
                clearInterval(autoTimer);
                return false;
            });
            
            //左クリック
            $btnPrev.click(function(){
                rollPrev();
                return false;
            });
            
            //インジケータークリック
            $indicator.find('li a').click(function(){
                
                //自動再生タイマーをクリア
                clearInterval(autoTimer);
                
                if(!$viewContents.is(":animated")){
                    
                    //インジケーターのインデックス番号を取得
                    var $indicatorIndex = $(this).parent().index() + 1;
                    
                    //カウントを更新
                    $currentNum = $indicatorIndex;
                    
                    //インジケーターのカレントのclassを付け替え
                    $indicator.find('.current').removeClass('current');
                    $(this).parent().addClass('current');
                    
                    //アイテムのカレントのclassを付け替え
                    $viewContents.find('.current').removeClass('current');
                    $viewContentsItem.eq($currentNum - 1).addClass('current');
                    
                    //アニメーション
                    $viewContents.animate({ 'left': - $viewContentsItemWidth * $currentNum,
                    }, $speed, function() {
                        
                        /*完了時の処理は不要*/
                        
                    });
                }
                
                return false;
                e.preventDefault();
            });
            
            //リサイズ対応
            var $resizeTimer = false;
            $(window).resize(function() {
                if ($resizeTimer !== false) {
                    clearTimeout($resizeTimer);
                }
                $resizeTimer = setTimeout(function() {
                    
                    //widthをリセット
                    $viewContents.find('div.item').css('width','auto');
                    $viewContents.css('width','auto').css('left','auto');
                    
                    //カルーセルのwidthを変数に格納し直す
                    $viewContentsItemWidth = $viewContentsItem.width();
                    
                    //画像のwidthを変数に格納し直す
                    $viewContentsItemImgWidth = $viewContentsItemImg.width();
                    
                    //アイテムのdivにwidthを設定
                    $viewContents.find('div.item').css('width', $viewContentsItemImgWidth);
                    
                    //アイテムの数で全体のwidthを設定(クローン分を含む)
                    $viewContents.css('width', $viewContentsItemWidth * ($viewContentsItemLength + 2));
                    
                }, 200);
            });
            
            //マウスオーバー時は自動再生タイマー停止
            $view.hover(function(){
                clearInterval(autoTimer);
            }, function(){
                autoLoad();
            });
            $indicator.find('li a').hover(function(){
                clearInterval(autoTimer);
            }, function(){
                    autoLoad();
            });
            
            //フリック対応
            $viewContents.on({
                
                //タッチ開始
                'touchstart': function(e) {
                    this.startX = e.originalEvent.changedTouches[0].pageX;
                    this.startY = e.originalEvent.changedTouches[0].pageY;
                    this.touchX = e.originalEvent.changedTouches[0].pageX;
                    this.slideX = $(this).position().left;
                    
                    //自動再生タイマーをクリア
                    clearInterval(autoTimer);
                },
                
                //タッチ移動
                'touchmove': function(e) {
                    var $moveX = this.startX - e.originalEvent.changedTouches[0].pageX,
                        $moveY = this.startY - e.originalEvent.changedTouches[0].pageY;
                    
                    //縦スクロール対応
                    var $moveRate = $moveX / $moveY;
                    if($moveRate > Math.tan(15 * Math.PI/180)) {
                        e.originalEvent.preventDefault();
                    }
    
                    this.slideX = this.slideX - (this.touchX - e.originalEvent.changedTouches[0].pageX);
                    $(this).css({left:this.slideX});
                    this.touchX = e.originalEvent.changedTouches[0].pageX;
                },
                
                //タッチ終了
                'touchend': function(e) {
                    var $diff = this.startX - this.touchX;
                    if ($diff < -50) {
                        rollPrev();
                    }else if(50 < $diff){
                        rollNext();
                    }else{
                        $(this).animate({left:this.slideX + $diff});
                    }
                }    
            });
            
        });
    });
});
  • jQueryを読み込んでいる場合のスクリプトとなります

解説

ほとんど以前のスクリプトの流用です。$.ajax()を使ってjsonファイルを読み込み、DOM生成後にdone()でつなげてそれ以降はほぼ同じソースです。一応jsonファイルの読み込みエラーを検知できるよう、thenを使って読み込み成功時と読み込み失敗時の処理を分けています。こちらについて詳しくは以下の記事をご参照ください。

また、別窓同窓を設定できるよう"target"~をjsonに記述することでコントロールできるようにしています。

デモ

実際に動作を確認できるデモページも公開しておりますのであわせてご確認ください。

まとめ

実案件で更新頻度の多いカルーセルですと、毎回直接htmlを触るのはリスクがありますし、他の更新とバッティングする可能性があります。このようにjsonファイルで更新したい箇所だけ外部ファイル化すると更新頻度や更新箇所の多いページでも柔軟にメンテナンスすることが可能です。

カルーセルjsでjsonファイルなどの外部ファイルを扱いたい場合は、どうぞ本記事をご参考にしていただければ幸いです。

追記(2017年7月14日)

本記事のスクリプトをさらに改良し、クロスドメイン対応しました。どうぞあわせてお読みください。

この記事を読んでいる方にオススメの記事

「Web」の他の記事を読む

この記事にコメントする

必須
必須
本文必須

  • 承認制のため、即時には反映されません。

ページの先頭に戻る