一、插件封装的思想
基于面向对象的方式来处理
- 调取一次插件相当于创建插件的一个实例
- 这样私有的可以设置,公有的方法也可以设置
我们要保证几个事情
- 灵活且功能强大(适配更多的应用场景)
- 容错性和可扩展性要强
- 追求极致的性能和优秀的代码管理方式
- 开源精神
细节知识点
- 我们封装公共方法的时候,如果需要传递的参数过多(超过两个就可以理解为多了),则不要定义形参,让用户依次传递,这样会受到顺序、传或者不传等因素的影响,管理起来很复杂。我们可以把需要传递的值统一放到一个对象中(一般都是 options),这样我们传递的信息可传可不传,顺序也随便,最后把传递的信息覆盖默认的信息即可,管理方便。也方便进行二次扩展
- 我们把后期需要用到的信息都挂载(给谁谁谁设置了啥啥啥)到当前的实例上,这样后面不管在那个方法中用这些信息,只要能获取到实例,直接通过实例获取既可
- 本插件中需要使用的工具类方法,我们尽量都放到类的私有属性上(普通对象)
二、枚举属性
可枚举属性、不可枚举属性
Object.prototype.AAA = 'zhufeng'let obj = {name:'zhufeng',year:10// __protp__:Object.prototype};for (let key in obj) {if(!obj.hasOwnProperty(key)) break;// 防止别人在原型上扩展的方法,是我们不需要的console.log(key);}
在 for in 遍历循环的时候,可以被迭代到的属性是可枚举属性,反之是不可枚举的属性。 可枚举的:一般是自己设置的私有属性和方法或者自己设置的共有属性和方法 不可枚举的:js 中基本包装类型的原型属性是不可枚举的,如 Object, Array, Number 等
三、向jQuery扩展插件
JQUERY:extend 向 JQ 内部扩展方法
// 向 JQ 的原型上扩展方法[写插件]$.fn.extend({ xxx:function () {} })$('.box').xxx();// 向 JQ 对象中增加私有的属性方法[完善类库,提供更多工具类方法]$.extend({ xxx:function () {} })$.xxx();
代码部分
~ function ($) {if (typeof $ === 'undefined') {// throw new Error() 抛出浏览器异常信息,此时下面代码不在执行throw new Error('当前插件必须依托JQUERY才可以实现~~');}// THROTTLE:函数节流function throttle(func, wait) {let timer = null,result = null,previous = 0;return function anonymous(...args) {let context = this,now = new Date,spanTime = wait - (now - previous);if (spanTime <= 0) {result = func.call(context, ...args);clearTimeout(timer);timer = null;previous = now;} else if (!timer) {timer = setTimeout(() => {result = func.call(context, ...args);timer = null;previous = new Date;}, spanTime);}return result;}}// BANNER-PLUGIN:只封装和轮播图相关的功能(自动轮播、焦点触发、左右按钮)function bannerPlugin() {// this:要实现轮播图的容器(原生 JS 对象)let $this = $(this),$wrapper = $this.find('.wrapper'),$pagination = $this.find('.pagination'),$buttonPrev = $this.find('.button-prev'),$buttonNext = $this.find('.button-next'),$slideList = $wrapper.find('.slide'),$paginationList = $pagination.find('span');let autoTimer = null,interval = 1000,speed = 300,activeIndex = 0,count = $slideList.length;let change = function () {let $active = $slideList.eq(activeIndex),$siblings = $active.siblings();$active.css('transition', `opacity ${speed}ms`);$siblings.css('transition', `opacity 0ms`);$active.css('z-index', 1);$siblings.css('z-index', 0);$active.css('opacity', 1).on('transitionend', function () {$siblings.css('opacity', 0);});// 焦点对齐$paginationList.each((index, item) => {let $item = $(item);if (index === activeIndex) {$item.addClass('active');return;}$item.removeClass('active');});};let autoMove = function () {activeIndex++;activeIndex >= count ? activeIndex = 0 : null;change();};let handlePagination = function () {$paginationList.mouseover(throttle(function () {activeIndex = $(this).index();change();}, 500));};let handleButton = function () {$buttonNext.click(throttle(autoMove, 300));$buttonPrev.click(throttle(function () {activeIndex--;activeIndex < 0 ? activeIndex = count - 1 : null;change();}, 500));};autoTimer = setInterval(autoMove, interval);handlePagination();handleButton();$this.mouseenter(() => clearInterval(autoTimer)).mouseleave(() => autoTimer = setInterval(autoMove, interval));}$.fn.extend({// bannerPlugin:bannerPluginbannerPlugin});}(jQuery);
四、插件版轮播图
html部分
<!DOCTYPE html><html><head><meta charset="UTF-8"><title>渐隐渐现版轮播图</title><!-- IMPORT CSS --><link rel="stylesheet" href="css/reset.min.css"><link rel="stylesheet" href="css/banner.css"></head><body><section class="container" id="container1"><!-- 轮播图容器 --><div class="wrapper"></div><!-- 分页器容器 --><div class="pagination"></div></section><section class="container" id="container2"><!-- 轮播图容器 --><div class="wrapper"></div><!-- 前进后退按钮 --><a href="javascript:;" class="button-prev"></a><a href="javascript:;" class="button-next"></a></section><!-- IMPORT JS --><!-- <script src="js/jquery.min.js"></script><script src="js/banner-plugin-jquery.js"></script><script src="js/banner.js"></script> --><script src="js/jquery.min.js"></script><script src="js/banner-plugin.min.js"></script><script src="js/banner.js"></script></body></html>
css部分
.container {box-sizing: border-box;position: relative;margin: 20px auto;width: 1226px;height: 460px;overflow: hidden;}.container .wrapper {position: relative;height: 100%;}.container .wrapper .slide {position: absolute;top: 0;left: 0;box-sizing: border-box;width: 100%;height: 100%;z-index: 0;opacity: 0;}.container .wrapper .slide:nth-child(1) {z-index: 1;opacity: 1;}.container .wrapper .slide img {width: 100%;height: 100%;}.container .wrapper .slide span{display: block;transform: translateY(-100px);font-size: 30px;color: red;}.container .pagination {position: absolute;right: 20px;bottom: 20px;z-index: 999;font-size: 0;}.container .pagination span {display: inline-block;margin: 0 4px;width: 6px;height: 6px;background: rgba(0, 0, 0, .4);border: 2px solid rgba(255, 255, 255, .4);border-radius: 50%;cursor: pointer;}.container .pagination span.active {background: rgba(255, 255, 255, .4);border-color: rgba(0, 0, 0, .4);}.container .button-prev,.container .button-next {position: absolute;top: 50%;margin-top: -35px;z-index: 999;width: 40px;height: 70px;background: url("../images/icon-slides.png") no-repeat;}.container .button-prev {left: 0;background-position: -83px 0;}.container .button-prev:hover {background-position: 0 0;}.container .button-next {right: 0;background-position: -124px 0;}.container .button-next:hover {background-position: -41px 0;}
清除默认样式
body,h1,h2,h3,h4,h5,h6,hr,p,blockquote,dl,dt,dd,ul,ol,li,button,input,textarea,th,td{margin:0;padding:0}body{font-size:12px;font-style:normal;font-family:"\5FAE\8F6F\96C5\9ED1",Helvetica,sans-serif}small{font-size:12px}h1{font-size:18px}h2{font-size:16px}h3{font-size:14px}h4,h5,h6{font-size:100%}ul,ol{list-style:none}a{text-decoration:none;background-color:transparent}a:hover,a:active{outline-width:0;text-decoration:none}table{border-collapse:collapse;border-spacing:0}hr{border:0;height:1px}img{border-style:none}img:not([src]){display:none}svg:not(:root){overflow:hidden}html{-webkit-touch-callout:none;-webkit-text-size-adjust:100%}input,textarea,button,a{-webkit-tap-highlight-color:rgba(0,0,0,0)}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block}audio:not([controls]),video:not([controls]){display:none;height:0}progress{vertical-align:baseline}mark{background-color:#ff0;color:#000}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-0.25em}sup{top:-0.5em}button,input,select,textarea{font-size:100%;outline:0}button,input{overflow:visible}button,select{text-transform:none}textarea{overflow:auto}button,html [type="button"],[type="reset"],[type="submit"]{-webkit-appearance:button}button::-moz-focus-inner,[type="button"]::-moz-focus-inner,[type="reset"]::-moz-focus-inner,[type="submit"]::-moz-focus-inner{border-style:none;padding:0}button:-moz-focusring,[type="button"]:-moz-focusring,[type="reset"]:-moz-focusring,[type="submit"]:-moz-focusring{outline:1px dotted ButtonText}[type="checkbox"],[type="radio"]{box-sizing:border-box;padding:0}[type="number"]::-webkit-inner-spin-button,[type="number"]::-webkit-outer-spin-button{height:auto}[type="search"]{-webkit-appearance:textfield;outline-offset:-2px}[type="search"]::-webkit-search-cancel-button,[type="search"]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-input-placeholder{color:inherit;opacity:.54}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}.clearfix:after{display:block;height:0;content:"";clear:both}
js部分
~ function () {/* Banner:渐隐渐现轮播图插件 */class Banner {constructor(selector, options = {}) {// 参数初始化this.initialParams(options);// 获取需要操作的容器if (!selector) throw new ReferenceError('The first selector parameter must be passed~~');if (typeof selector === "string") {this.container = document.querySelector(selector);} else if (selector.nodeType) {this.container = selector;}this.wrapper = this.container.querySelector('.wrapper');this.slideList = this.wrapper.querySelectorAll('.slide');this.autoTimer = null;this.activeIndex = this.initialSlide;this.count = this.slideList.length;// 初始展示SLIDE[].forEach.call(this.slideList, (item, index) => {if (index === this.initialSlide) {item.style.zIndex = 1;item.style.opacity = 1;return;}item.style.zIndex = 0;item.style.opacity = 0;});// 自动轮播处理if (this.autoplay) {let anonymous = this.autoMove.bind(this);this.autoTimer = setInterval(anonymous, this.autoplay);this.container.addEventListener('mouseenter', () => {clearInterval(this.autoTimer);});this.container.addEventListener('mouseleave', () => {this.autoTimer = setInterval(anonymous, this.autoplay);});}// 分页器的处理if (this.pagination && this.pagination.el) {this.handlePagination();}// 前进和后退按钮处理if (this.navigation) {this.handleButton();}// 钩子函数的处理// 初始化成功this.on && this.on.init && this.on.init.call(this, this);}/*===Banner.prototype===*//** initialParams:初始化插件的参数配置信息*/initialParams(options) {// 1.首先设置默认的参数信息let _default = {initialSlide: 0,speed: 300,autoplay: 3000,pagination: {el: '.pagination',triggerEvent: 'click'},navigation: {nextEl: '.button-next',prevEl: '.button-prev',hide: true},on: {init: function (examp) {},transitionStart: function (examp) {},transitionEnd: function (examp) {}}};// 2.把传递进来的OPTIONS中的信息替换_DEFAULT中的信息for (let key in options) {if (!options.hasOwnProperty(key)) break;if (/^(pagination|navigation|on)$/i.test(key)) continue;_default[key] = options[key];}// paginationlet pagination = options.pagination;if (pagination !== null) {pagination = pagination || {};for (let key in pagination) {if (!pagination.hasOwnProperty(key)) break;_default['pagination'][key] = pagination[key];}} else {_default['pagination'] = null;}// navigationlet navigation = options.navigation;if (navigation !== null) {navigation = navigation || {};for (let key in navigation) {if (!navigation.hasOwnProperty(key)) break;_default['navigation'][key] = navigation[key];}} else {_default['navigation'] = null;}// onlet _on = options.on;if (_on !== null) {_on = _on || {};for (let key in _on) {if (!_on.hasOwnProperty(key)) break;_default['on'][key] = _on[key];}} else {_default['on'] = null;}// 3.把处理好的信息挂载到实例上for (let key in _default) {if (!_default.hasOwnProperty(key)) break;this[key] = _default[key];}}/* 实现轮播图切换 */change() {[].forEach.call(this.slideList, (item, index) => {if (index === this.activeIndex) {// 当前要操作的SLIDEitem.style.transition = `opacity ${this.speed}ms`;item.style.zIndex = 1;return;}// 其余的SLIDEitem.style.transition = `opacity 0ms`;item.style.zIndex = 0;});// 开始动画// 动画开始前的钩子函数this.on && this.on.transitionStart && this.on.transitionStart.call(this, this);let active = this.slideList[this.activeIndex];active.style.opacity = 1;active.addEventListener('transitionend', () => {// addEventListener:DOM2级事件绑定[].forEach.call(this.slideList, (item, index) => {if (index !== this.activeIndex) {item.style.opacity = 0;}});// 动画结束后的钩子函数this.on && this.on.transitionEnd && this.on.transitionEnd.call(this, this);});// 焦点对齐if (this.paginationList) {[].forEach.call(this.paginationList, (item, index) => {if (index === this.activeIndex) {item.className = "active";return;}item.className = "";});}}/* 自动轮播 */autoMove() {this.activeIndex++;this.activeIndex >= this.count ? this.activeIndex = 0 : null;this.change();}/* 分页器处理 */handlePagination() {// 获取分页器盒子,动态创建内容this.paginationBox = this.container.querySelector(this.pagination.el);let str = ``;for (let i = 0; i < this.count; i++) {str += `<span class='${i===this.activeIndex?'active':''}'></span>`;}this.paginationBox.innerHTML = str;this.paginationList = this.paginationBox.querySelectorAll('span');// 是否焦点触发切换if (this.pagination.triggerEvent) {[].forEach.call(this.paginationList, (item, index) => {item.addEventListener(this.pagination.triggerEvent, Banner.throttle(() => {this.activeIndex = index;this.change();}, 500));});}}/* 前进后退按钮 */handleButton() {this.prevEl = this.container.querySelector(this.navigation.prevEl);this.prevEl.addEventListener('click', Banner.throttle(() => {this.activeIndex--;this.activeIndex < 0 ? this.activeIndex = this.count - 1 : null;this.change();}, 500));this.nextEl = this.container.querySelector(this.navigation.nextEl);this.nextEl.addEventListener('click', Banner.throttle(this.autoMove.bind(this), 500));// 显示隐藏的处理if (this.navigation.hide) {this.prevEl.style.display = 'none';this.nextEl.style.display = 'none';this.container.addEventListener('mouseenter', () => {this.prevEl.style.display = 'block';this.nextEl.style.display = 'block';});this.container.addEventListener('mouseleave', () => {this.prevEl.style.display = 'none';this.nextEl.style.display = 'none';});}}/* 设置私有的方法 */static throttle(func, wait) {let timer = null,result = null,previous = 0;return function anonymous(...args) {let context = this,now = new Date,spanTime = wait - (now - previous);if (spanTime <= 0) {result = func.call(context, ...args);clearTimeout(timer);timer = null;previous = now;} else if (!timer) {timer = setTimeout(() => {result = func.call(context, ...args);timer = null;previous = new Date;}, spanTime);}return result;}}}window.Banner = Banner;}();
