wingmeng / front-end-quiz

前端小测试答题收集
0 stars 0 forks source link

DOM基础测试30:滑动条 #9

Open wingmeng opened 5 years ago

wingmeng commented 5 years ago

题目:

image

.slider {
    padding: 5px 0;
    position: relative;
    margin: 30px 10%;
    --percent: 0;
}
.slider-track {
    display: block;
    width: 100%; height: 6px;
    background-color: lightgray;
    border: 0; padding: 0;
}
.slider-track::before {
    content: '';
    display: block;
    height: 100%;
    background-color: skyblue;
    width: calc(1% * var(--percent));
}
.slider-thumb {
    position: absolute;
    width: 16px; height: 16px;
    border: 0; padding: 0;
    background: #fff;
    box-shadow: 0 0 0 1px skyblue;
    border-radius: 50%;
    left: calc(1% * var(--percent)); top: 0;
    margin: auto -8px;
}

image


我的答案:

> 在线 DEMO <

  1. 支持 PC 端和移动端;
  2. 支持缺省赋值(第 2 题);
  3. 支持键盘操作;
  4. 可使用 js 动态赋值(第 2 题);
  5. 可指定渲染容器,默认渲染到 body 标签最后(第 1 题);
  6. 支持点击“轨道”赋值定位(第 3 题);
  7. 支持滑块拖动赋值(第 4 题)。

使用方式示例

let myslider1 = new Slider({ el: '#box' });  // 指定容器
new Slider({ value: 50 });  // 缺省赋值
new Slider();  // 无参数(插入到 body 标签最后,赋值为 0)

myslider1.val(value);  // js 动态赋值

已封装的 Slider 组件

class Slider {
  constructor(opts = {}) {
    this.el = opts.el;
    this.value = opts.value || 0;
    this.slider = null;
    this.render();
    this.bindEvt();

    return {
      // 赋值方法
      val: (value) => {
        this.val(value);
      }
    }
  }

  // 渲染 DOM
  render() {
    const container = document.querySelector(this.el);
    const slider = document.createElement('div');

    this.slider = slider;

    // 有缺省值则赋值
    if (this.value) {
      this.val(this.value);
    }

    slider.className = 'slider';
    slider.innerHTML = (
      // 轨道无需获取焦点
      `<button class="slider-track" tabindex="-1"></button>
      <button class="slider-thumb"></button>`
    );

    if (container) {
      container.appendChild(slider);
    } else {
      // 若未指定容器,则在 body 标签最后插入 DOM 结构
      document.body.appendChild(slider);
    }
  }

  // 绑定事件
  bindEvt() {
    const { slider } = this;
    const slider_track = slider.querySelector('.slider-track');
    const slider_thumb = slider.querySelector('.slider-thumb');
    let readyMove = false;

    const startHandle = e => {
      if (e.target === slider_thumb) {
        e.stopPropagation();
        readyMove = true;
      }
    };

    const moveHandle = e => {
      if (readyMove) {
        this.computeVal(e);
      }
    };

    const endHandle = () => readyMove = false;

    // 点击监听
    slider.addEventListener('click', e => {
      // 点击轨道
      if (e.target === slider_track) {
        this.computeVal(e);
      }
    }, false);

    // 键盘监听
    slider.addEventListener('keydown', e => {
      // 滑块获得焦点
      if (document.activeElement === slider_thumb) {
        let value = this.val();

        switch(e.keyCode) {
          case 37:  // 左箭头
            value--;
            break;
          case 39:  // 右箭头
            value++;
            break;
        }

        this.val(value);
      }
    }, false);

    // 开始拖动
    slider.addEventListener('touchstart', startHandle);
    slider.addEventListener('mousedown', startHandle);

    // 拖动中
    window.addEventListener('touchmove', moveHandle);
    window.addEventListener('mousemove', moveHandle);

    // 拖动结束
    window.addEventListener('touchend', endHandle);
    window.addEventListener('mouseup', endHandle);
  }

  // 计算当前值
  computeVal(e) {
    const { width, left } = this.slider.getBoundingClientRect();
    let posX = e.pageX;

    if (e.touches) {  // 兼容移动端
      posX = e.touches[0].pageX;
    }

    this.val((posX - left) / width * 100);
  }

  // 赋值 & 取值
  val(value) {
    if (typeof value === 'undefined') {
      // 返回当前 slider 的 percent 值
      return this.slider.style.getPropertyValue('--percent').trim() || 0;
    }

    if (isNaN(value)) {  // 过滤非法字符
      return;
    }

    // 边界处理
    if (value < 0) {
      value = 0;
    } else if (value > 100) {
      value = 100;
    }

    this.slider.style.setProperty('--percent', value);
  }
}
wingmeng commented 5 years ago

自我评分:优秀

优秀、良好、一般、差劲

不足之处:

  1. 使用 pageX 会导致在出现页面滚动条后定位异常;
  2. 无需使用 getBoundingClientRect

学习收获:

  1. CSS 变量实战,js 获取 CSS 变量用 getPropertyValue,设置用 setProperty
  2. js DOM 元素定位操作。