JS物件導向應用,常見元件的封裝(輪播,tab,曝光載入)

weixin_33806914發表於2017-09-03

tab切換

用物件導向的寫法如下,建立的物件例項個個獨立,不需要考慮相互影響,只需要考慮自己怎麼實現即可,下面程式碼還可以進行優化

<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>元件tab</title>
    <style>
        ul,
        li {
            margin: 0;
            padding: 0;
        }

        li {
            list-style: none;
        }

        .clearfix:after {
            content: '';
            display: block;
            clear: both;
        }

        .tab {
            width: 600px;
            margin: 20px auto;
            border: 1px solid #ccc;
            padding: 20px 10px;
            border-radius: 4px;
        }

        .tab-header {
            border-bottom: 1px solid #ccc;
        }

        .tab-header>li {
            float: left;
            color: brown;
            border-top: 1px solid #fff;
            border-left: 1px solid #fff;
            border-right: 1px solid #fff;
            padding: 10px 20px;
            cursor: pointer;
        }

        .tab-header .active {
            border: 1px solid #ccc;
            border-bottom-color: #fff;
            border-radius: 4px 4px 0 0;
            color: #333;
            margin-bottom: -1px;
        }

        .tab-container {
            padding: 20px 10px;
        }

        .tab-container>li {
            display: none;
        }

        .tab-container>.active {
            display: block;
        }

        .box {
            height: 1000px;
        }
    </style>
</head>
<body>


<div class="tab">
    <ul class="tab-header clearfix">
        <li class="active">選項1</li>
        <li>選項2</li>
        <li>選項3</li>
    </ul>
    <ul class="tab-container">
        <li class="active">內容1
            <ul>
                <li></li>
            </ul>
        </li>
        <li>內容2</li>
        <li>內容3</li>
    </ul>
</div>

<div class="tab">
    <ul class="tab-header clearfix">
        <li class="active">選項1</li>
        <li>選項2</li>
        <li>選項3</li>
        <li>選項4</li>
    </ul>
    <ul class="tab-container">
        <li class="active">內容1
            <ul>
                <li></li>
            </ul>
        </li>
        <li>內容2</li>
        <li>內容3</li>
        <li>內容4</li>
    </ul>
</div>

<div class="tab">
    <ul class="tab-header clearfix">
        <li class="active">內容1</li>
        <li>內容2</li>
        <li>內容3</li>
        <li>內容4</li>
    </ul>
    <ul class="tab-container">
        <li class="active">內容1
            <ul>
                <li></li>
            </ul>
        </li>
        <li>內容2</li>
        <li>內容3</li>
        <li>內容4</li>
    </ul>
</div>

<script src="../jquery-3.2.1.min.js"></script>
<script>


    function Tab (ct) {
        //思考Tab建構函式裡的屬性和方法,屬性有選項和內容的li
        //這部分為初始化的東西
        this.option = ct.querySelectorAll('.tab-header>li');
        this.content = ct.querySelectorAll('.tab-container>li');
        var self = this;
        //上面部分為初始化的東西

        this.option.forEach(function (option) { //遍歷選項中的每一項
            option.addEventListener('click',function (e) { //給每個選項都繫結事件
                var target = e.target;
//            var index = this.option.indexOf(target);//不能這麼寫。這裡面的this代表當前點選的元素,所以要在外面儲存this
//            var index = self.option.indexOf(target); //但是寫成這樣,由於self.option不是一個陣列,沒有indexOf方法,所以還需要使用call/apply
                var index = [].indexOf.call(self.option,target); // 所以有2點需要注意,一是this的值,2是call
                self.option.forEach(function (option) {
                    option.classList.remove('active');//把每一項的active類都去掉
                })
                target.classList.add('active'); //只給當前點選的option加上active類
                self.content.forEach(function (content) { //下面的content同理
                    content.classList.remove('active');
                })
                self.content[index].classList.add('active');
            },false)
        })

    }
//    Tab.prototype.
    new Tab(document.querySelectorAll('.tab')[0]);
    new Tab(document.querySelectorAll('.tab')[1]);
    new Tab(document.querySelectorAll('.tab')[2]);
</script>
</body>
</html>

把初始化的東西封裝到一起,繫結事件的程式碼封裝到一起,都作為方法繫結到原型上,優化之後的程式碼如下:

<script>


    function Tab (ct) {
        //思考Tab建構函式裡的屬性和方法,屬性有選項和內容的li
        this.ct = ct;
        this,init();
        this.bind();
    }
    Tab.prototype.init = function () {
        //這部分為初始化的東西
        this.option = this.ct.querySelectorAll('.tab-header>li');
        this.content = this.ct.querySelectorAll('.tab-container>li');
        //上面部分為初始化的東西
    }
    Tab.prototype.bind = function () {
        var self = this;
        this.option.forEach(function (option) { //遍歷選項中的每一項
            option.addEventListener('click',function (e) { //給每個選項都繫結事件
                var target = e.target;
//            var index = this.option.indexOf(target);//不能這麼寫。這裡面的this代表當前點選的元素,所以要在外面儲存this
//            var index = self.option.indexOf(target); //但是寫成這樣,由於self.option不是一個陣列,沒有indexOf方法,所以還需要使用call/apply
                var index = [].indexOf.call(self.option,target); // 所以有2點需要注意,一是this的值,2是call
                self.option.forEach(function (option) {
                    option.classList.remove('active');//把每一項的active類都去掉
                })
                target.classList.add('active'); //只給當前點選的option加上active類
                self.content.forEach(function (content) { //下面的content同理
                    content.classList.remove('active');
                })
                self.content[index].classList.add('active');
            },false)
        })
    }
    new Tab(document.querySelectorAll('.tab')[0]);
    new Tab(document.querySelectorAll('.tab')[1]);
    new Tab(document.querySelectorAll('.tab')[2]);
</script>

輪播

原始碼地址
進行物件導向的元件化寫法:
整體思路就是把所有的屬性方法都用this關聯起來,這樣就可以只考慮自己了,但是要注意繫結事件還有匿名函式內部的this要注意儲存

<script>

    function Carousel ($ct) {
        this.$ct = $ct;
        this.init();
        this.bind();
    }
    Carousel.prototype.init = function () {
        var $imgct = this.$imgct= this.$ct.find('.img-ct');
        var $imgs = this.$imgs = this.$ct.find('.img-ct>li');
        //獲取寬度和個數
        var imgWidth = this.$imgWidth = this.$imgs.width();
        var imgCount = this.$imgCount = this.$imgs.length;
        var $pre = this.$pre = this.$ct.find('.pre'); //不這麼寫的話,下面bind中是找不到$pre的,元件化的核心思想就是都去使用this去進行操作
        var $next = this.$next =this.$ct.find('.next');
        var $bullets = this.$bullets =this.$ct.find('.bullet>li');
        var pageIndex = this.pageIndex = 0;  //記錄當前頁碼
        var animating = this.animating =  false; //針對連續重複點選,設定變數來監聽是否處於動畫過程中

        this.$imgct.append(this.$imgs.first().clone());
        this.$imgct.prepend(this.$imgs.last().clone());
        this.$imgct.width((this.$imgCount+2) * this.$imgWidth);
        this.$imgct.css({
            left: -this.$imgWidth,
        })
    }
    Carousel.prototype.bind = function () {
        var self = this;
        this.$pre.on('click',function () {
            self.playPre(1);//這裡面的this不對,是當前點選的元素this.$pre,所以還是需要儲存this
        })
        this.$next.on('click',function () {
            self.playNext(1);
        })
        this.$bullets.on('click',function () {
            var index = $(this).index();
            if (index < self.pageIndex) {
                self.playPre(self.pageIndex - index);
            }
            else {
                self.playNext(index - self.pageIndex);
            }
        })
    }
    Carousel.prototype.playPre = function (num) {
        var self = this;
        if (this.animating) {
            return; //如果還在動畫中,直接return掉
        }
        this.animating = true; //進入動畫,animating設為true表示正在動畫中
        this.$imgct.animate({
            left: '+=' + num * this.$imgWidth,
        },function () { // 儲存this,因為在匿名函式內部
            self.pageIndex -= num;
            if (self.pageIndex === -1) {
                self.pageIndex = self.$imgCount-1;
                self.$imgct.css({
                    left: -self.$imgCount*self.$imgWidth,
                })
            }
            self.setBullets();
            self.animating = false; //動畫結束後重新設定為false
        })
    }
    Carousel.prototype.playNext = function (num) {
        var self = this;
        if (this.animating) {
            return;
        }
        this.animating = true;
        this.$imgct.animate({
            left: '-=' + num * this.$imgWidth,
        },function () {
            self.pageIndex += num;
            if (self.pageIndex === self.$imgCount) {
                self.pageIndex = 0;
                self.$imgct.css({
                    left: -self.$imgWidth,
                })
            }
            self.setBullets();
            self.animating = false;
        })
    }
    Carousel.prototype.setBullets = function () {
        this.$bullets.removeClass('active')
                .eq(this.pageIndex).addClass('active');
    }
    var c1 = new Carousel($('.carousel').eq(0));
    var c2 = new Carousel($('.carousel').eq(1));

</script>

輪播二次封裝

對於上面封裝的元件,如果想達到使用Carousel2.init($('.carousel'));就使得所有的carousel都運轉起來的話,就要使用另外一種模組化的方式

首先來看一個區別:

    Carousel2 = {  //方法1 寫成一個物件
        init: function () {//寫成這樣的話 Carousel就是一個物件,只能新增一些屬性方法
            console.log(123);
        }
    }
    Carousel2 = (function () { //方法2 寫成一個立即執行函式
        var a = 1; //寫成這樣的好處在於可以賦值一些區域性變數,並且這個值外部永遠訪問不到,並且也能在return中新增新的屬性方法
        return {
            init: function () {
                console.log(a);
            }
        }
    })();

上例中,方法2才是真正的封裝。所以我們可以把上面的Calousel建構函式直接放到裡面Carousel2的立即執行函式的區域性作用域裡,然後再return中,遍歷選取到的所有$('.carousel'),給每一個選取到的$('.carousel')new一個Carousel()的例項。
整體思路如下:

    Carousel2 = (function () { //方法2 寫成一個立即執行函式
        // 將寫好的Carousel建構函式直接放到這個區域性作用域裡 就不會暴露出去
        
        return {
            init: function ($ct) {
//                然後只在這裡遍歷選取到的所有目標元素,每一個元素都相應的建立例項
                   $ct.each(function (index,node) {
                       new Carousel($(node));
                   })
            }
        }
    })();
    Carousel2.init($('.carousel'));

最後可以將Carousel建構函式放到區域性作用域中,這樣就不會將寫好的建構函式暴露出去了。

<script>



    Carousel2 = (function () { //方法2 寫成一個立即執行函式
        // 將寫好的Carousel建構函式直接放到這個區域性作用域裡 就不會暴露出去

        function Carousel ($ct) {
            this.$ct = $ct;
            this.init();
            this.bind();
        }
        Carousel.prototype.init = function () {
            var $imgct = this.$imgct= this.$ct.find('.img-ct');
            var $imgs = this.$imgs = this.$ct.find('.img-ct>li');
            //獲取寬度和個數
            var imgWidth = this.$imgWidth = this.$imgs.width();
            var imgCount = this.$imgCount = this.$imgs.length;
            var $pre = this.$pre = this.$ct.find('.pre'); //不這麼寫的話,下面bind中是找不到$pre的,元件化的核心思想就是都去使用this去進行操作
            var $next = this.$next =this.$ct.find('.next');
            var $bullets = this.$bullets =this.$ct.find('.bullet>li');
            var pageIndex = this.pageIndex = 0;  //記錄當前頁碼
            var animating = this.animating =  false; //針對連續重複點選,設定變數來監聽是否處於動畫過程中

            this.$imgct.append(this.$imgs.first().clone());
            this.$imgct.prepend(this.$imgs.last().clone());
            this.$imgct.width((this.$imgCount+2) * this.$imgWidth);
            this.$imgct.css({
                left: -this.$imgWidth,
            })
        }
        Carousel.prototype.bind = function () {
            var self = this;
            this.$pre.on('click',function () {
                self.playPre(1);//這裡面的this不對,是當前點選的元素this.$pre,所以還是需要儲存this
            })
            this.$next.on('click',function () {
                self.playNext(1);
            })
            this.$bullets.on('click',function () {
                var index = $(this).index();
                if (index < self.pageIndex) {
                    self.playPre(self.pageIndex - index);
                }
                else {
                    self.playNext(index - self.pageIndex);
                }
            })
        }
        Carousel.prototype.playPre = function (num) {
            var self = this;
            if (this.animating) {
                return; //如果還在動畫中,直接return掉
            }
            this.animating = true; //進入動畫,animating設為true表示正在動畫中
            this.$imgct.animate({
                left: '+=' + num * this.$imgWidth,
            },function () { // 儲存this,因為在匿名函式內部
                self.pageIndex -= num;
                if (self.pageIndex === -1) {
                    self.pageIndex = self.$imgCount-1;
                    self.$imgct.css({
                        left: -self.$imgCount*self.$imgWidth,
                    })
                }
                self.setBullets();
                self.animating = false; //動畫結束後重新設定為false
            })
        }
        Carousel.prototype.playNext = function (num) {
            var self = this;
            if (this.animating) {
                return;
            }
            this.animating = true;
            this.$imgct.animate({
                left: '-=' + num * this.$imgWidth,
            },function () {
                self.pageIndex += num;
                if (self.pageIndex === self.$imgCount) {
                    self.pageIndex = 0;
                    self.$imgct.css({
                        left: -self.$imgWidth,
                    })
                }
                self.setBullets();
                self.animating = false;
            })
        }
        Carousel.prototype.setBullets = function () {
            this.$bullets.removeClass('active')
                    .eq(this.pageIndex).addClass('active');
        }
        return {
            init: function ($ct) {
//                然後只在這裡遍歷選取到的所有目標元素,每一個元素都相應的建立例項
                   $ct.each(function (index,node) {
                       new Carousel($(node));
                   })
            }
        }
    })();
    Carousel2.init($('.carousel'));
</script>

懶載入(曝光載入)封裝

懶載入原始碼

相關文章