レスポンシブデザインでのブレイクポイント変更検知のベストプラクティス

f:id:ultrasevenstar:20161120162420p:plain レスポンシブサイトでブレイクポイントによってjavascriptの挙動が変わることって多いと思います。

例えばPCサイトではメガメニューがサイドバーの横から出てくるけど、 タブレットではヘッダの上から出てくるとか

問題のある実装1

$('.button').on('click', function() {
    if($(window).width() >= 1200) {
        // PCの処理
    }else if(this.$window.width() < 768) {
        // SPの処理
    }
})

$('.button2').on('click', function() {
    if($(window).width() >= 1200) {
        // PCの処理
    }else if(this.$window.width() < 768) {
        // SPの処理
    }
})

上記の実装のようにイベント発火時にWindowの横幅を調べる実装だと ブレイクポイントが変更や追加された場合 色々な箇所で変更の必要があり、 DRYの原則に反し拡張性、メンテナンス性が悪いです

問題のある実装2

$('.button').on('click', function() {
    // $('.hunbergerMenu')はSP時のみ表示されるHTML要素
    if(! $('.hunbergerMenu').is(':visible') {
        // PCの処理
    }else {
        // SPの処理
    }
})

$('.button2').on('click', function() {
    if(! $('.hunbergerMenu').is(':visible') {
        // PCの処理
    }else{
        // SPの処理
    }
})

上記実装のように、ブレイクポイント毎に表示される固有のDOMの表示非表示にて 現在のブレイクポイントを判定する処理の場合 判定用のDOMが仕様変更等により無くなる可能性があり、 さらにタブレットやミニPC等の判定が必要にあるとelse ifが増えていくことになります。 また問題のある実装1と同様、色々な箇所で同様の記述が必要となり DRYの原則に反し拡張性、メンテナンス性が悪いです

別オブジェクトに切り出して実装すれば良い

ブレイクポイント変更検知専用のオブジェクトを作成し、ブレイクポイント変更時には カスタムトリガーをWindowに対して発火させ、変更検知したいオブジェクトでそのカスタムトリガーを受ければ良い

ChangeBreakPoint = {
    init: function() {
        this.setParameters();
        this.bindEvents();
    },
    setParameters: function() {
        this.$window = $(window);
        this.pageDivision = this.judgePageDivision();// 初回起動時に現在のブレイクポイントを取得する
    },
    bindEvents: function() {
        var self = this;
        var thisPagedivision = '';

         // Windowのリサイズ時に現在のWindow幅を調べ、
         // ブレイクポイントが変更されれば、Windowオブジェクトに対して
         // 「changeBreakPoint」カスタムトリガーを発火させ、
         // 引数に変更後のブレイクポイントを渡す
        this.$window.on('resize', function() {

            thisPagedivision = self.judgePageDivision();

            if(self.pageDivision != thisPagedivision) {
                self.pageDivision = thisPagedivision
                self.$window.trigger('changeBreakPoint', [thisPagedivision]);
            }
        });
    },
    getPageDivision: function() {
        return this.pageDivision;
    },
    judgePageDivision: function() {
        if(this.$window.width() >= 1200) {
            thisPageDivision = 'pc';
        }
        if(this.$window.width() < 1200 && this.$window.width() >= 980) {
            thisPageDivision = 'minipc';
        }
        if(this.$window.width() < 980 && this.$window.width() >= 768) {
            thisPageDivision = 'tb';
        }
        if(this.$window.width() < 768) {
            thisPageDivision = 'sp';
        }
        return thisPageDivision;
    }
}
$(function() {
    ChangeBreakPoint.init();
});

HOGE = {
    bindEvents: function() {
         var self = this;
         // changeBreakPointの第2引数が変更されたbreakpointなのでそれを変数等に格納
         $(window).on('changeBreakPoint', function(event, breakpoint) {
            self. breakpoint = breakpoint;
        });
    }
}

上記実装の場合、使いたいオブジェクトに対しカスタムトリガーを仕込むだけで、ブレイクポイントの変更を検知でき、ブレイクポイントの数が増えた場合もChangeBreakPointオブジェクトの修正のみで対応可能なためDRYの原則にも反しません。