[字节青训营]前端方向Day5-前端入门 - 基础语言篇 - 如何写好JavaScript-组件封装-手撕简单的轮播图(3) | 豆包MarsCode AI刷题

57 阅读3分钟

源代码:code.juejin.cn/pen/7108191…

[本文适合小白,对于JS不是很清楚,听课听不懂的]

构造方法保持简单,实例化过程更清晰,只需获取核心元素而不处理复杂逻辑。

插件实现了类和功能的分离,将事件绑定和切换逻辑抽离出类定义,使类结构更简洁,增强代码的复用性和灵活性。

插件本质上就是将本可以在 class 内部定义的功能提取出来,作为独立的函数或模块,这样不仅减少了类的复杂性,还可以随时添加或移除,不影响 Slider 类核心。

通过 registerPlugins,可以同步注册多个插件,一行代码即可完成所有注册,使代码更简洁,核心功能与扩展功能互不干扰,从而提升了代码的模块化和可维护性。

  • 自定义事件 slide 用于传递照片索引 idx,这样触发事件时,可以让按钮组接收 idx,将对应的按钮设为选中状态。这样照片和按钮就能同步显示,实现了两者相对应的效果。
class Slider {
  constructor(id, cycle = 3000) { // 定义一个构造方法,在进行实例化时需要传入两个参数,一个是容器id,一个是自动切换时间
    this.container = document.getElementById(id); // 通过实例化时传入的id获取对应的DOM元素
    this.items = this.container.querySelectorAll(
      ".slider-list__item, .slider-list__item--selected",
    ); // 取到所有的图片
    this.cycle = cycle; // 取到实例化时传入的自动切换时间
  }
  registerPlugins(...plugins) { // 注册插件,列表展开符号(...)将所有传入的插件参数转换为一个数组
    plugins.forEach((plugin) => plugin(this)); // 遍历每一个输入的插件,都进行注册
  }
  getSelectedItem() { // 获取当前选中的照片元素
    const selected = this.container.querySelector(
      ".slider-list__item--selected",
    );
    return selected;
  }
  getSelectedItemIndex() { // 返回当前选中照片的索引
    return Array.from(this.items).indexOf(this.getSelectedItem());
  }
  slideTo(idx) { // 移动到索引为idx的照片的位置
    const selected = this.getSelectedItem();
    if (selected) {
      selected.className = "slider-list__item";
    }
    const item = this.items[idx];
    if (item) {
      item.className = "slider-list__item--selected";
    }

    // 创建自定义事件"slide",事件在 DOM 树中向上冒泡,父级元素也可以监听此事件
    const detail = { index: idx }; // 新建一个slide自定义事件,其中的bubbles是指DOM元素可以进行冒泡,其父元素可以进行接收
    const event = new CustomEvent("slide", { bubbles: true, detail }); // detail是数据载体,说明slide里面有一个参数index,其值是idx
    this.container.dispatchEvent(event); // 在 container 元素上触发"slide"事件,所有对该事件设置了监听的代码会被调用
  }
  slideNext() { // 滑到下一张照片,先获取索引,然后令索引值加1,调用slideTo函数
    const currentIdx = this.getSelectedItemIndex();
    const nextIdx = (currentIdx + 1) % this.items.length;
    this.slideTo(nextIdx);
  }
  slidePrevious() { // 滑到上一张照片,先获取索引,然后令索引值减1,调用slideTo函数
    const currentIdx = this.getSelectedItemIndex();
    const previousIdx = (this.items.length + currentIdx - 1) %
      this.items.length;
    this.slideTo(previousIdx);
  }
  addEventListener(type, handler) { // 重写事件监听方法,输入一个event的type,以及对应的event
    this.container.addEventListener(type, handler); // 容器进行监听
  }
  start() { // 开始自动切换函数,需要先清楚当前的计时器,然后设置一个cycle毫秒的计时器
    this.stop();
    this._timer = setInterval(() => this.slideNext(), this.cycle);
  }
  stop() { // 停止自动切换函数 清楚当前的计时器
    clearInterval(this._timer);
  }
}

function pluginController(slider) { // 插件控制器函数
  const controller = slider.container.querySelector(".slide-list__control"); // 取到类为slide-list__control的DOM元素
  if (controller) { // 如果取到的话
    const buttons = controller.querySelectorAll(
      ".slide-list__control-buttons, .slide-list__control-buttons--selected",
    ); // 从该DOM元素中取到所有的按钮
    controller.addEventListener("mouseover", (evt) => { // 事件监听,如果鼠标移动上来 就切换到对应idx的照片
      const idx = Array.from(buttons).indexOf(evt.target);
      if (idx >= 0) {
        slider.slideTo(idx);
        slider.stop();
      }
    });

    controller.addEventListener("mouseout", (evt) => { // 鼠标挪走,就开始自动切换
      slider.start();
    });

    slider.addEventListener("slide", (evt) => { // 刚刚在类中重写的事件监听方法。
      const idx = evt.detail.index; // 获取事件中数据载体detail中参数名为index的参数
      const selected = controller.querySelector( // 从当前DOM元素取出当前选中的按钮
        ".slide-list__control-buttons--selected",
      );
      if (selected) selected.className = "slide-list__control-buttons"; // 将其设置成未选中
      buttons[idx].className = "slide-list__control-buttons--selected"; // 将idx对应的按钮设置成选中
    });
  }
}

function pluginPrevious(slider) { // 切换前一张图片
  const previous = slider.container.querySelector(".slide-list__previous");
  if (previous) {
    previous.addEventListener("click", (evt) => {
      slider.stop();
      slider.slidePrevious();
      slider.start();
      evt.preventDefault();
    });
  }
}

function pluginNext(slider) { // 切换后一张图片
  const next = slider.container.querySelector(".slide-list__next");
  if (next) {
    next.addEventListener("click", (evt) => {
      slider.stop();
      slider.slideNext();
      slider.start();
      evt.preventDefault();
    });
  }
}

// 写了三个插件,然后进行注册。
const slider = new Slider("my-slider");
slider.registerPlugins(pluginController, pluginPrevious, pluginNext);
slider.start();