一、插件封装的思想

基于面向对象的方式来处理

  1. 调取一次插件相当于创建插件的一个实例
  2. 这样私有的可以设置,公有的方法也可以设置

我们要保证几个事情

  1. 灵活且功能强大(适配更多的应用场景)
  2. 容错性和可扩展性要强
  3. 追求极致的性能和优秀的代码管理方式
  4. 开源精神

细节知识点

  1. 我们封装公共方法的时候,如果需要传递的参数过多(超过两个就可以理解为多了),则不要定义形参,让用户依次传递,这样会受到顺序、传或者不传等因素的影响,管理起来很复杂。我们可以把需要传递的值统一放到一个对象中(一般都是 options),这样我们传递的信息可传可不传,顺序也随便,最后把传递的信息覆盖默认的信息即可,管理方便。也方便进行二次扩展
  2. 我们把后期需要用到的信息都挂载(给谁谁谁设置了啥啥啥)到当前的实例上,这样后面不管在那个方法中用这些信息,只要能获取到实例,直接通过实例获取既可
  3. 本插件中需要使用的工具类方法,我们尽量都放到类的私有属性上(普通对象)

二、枚举属性

可枚举属性、不可枚举属性

  1. Object.prototype.AAA = 'zhufeng'
  2. let obj = {
  3. name:'zhufeng',
  4. year:10
  5. // __protp__:Object.prototype
  6. };
  7. for (let key in obj) {
  8. if(!obj.hasOwnProperty(key)) break;
  9. // 防止别人在原型上扩展的方法,是我们不需要的
  10. console.log(key);
  11. }

在 for in 遍历循环的时候,可以被迭代到的属性是可枚举属性,反之是不可枚举的属性。 可枚举的:一般是自己设置的私有属性和方法或者自己设置的共有属性和方法 不可枚举的:js 中基本包装类型的原型属性是不可枚举的,如 Object, Array, Number 等

三、向jQuery扩展插件

JQUERY:extend 向 JQ 内部扩展方法

  1. // 向 JQ 的原型上扩展方法[写插件]
  2. $.fn.extend({ xxx:function () {} })
  3. $('.box').xxx();
  4. // 向 JQ 对象中增加私有的属性方法[完善类库,提供更多工具类方法]
  5. $.extend({ xxx:function () {} })
  6. $.xxx();

代码部分

  1. ~ function ($) {
  2. if (typeof $ === 'undefined') {
  3. // throw new Error() 抛出浏览器异常信息,此时下面代码不在执行
  4. throw new Error('当前插件必须依托JQUERY才可以实现~~');
  5. }
  6. // THROTTLE:函数节流
  7. function throttle(func, wait) {
  8. let timer = null,
  9. result = null,
  10. previous = 0;
  11. return function anonymous(...args) {
  12. let context = this,
  13. now = new Date,
  14. spanTime = wait - (now - previous);
  15. if (spanTime <= 0) {
  16. result = func.call(context, ...args);
  17. clearTimeout(timer);
  18. timer = null;
  19. previous = now;
  20. } else if (!timer) {
  21. timer = setTimeout(() => {
  22. result = func.call(context, ...args);
  23. timer = null;
  24. previous = new Date;
  25. }, spanTime);
  26. }
  27. return result;
  28. }
  29. }
  30. // BANNER-PLUGIN:只封装和轮播图相关的功能(自动轮播、焦点触发、左右按钮)
  31. function bannerPlugin() {
  32. // this:要实现轮播图的容器(原生 JS 对象)
  33. let $this = $(this),
  34. $wrapper = $this.find('.wrapper'),
  35. $pagination = $this.find('.pagination'),
  36. $buttonPrev = $this.find('.button-prev'),
  37. $buttonNext = $this.find('.button-next'),
  38. $slideList = $wrapper.find('.slide'),
  39. $paginationList = $pagination.find('span');
  40. let autoTimer = null,
  41. interval = 1000,
  42. speed = 300,
  43. activeIndex = 0,
  44. count = $slideList.length;
  45. let change = function () {
  46. let $active = $slideList.eq(activeIndex),
  47. $siblings = $active.siblings();
  48. $active.css('transition', `opacity ${speed}ms`);
  49. $siblings.css('transition', `opacity 0ms`);
  50. $active.css('z-index', 1);
  51. $siblings.css('z-index', 0);
  52. $active.css('opacity', 1).on('transitionend', function () {
  53. $siblings.css('opacity', 0);
  54. });
  55. // 焦点对齐
  56. $paginationList.each((index, item) => {
  57. let $item = $(item);
  58. if (index === activeIndex) {
  59. $item.addClass('active');
  60. return;
  61. }
  62. $item.removeClass('active');
  63. });
  64. };
  65. let autoMove = function () {
  66. activeIndex++;
  67. activeIndex >= count ? activeIndex = 0 : null;
  68. change();
  69. };
  70. let handlePagination = function () {
  71. $paginationList.mouseover(throttle(function () {
  72. activeIndex = $(this).index();
  73. change();
  74. }, 500));
  75. };
  76. let handleButton = function () {
  77. $buttonNext.click(throttle(autoMove, 300));
  78. $buttonPrev.click(throttle(function () {
  79. activeIndex--;
  80. activeIndex < 0 ? activeIndex = count - 1 : null;
  81. change();
  82. }, 500));
  83. };
  84. autoTimer = setInterval(autoMove, interval);
  85. handlePagination();
  86. handleButton();
  87. $this.mouseenter(() => clearInterval(autoTimer))
  88. .mouseleave(() => autoTimer = setInterval(autoMove, interval));
  89. }
  90. $.fn.extend({
  91. // bannerPlugin:bannerPlugin
  92. bannerPlugin
  93. });
  94. }(jQuery);

四、插件版轮播图

html部分

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>渐隐渐现版轮播图</title>
  6. <!-- IMPORT CSS -->
  7. <link rel="stylesheet" href="css/reset.min.css">
  8. <link rel="stylesheet" href="css/banner.css">
  9. </head>
  10. <body>
  11. <section class="container" id="container1">
  12. <!-- 轮播图容器 -->
  13. <div class="wrapper"></div>
  14. <!-- 分页器容器 -->
  15. <div class="pagination"></div>
  16. </section>
  17. <section class="container" id="container2">
  18. <!-- 轮播图容器 -->
  19. <div class="wrapper"></div>
  20. <!-- 前进后退按钮 -->
  21. <a href="javascript:;" class="button-prev"></a>
  22. <a href="javascript:;" class="button-next"></a>
  23. </section>
  24. <!-- IMPORT JS -->
  25. <!-- <script src="js/jquery.min.js"></script>
  26. <script src="js/banner-plugin-jquery.js"></script>
  27. <script src="js/banner.js"></script> -->
  28. <script src="js/jquery.min.js"></script>
  29. <script src="js/banner-plugin.min.js"></script>
  30. <script src="js/banner.js"></script>
  31. </body>
  32. </html>

css部分

  1. .container {
  2. box-sizing: border-box;
  3. position: relative;
  4. margin: 20px auto;
  5. width: 1226px;
  6. height: 460px;
  7. overflow: hidden;
  8. }
  9. .container .wrapper {
  10. position: relative;
  11. height: 100%;
  12. }
  13. .container .wrapper .slide {
  14. position: absolute;
  15. top: 0;
  16. left: 0;
  17. box-sizing: border-box;
  18. width: 100%;
  19. height: 100%;
  20. z-index: 0;
  21. opacity: 0;
  22. }
  23. .container .wrapper .slide:nth-child(1) {
  24. z-index: 1;
  25. opacity: 1;
  26. }
  27. .container .wrapper .slide img {
  28. width: 100%;
  29. height: 100%;
  30. }
  31. .container .wrapper .slide span{
  32. display: block;
  33. transform: translateY(-100px);
  34. font-size: 30px;
  35. color: red;
  36. }
  37. .container .pagination {
  38. position: absolute;
  39. right: 20px;
  40. bottom: 20px;
  41. z-index: 999;
  42. font-size: 0;
  43. }
  44. .container .pagination span {
  45. display: inline-block;
  46. margin: 0 4px;
  47. width: 6px;
  48. height: 6px;
  49. background: rgba(0, 0, 0, .4);
  50. border: 2px solid rgba(255, 255, 255, .4);
  51. border-radius: 50%;
  52. cursor: pointer;
  53. }
  54. .container .pagination span.active {
  55. background: rgba(255, 255, 255, .4);
  56. border-color: rgba(0, 0, 0, .4);
  57. }
  58. .container .button-prev,
  59. .container .button-next {
  60. position: absolute;
  61. top: 50%;
  62. margin-top: -35px;
  63. z-index: 999;
  64. width: 40px;
  65. height: 70px;
  66. background: url("../images/icon-slides.png") no-repeat;
  67. }
  68. .container .button-prev {
  69. left: 0;
  70. background-position: -83px 0;
  71. }
  72. .container .button-prev:hover {
  73. background-position: 0 0;
  74. }
  75. .container .button-next {
  76. right: 0;
  77. background-position: -124px 0;
  78. }
  79. .container .button-next:hover {
  80. background-position: -41px 0;
  81. }

清除默认样式

  1. 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部分

  1. ~ function () {
  2. /* Banner:渐隐渐现轮播图插件 */
  3. class Banner {
  4. constructor(selector, options = {}) {
  5. // 参数初始化
  6. this.initialParams(options);
  7. // 获取需要操作的容器
  8. if (!selector) throw new ReferenceError('The first selector parameter must be passed~~');
  9. if (typeof selector === "string") {
  10. this.container = document.querySelector(selector);
  11. } else if (selector.nodeType) {
  12. this.container = selector;
  13. }
  14. this.wrapper = this.container.querySelector('.wrapper');
  15. this.slideList = this.wrapper.querySelectorAll('.slide');
  16. this.autoTimer = null;
  17. this.activeIndex = this.initialSlide;
  18. this.count = this.slideList.length;
  19. // 初始展示SLIDE
  20. [].forEach.call(this.slideList, (item, index) => {
  21. if (index === this.initialSlide) {
  22. item.style.zIndex = 1;
  23. item.style.opacity = 1;
  24. return;
  25. }
  26. item.style.zIndex = 0;
  27. item.style.opacity = 0;
  28. });
  29. // 自动轮播处理
  30. if (this.autoplay) {
  31. let anonymous = this.autoMove.bind(this);
  32. this.autoTimer = setInterval(anonymous, this.autoplay);
  33. this.container.addEventListener('mouseenter', () => {
  34. clearInterval(this.autoTimer);
  35. });
  36. this.container.addEventListener('mouseleave', () => {
  37. this.autoTimer = setInterval(anonymous, this.autoplay);
  38. });
  39. }
  40. // 分页器的处理
  41. if (this.pagination && this.pagination.el) {
  42. this.handlePagination();
  43. }
  44. // 前进和后退按钮处理
  45. if (this.navigation) {
  46. this.handleButton();
  47. }
  48. // 钩子函数的处理
  49. // 初始化成功
  50. this.on && this.on.init && this.on.init.call(this, this);
  51. }
  52. /*===Banner.prototype===*/
  53. /*
  54. * initialParams:初始化插件的参数配置信息
  55. */
  56. initialParams(options) {
  57. // 1.首先设置默认的参数信息
  58. let _default = {
  59. initialSlide: 0,
  60. speed: 300,
  61. autoplay: 3000,
  62. pagination: {
  63. el: '.pagination',
  64. triggerEvent: 'click'
  65. },
  66. navigation: {
  67. nextEl: '.button-next',
  68. prevEl: '.button-prev',
  69. hide: true
  70. },
  71. on: {
  72. init: function (examp) {},
  73. transitionStart: function (examp) {},
  74. transitionEnd: function (examp) {}
  75. }
  76. };
  77. // 2.把传递进来的OPTIONS中的信息替换_DEFAULT中的信息
  78. for (let key in options) {
  79. if (!options.hasOwnProperty(key)) break;
  80. if (/^(pagination|navigation|on)$/i.test(key)) continue;
  81. _default[key] = options[key];
  82. }
  83. // pagination
  84. let pagination = options.pagination;
  85. if (pagination !== null) {
  86. pagination = pagination || {};
  87. for (let key in pagination) {
  88. if (!pagination.hasOwnProperty(key)) break;
  89. _default['pagination'][key] = pagination[key];
  90. }
  91. } else {
  92. _default['pagination'] = null;
  93. }
  94. // navigation
  95. let navigation = options.navigation;
  96. if (navigation !== null) {
  97. navigation = navigation || {};
  98. for (let key in navigation) {
  99. if (!navigation.hasOwnProperty(key)) break;
  100. _default['navigation'][key] = navigation[key];
  101. }
  102. } else {
  103. _default['navigation'] = null;
  104. }
  105. // on
  106. let _on = options.on;
  107. if (_on !== null) {
  108. _on = _on || {};
  109. for (let key in _on) {
  110. if (!_on.hasOwnProperty(key)) break;
  111. _default['on'][key] = _on[key];
  112. }
  113. } else {
  114. _default['on'] = null;
  115. }
  116. // 3.把处理好的信息挂载到实例上
  117. for (let key in _default) {
  118. if (!_default.hasOwnProperty(key)) break;
  119. this[key] = _default[key];
  120. }
  121. }
  122. /* 实现轮播图切换 */
  123. change() {
  124. [].forEach.call(this.slideList, (item, index) => {
  125. if (index === this.activeIndex) {
  126. // 当前要操作的SLIDE
  127. item.style.transition = `opacity ${this.speed}ms`;
  128. item.style.zIndex = 1;
  129. return;
  130. }
  131. // 其余的SLIDE
  132. item.style.transition = `opacity 0ms`;
  133. item.style.zIndex = 0;
  134. });
  135. // 开始动画
  136. // 动画开始前的钩子函数
  137. this.on && this.on.transitionStart && this.on.transitionStart.call(this, this);
  138. let active = this.slideList[this.activeIndex];
  139. active.style.opacity = 1;
  140. active.addEventListener('transitionend', () => {
  141. // addEventListener:DOM2级事件绑定
  142. [].forEach.call(this.slideList, (item, index) => {
  143. if (index !== this.activeIndex) {
  144. item.style.opacity = 0;
  145. }
  146. });
  147. // 动画结束后的钩子函数
  148. this.on && this.on.transitionEnd && this.on.transitionEnd.call(this, this);
  149. });
  150. // 焦点对齐
  151. if (this.paginationList) {
  152. [].forEach.call(this.paginationList, (item, index) => {
  153. if (index === this.activeIndex) {
  154. item.className = "active";
  155. return;
  156. }
  157. item.className = "";
  158. });
  159. }
  160. }
  161. /* 自动轮播 */
  162. autoMove() {
  163. this.activeIndex++;
  164. this.activeIndex >= this.count ? this.activeIndex = 0 : null;
  165. this.change();
  166. }
  167. /* 分页器处理 */
  168. handlePagination() {
  169. // 获取分页器盒子,动态创建内容
  170. this.paginationBox = this.container.querySelector(this.pagination.el);
  171. let str = ``;
  172. for (let i = 0; i < this.count; i++) {
  173. str += `<span class='${i===this.activeIndex?'active':''}'></span>`;
  174. }
  175. this.paginationBox.innerHTML = str;
  176. this.paginationList = this.paginationBox.querySelectorAll('span');
  177. // 是否焦点触发切换
  178. if (this.pagination.triggerEvent) {
  179. [].forEach.call(this.paginationList, (item, index) => {
  180. item.addEventListener(this.pagination.triggerEvent, Banner.throttle(() => {
  181. this.activeIndex = index;
  182. this.change();
  183. }, 500));
  184. });
  185. }
  186. }
  187. /* 前进后退按钮 */
  188. handleButton() {
  189. this.prevEl = this.container.querySelector(this.navigation.prevEl);
  190. this.prevEl.addEventListener('click', Banner.throttle(() => {
  191. this.activeIndex--;
  192. this.activeIndex < 0 ? this.activeIndex = this.count - 1 : null;
  193. this.change();
  194. }, 500));
  195. this.nextEl = this.container.querySelector(this.navigation.nextEl);
  196. this.nextEl.addEventListener('click', Banner.throttle(this.autoMove.bind(this), 500));
  197. // 显示隐藏的处理
  198. if (this.navigation.hide) {
  199. this.prevEl.style.display = 'none';
  200. this.nextEl.style.display = 'none';
  201. this.container.addEventListener('mouseenter', () => {
  202. this.prevEl.style.display = 'block';
  203. this.nextEl.style.display = 'block';
  204. });
  205. this.container.addEventListener('mouseleave', () => {
  206. this.prevEl.style.display = 'none';
  207. this.nextEl.style.display = 'none';
  208. });
  209. }
  210. }
  211. /* 设置私有的方法 */
  212. static throttle(func, wait) {
  213. let timer = null,
  214. result = null,
  215. previous = 0;
  216. return function anonymous(...args) {
  217. let context = this,
  218. now = new Date,
  219. spanTime = wait - (now - previous);
  220. if (spanTime <= 0) {
  221. result = func.call(context, ...args);
  222. clearTimeout(timer);
  223. timer = null;
  224. previous = now;
  225. } else if (!timer) {
  226. timer = setTimeout(() => {
  227. result = func.call(context, ...args);
  228. timer = null;
  229. previous = new Date;
  230. }, spanTime);
  231. }
  232. return result;
  233. }
  234. }
  235. }
  236. window.Banner = Banner;
  237. }();