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

33 阅读4分钟

注:只针对js进行详细解释。

监听事件: element.addEventListener(eventType, (event) => { }); --element :绑定事件的 HTML 元素。

--eventType : 事件类型,例如 "click", "mouseover", "keydown" 等。

--(event) => { } : 是事件触发后执行的回调函数。event 是事件对象,它包含触发事件的相关信息。

自定义事件:

const event = new CustomEvent(eventName, {
  detail: dataObject, // 可选,用于传递数据
  bubbles: true,      // 可选,是否允许事件冒泡
  cancelable: true    // 可选,是否允许取消事件
});
element.dispatchEvent(event); // 在element元素上触发自定义事件

--eventName 是自定义事件的名称,例如 "customEventName".

--detail 是一个对象,用于携带需要传递的数据。

--bubbles 表示事件是否冒泡到父元素,默认为 false

--cancelable 表示事件是否可以被取消,默认为 false

默认事件:

许多 HTML 元素在触发特定事件时会执行默认行为。例如:

-- 在 <a> 标签上点击会跳转到链接地址。

-- 在 <form> 表单上提交会刷新页面并发送数据。

-- 在文本框上按回车键会提交表单。

阻止默认事件的原因:

-- 有时,我们希望自定义这些元素的行为,而不执行它们的默认操作。此时,可以使用 event.preventDefault() 阻止默认行为。

class Slider {
  constructor(id, cycle = 3000) {
    this.container = document.getElementById(id); // 根据容器的id获取容器 这里获取到的还是最外层的div元素
    this.items = this.container.querySelectorAll( // 根据两个类名查询所有的图片元素,返回一个NodeList对象
      ".slider-list__item, .slider-list__item--selected",
    );
    this.cycle = cycle;  // 取到实例化对象时传入的参数
​
    const controller = this.container.querySelector(".slide-list__control"); // 根据类名取到按钮组
    if (controller) {// 如果取到的话
      const buttons = controller.querySelectorAll( // 根据按钮的class获取button元素
        ".slide-list__control-buttons, .slide-list__control-buttons--selected",
      );
      controller.addEventListener("mouseover", (evt) => { // 设置一个监听事件。监听事件的格式在下面会进行讲解。
        const idx = Array.from(buttons).indexOf(evt.target); // 由于buttons也是一个NodeList对象,所以需要先转换成数组。然后取到指向的按钮的索引
        if (idx >= 0) {
          this.slideTo(idx); // 移动到这个索引对应的图片
          this.stop();  // 暂停cycle毫秒一次的自动切换
        }
      });
​
      controller.addEventListener("mouseout", (evt) => {// 设置另一个监听事件 用于鼠标挪走之后进行cycle毫秒一次的自动切换
        this.start();
      });
​
      this.container.addEventListener("slide", (evt) => { // 设置一个监听事件,不过这个监听事件是通过new CustomEvent自定义的。
        const idx = evt.detail.index;               // detail是slide自定义事件的数据载体,这个载体里面放着小圆圈的索引
        const selected = controller.querySelector(  // controller时按钮组,在按钮组中找到选中的按钮 用selected来接收
          ".slide-list__control-buttons--selected",
        );
        if (selected) selected.className = "slide-list__control-buttons"; // 如果接收到了,那么就把该按钮的class设为未选中
        buttons[idx].className = "slide-list__control-buttons--selected";// 然后把鼠标指向的按钮的class设为选中
      });
    }
​
    const previous = this.container.querySelector(".slide-list__previous"); // 从容器中获取左箭头元素
    if (previous) { // 如果获取到了
      previous.addEventListener("click", (evt) => { // 设置一个监听事件,点击进行触发。
        this.stop(); // 点击之后 停止自动切换
        this.slidePrevious(); // 挪到前一张照片
        this.start(); // 开始自动切换
        evt.preventDefault(); // 阻止默认事件(例如页面滚动等)
      });
    }
​
    const next = this.container.querySelector(".slide-list__next"); // 从容器中获取右箭头元素
    if (next) {
      next.addEventListener("click", (evt) => { // 设置一个监听事件,点击进行触发
        this.stop(); // 点击之后 停止自动切换
        this.slideNext(); // 挪到后一张照片
        this.start(); // 开始自动切换
        evt.preventDefault(); // 阻止默认事件
      });
    }
  }
  getSelectedItem() { // 定义一个函数,用于返回选中图片的元素
    let selected = this.container.querySelector(".slider-list__item--selected");
    return selected;
  }
  getSelectedItemIndex() { // 定义一个函数,用于返回选中图片的索引
    return Array.from(this.items).indexOf(this.getSelectedItem());
  }
  slideTo(idx) { // 定义一个函数,用于指定切换对应图片
    let selected = this.getSelectedItem();
    if (selected) {
      selected.className = "slider-list__item";
    }
    let item = this.items[idx];
    if (item) {
      item.className = "slider-list__item--selected";
    }
​
    const detail = { index: idx };  // 定义一个对象detail,里面包含一个属性 index,属性的值是idx,代表指定切换照片的索引
    const event = new CustomEvent("slide", { bubbles: true, detail }); // 自定义一个slide事件 ,bubbles: true代表可以在DOM树上冒泡,父元素可以对此事件进行监听
    // detail 是事件的数据载体,通过 { detail } 将包含当前索引的 detail 对象附加到事件上。
    this.container.dispatchEvent(event); // 触发slide自定义事件。
    // 它在 container 元素上触发 "slide" 事件,所有对该事件设置了监听的代码(如 controller.addEventListener("slide", ...))会被调用。
  }
  slideNext() { // 切换下一张图片
    let currentIdx = this.getSelectedItemIndex();
    let nextIdx = (currentIdx + 1) % this.items.length;
    this.slideTo(nextIdx);
  }
  slidePrevious() { // 切换上一张图片
    let currentIdx = this.getSelectedItemIndex();
    let previousIdx = (this.items.length + currentIdx - 1) % this.items.length; // 这里先加一个图片的个数是因为怕索引是0的时候,减1出现负数的情况
    this.slideTo(previousIdx);
  }
  start() { // 开始自动切换照片函数
    this.stop();  // 先调用stop函数把计时器清零
    this._timer = setInterval(() => this.slideNext(), this.cycle); // 设置计时器,cycle毫秒调用一次slideNext函数,往后切换一张图片
  }
  stop() { // 停止自动切换照片函数
    clearInterval(this._timer); // 清楚计时器
  }
}
​
const slider = new Slider("my-slider");
slider.start();