原生Javascript實現星星評分元件

weixin_34208283發表於2018-03-06

前言

元件分兩種狀態

  1. 只讀
    只負責接受資料然後顯示,使用者不可操作,精確到小數點後1位
  2. 可操作
    按道理講,不接受資料,預設為0分,滑鼠移入進行預覽效果,滑鼠移出恢復預設值,滑鼠點選設定預設值

效果圖

5531211-41b4ddd384b792b8.gif
aa.gif

程式碼

html

// 只讀狀態,不繫結事件
<div class="star-component" data-rate="9.2" readonly="true"></div>
// 可操作狀態,初始值為0,不應該傳data-rate值
<div class="star-component"></div>
<script>
  const rate = new StarRate('.star-component');
</script>

css

.star-component {
  position: relative;
  display: inline-block;
}

.star-component .star-item {
  display: inline-block;
  width: 30px;
  height: 30px;
  cursor: pointer;
  padding-right: 5px;
  transition: all .1s ease-in;
}

.star-component:not(.star-readonly) .star-item:hover {
  transform: scale(1.2);
}

.star-component:not(.star-readonly) .star-item:active {
  transform: scale(1.3);
}

.star-component .star-item.star-on {
  background: url('star-on.png') no-repeat center / 30px;
}

.star-component .star-item.star-off {
  background: url('star-off.png') no-repeat center / 30px;
}

// 只讀狀態的星星層,定位覆蓋背景層,然後動態設定寬度
.star-component.star-readonly .star {
  position: absolute;
  top: 0;
  left: 0;
  white-space: nowrap;
  overflow: hidden;
}

.star-component.star-readonly .star-item {
  cursor: not-allowed;
}

javascript

/// StarRate類,如果用jquery寫程式碼會少很多
class StarRate {
  constructor(el, options) {
    this.el = document.querySelectorAll(el); // 元件節點類名
    this.options = options || {}; // 引數
    this.init(); // 當例項化物件之後立刻初始化該元件,而不用使用者自己rate.init();
  }
  insertTemplate() {
    this.el.forEach(item => {
      let readonly = item.classList.contains('star-readonly'); // contains方法可以判斷該節點是否存在指定類名,返回true或者false
      let rate = readonly ? Number(item.getAttribute('data-rate')) : 0; // 評分值
      if (rate > 10 || rate < 0) throw new Error('data-rate的值應為0-10之間'); // 丟擲錯誤警告
      readonly ? this.insertForReadonly(item, rate) : this.insertForUser(item, rate); // 分發
    })
  }
  insertForReadonly(el, rate) {
    // 背景層
    let bgElement = document.createElement('div');
    bgElement.className = 'background';
    // 星星層
    let starElement = document.createElement('div');
    starElement.className = 'star';
    starElement.style.width = rate * 10 + '%'; // 動態設定寬度
    // 插入模板
    for (let i = 0; i < 5; i++) {
      bgElement.innerHTML += '<div class="star-item star-off"></div>';
      starElement.innerHTML += '<div class="star-item star-on"></div>';
    }
    el.appendChild(bgElement);
    el.appendChild(starElement);
  }
  insertForUser(el, rate) {
    // 插入模板
    let template = '';
    for (let i = 0; i < 5; i++) {
      template += `<div class="star-item star-off" data-index="${i}"></div>`
    }
    el.innerHTML = template;
    this.bindEvent(el, rate); // 只為可操作的元件繫結事件
  }
  bindEvent(el, rate) {
    var star = el.querySelectorAll('.star-item');
    star.forEach((item, index) => {
      item.addEventListener('mouseenter', () => {
        // 滑鼠移入預覽
        this.lightOn(el, index + 1);
      });
      item.addEventListener('mouseleave', () => {
        // 滑鼠移開回到預設值
        this.lightOn(el, rate);
      });
      item.addEventListener('click', () => {
        // 滑鼠點選設定預設值
        rate = index + 1;
        item.parentNode.setAttribute('data-rate', (rate * 2)); // 設定data-rate屬性到元件,方便拿
        this.lightOn(el, index + 1);
      });
    })
  }
  lightOn(el, rate) {
    el.querySelectorAll('.star-item').forEach((item, index) => {
      if (index < rate) {
        item.classList.add('star-on');
        item.classList.remove('star-off');
      } else {
        item.classList.add('star-off');
        item.classList.remove('star-on');
      }
    })
  }
  init() {
    this.insertTemplate();
  }
}
如果您喜歡這篇文章,那麼記得動動你們的?,給個like或者關注我哦?。

相關文章