1. // 一般插件都是 new 类调用,一般情况下只能用 new 调用;
    2. // 插件封装时,简化构造函数中的调用步骤
    3. function Tab(options) {
    4. // 校验这个方法是否是被 new 调用的:如果用 new 调用 this 就是 Tab 的实例,如果不用 new 调用,this 就不是 Tab 的实例
    5. if (!(this instanceof Tab)) {
    6. console.error('Tab is a constructor which should be called by new');
    7. return;
    8. }
    9. // 1. 把配置对象保存实例上
    10. // 在存之前做参数的合法性校验
    11. if (!options || !options.el) {
    12. // 实参没有传递 options 或者 options 对象中没有 el 属性
    13. console.error('缺少options或者options.el属性');
    14. return;
    15. }
    16. this.options = options;
    17. this.init(); // 一般都会有这样一个 init 方法,表示这个插件从这里开始启动工作,一般类似这种名称有 _init、 main、 bootstrap....
    18. }
    19. // 1. 获取元素对象方法
    20. Tab.prototype.queryEle = function () {
    21. // this 当前 Tab 的实例,实例的 options 保存着 配置对象
    22. // 获取容器元素
    23. const EL_ID = this.options.el; // 从实例中取出 options.el
    24. let wrapper = document.querySelector(EL_ID);
    25. // 获取选项卡头,并且保存到实例上
    26. this.headerList = wrapper.querySelectorAll('.header > li');
    27. // 获取选项卡卡片,并且保存到实例上
    28. this.cardList = wrapper.querySelectorAll('div');
    29. };
    30. // 2. 绑定事件的方法
    31. Tab.prototype.bindEvent = function () {
    32. // 给上一步获取来的选项卡头绑定事件
    33. // ? 我们获取来元素对象存哪里了?上一步已经将选项卡头和卡片保存在实例中
    34. let headerList = this.headerList; // 缓存 headerList
    35. // 事件函数this改变解决方案2:箭头函数
    36. for (let i = 0; i < headerList.length; i++) {
    37. headerList[i].onclick = () => {
    38. // 这样写有一个问题,这个事件函数中的 this 是被点击的 li,我们希望 this 是 Tab 类的实例,因为只有 Tab 类的实例才能调用 clearClass 和 addClass 方法;箭头函数的 this 指向它声明时所在作用域(上级作用域)的 this
    39. // 1. 清除选中样式
    40. this.clearClass();
    41. // 2. 给被点击的添加选中样式
    42. this.addClass(i);
    43. }
    44. }
    45. };
    46. // 3. 移除类名的方法
    47. Tab.prototype.clearClass = function () {
    48. // 移除所有的选项卡头、卡片的选中样式
    49. // ? 这些元素对象在哪里存着?这些东西是 Tab 类实例的私有的属性
    50. // let headerList = this.headerList;
    51. // let cardList = this.cardList;
    52. let { headerList, cardList } = this; // 和上面的写法等价
    53. for (let i = 0; i < headerList.length; i++) {
    54. headerList[i].className = '';
    55. cardList[i].className = '';
    56. }
    57. };
    58. // 4. 添加类名的方法
    59. Tab.prototype.addClass = function (index) {
    60. // 给当前被点击的li以及对应的卡片增加选中样式
    61. this.headerList[index].className = 'active';
    62. this.cardList[index].className = 'active';
    63. };
    64. // 5. 简化构造函数中的调用
    65. Tab.prototype.init = function () {
    66. // 2. 执行获取元素对象
    67. this.queryEle(); // this 就是 Tab 的实例
    68. // 3. 绑定事件
    69. this.bindEvent();
    70. };
    71. new Tab({
    72. el: '#tab1'
    73. });