JS面向对象应用,常见组件的封装(轮播,tab,曝光加载)

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>

懒加载(曝光加载)封装

懒加载源代码

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,980评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,178评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,868评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,498评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,492评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,521评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,910评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,569评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,793评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,559评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,639评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,342评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,931评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,904评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,144评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,833评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,350评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,378评论 25 707
  • 从序列中移除重复项且保持元素间顺序不变 方法 可以用集合和生成器来解决 先来了解下什么是可哈希(hashable)...
    Jlan阅读 390评论 0 1
  • 一个人 一个人的时候会想很多。 特别是一个人对未来迷茫的时候会想更多。 回想我这22年的时光,18岁前除了读书就是...
    余保林阅读 277评论 1 0