記一次使iview庫的Radio可取消的過程

概述

ui庫用的是iview .
radio、radioGroup是咱們很是經常使用的組件。radio有一個特徵是選中以後沒法取消。現實中取消radio的需求是常見且能夠理解的。
因此看到這個需求以後第一嘗試 在iview組件之上搞一搞,這一搞就入坑了,如今就來理一理個人入坑之路吧。css

1. 原生組件radio的取消

首先咱們來看一下在 vue 中使用原生的radio組件如何使用取消。 原生radio取消選中的文章很是多,隨便拎一篇看看就行,好比 取消radio的三種方法
嗯,原理就是給radio元素的checked屬性賦值爲 false。
具體在 vue 中使用是這樣的:vue

const { name, value } = data;
<div>
    <input
        class="xxx"
        type="radio"
        name={this.id}
        ref={name}
        value={value}
        onClick={(vlaue) => this.radioGroupChange(value, name)}
    />
    {name}
</div>;

// 方法
radioGroupChange (value, name) {
    if (this.checked === value) {
        // 取消選中
        this.$refs[name].checked = false;
        // 當前選中值爲空
        this.checked = '';
    } else {
        this.checked = value;
    }
}

這樣就OK了。大概惟一區別是 在 vue中經過 ref取到真實 dom。vuex

2. iview中radio取消之路

借鑑原生的radio,看來取消也不難嘛,監聽radio或者 radio的change事件嘛。然而你會發現,重複點選某一個 radio的時候,iview中的on-change函數跟本木有響應。這是爲何呢? 去 iview 源碼中看一看瀏覽器

2.1 iview組件radio源碼探究

先上代碼爲敬:
先看下 radio.vue的 templateiview

<template>
    <label :class="wrapClasses">
        <span :class="radioClasses">
            <span :class="innerClasses"></span>
            <input
                type="radio"
                :class="inputClasses"
                :disabled="disabled"
                :checked="currentValue"
                :name="groupName"
                @change="change"
                @focus="onFocus"
                @blur="onBlur">
        </span><slot>{{ label }}</slot>
    </label>
</template>

再看下 change的響應函數dom

change (event) {
    // debugger
    if (this.disabled) {
        return false;
    }

    const checked = event.target.checked;
    this.currentValue = checked;

    const value = checked ? this.trueValue : this.falseValue;
    this.$emit('input', value);

    if (this.group) {
        if (this.label !== undefined) {
            this.parent.change({
                value: this.label,
                checked: this.value
            });
        }
    } else {
        this.$emit('on-change', value);
        this.dispatch('FormItem', 'on-form-change', value);
    }
},

一開始懷疑 change事件中對 value作了處理,只有 不同的時候才函數


emit,仔細看,change函數中並無這部分處理。
問題在於 input 這裏監聽的 change 事件而不是 click事件,因此反覆點擊同一個 radio的時候沒有 響應是正常的。
clipboard.png佈局

2.2 如何監聽到 click事件

看到上面是否是就想 我在 radio上綁定一個click 事件就能夠了;事實證實是不能夠的,想一下就知道了,由於 iview並無監聽click事件天然也沒有把click事件 emit到父組件,因此是沒法監聽click的。
既然 Radio 不支持click,那就想辦法在能讓元素響應 click 測試

2.2.1 Radio外增長一層 元素: div/span

這裏的思路是在 RadioGroup的Radio 外包裹一層 div/span,在這個元素上綁定 click 函數,這個元素確定會響應 click,在響應函數中判斷 當前 選中的 value(這個變量一般會維護在data中,暫且用 this.value 表示) 和點擊元素包裹的 value 值是否相同,相同則將 this.value 置爲空,則能夠達到取消的效果。
通過實踐,該方法是可行的。 值得注意的是這種狀況下須要取消RadioGroup的on-change事件監聽,不然radio改變了this.value,會觸發RadioGroup的on-change事件,致使沒法取消。 還有一個弊端是 須要給 Radio增長一個包裹元素,可能還須要對元素寫個樣式,不要影響到原Radio 元素佈局, 那麼還有沒有更好的方法呢?ui

2.2.2 Radio更好的綁定click的方式

vue中提供了監聽原生事件的修飾符,在jsx形式的vue監聽原生事件的寫法以下:

<Radio
    nativeOnClick={this.handleRadioClick.bind(this, value)}
    label={value}
    disabled={disabled}
    >
    <span>{label}</span>
</Radio>

因此咱們不用增長元素也能監聽到click事件,接下里就能夠在響應函數中 處理取消/選中的邏輯了。

2.2.3 Radio 怎麼響應了2次?

嗯, 終於找到一個很好的綁定 click的方法,然而測試的時候,卻發現 點擊一次卻響應了 兩次點擊函數,百思不得其解嗎? 這時候 baidu 或者google給了咱們答案: 一個文章說的特別好,解釋了兩次的緣由:
以下文章連接:
觸發的事件源分別爲input和label;

觸發條件很簡單:

一、監聽的是label和input的上層元素click事件

二、label和input關聯(for或者input在label下)

問題緣由::

點擊label的時候,事件冒泡一次,同時會觸發關聯的input的click事件,致使事件再次冒泡。

解決辦法:
方法有很中拉,

  1. 2次響應由 label形成,那咱們不要label了,這對於使用iview的狀況下是不可能的,由於iview就是使用 label 和 input 作的
  2. 響應函數裏判斷event的 tagName ,若是 tagName 爲 label則不作處理,這中方法問題不少,要作 event的瀏覽器兼容,還有判斷label有沒有關聯 input等,稍顯複雜
  3. 根據經過事件觸發的時間戳來判斷,相隔太近則認爲是一次點擊。

2.3 Radio/RadioGroup 可取消完整方案

有了上面的分析,下面咱們完整是總結一下 讓radio能夠取消的步驟:
注意: 我下面寫的是 vue的jsx 形式,因此若是是vue形式,請自行修改。
首先: template

<RadioGroup
    onOn-change={this.sync.bind(this)}
    value={this.value}
    type={this.mode}
    size={this.size}
    style={this.css}
    class={this.theme}
>
    <Radio
        nativeOnClick={this.handleRadioClick.bind(this, value)}
        label={value}
        disabled={disabled}
    >
        <span>{label}</span>
    </Radio>
    <Radio
        nativeOnClick={this.handleRadioClick.bind(this, value)}
        label={value}
        disabled={disabled}
    >
        <span>{label}</span>
    </Radio>
</RadioGroup>

data 和 methods:

data() {
    return {
        value: ''
    }
}
methods: {
    handleRadioClick (value) {
        let now = +new Date();
        if (now- this.evTimeStamp < 100) {
            return;
        }
        this.evTimeStamp = now;
        value = this.value === value ? '' : value;
        this.update(value);  // 能夠理解爲vuex 通知更新  this.value
    },
}

限於自己實現還有其餘關聯部分,這裏沒有加上 更新 this.value具體代碼,可是我相信看到這裏應該已經知在使用 ui 庫的狀況如何 是 radio 能夠取消了。

遇到問題歡迎評論提問,不足之處也歡迎指正。

相關文章
相關標籤/搜索