ES6的に選択したラジオボタンのチェックを外す方法

# コンパクトにできたので紹介

## 内容
* ES6的、選択ラジオボタンのチェック外し
    * アロー関数
    * class
    * static, get
    * const
    * Array.from()
* ES6でなくても、クロージャ使えばできる
    * 関数型に慣れてる人はそちらでもどうぞ
    * クロージャがクラス定義と実質的に同等(!?)
    * でも、唯一性が担保されない    
* シングルトンまですると楽

---
# 実際
```html
<!-- name属性でグループ化されているので -->
<input type="radio" id="A" name="group0">
<input type="radio" id="B" name="group0">
<input type="radio" id="C" name="group0">
```

```javascript
class OffRadio {
  constructor(){
    this.onclick = (evt)=>{
      const sender = evt.target;
      const name = sender.name;
      if (this[name] === sender) { // 具体的には、Aがチェック済みならthis.group0 = Aのノード
        this[name] = null;
        sender.checked = false     // クリックイベントが発生したチェックボックスのチェックを外す
      } else {
        this[name] = sender;       // 例えばBをチェックしたらthis.group0 = Bのノード
      }
    }
  }
  static get onclick(){ // クラス定数でシングルトン
    if (!OffRadio.instance){
      OffRadio.instance = new OffRadio()
    }
    return OffRadio.instance.onclick
  }
  static attach(groupName=document.querySelector('input[type="radio"]')){
    Array.from(document.getElementsByName(groupName)).forEach((e)=>{
      e.addEventListener('click', OffRadio.onclick); //イベントを追加  
    })
  }
}

// 使い方
// OffRadioはプログラム中でインスタンス一つだけで良い
// 静的にHTMLに書き下してある要素には
OffRadio.attach()
// 動的に作った要素には
OffRadio.attach('group0')
```

---
# まれによくある課題

* チェックを外したいけど外れない

```javascript
function offRadio(evt){
  if (this.checked == true) { 
      this.checked = false);
    // クリックして呼び出された時点でcheckedプロパティはtrue
    // 従って、クリックしてもチェックされた状態にならない
    // チェックされたけどイベントハンドラ中で常に解除してる状態
  }
}
```

---
# チェック済を保持する必要あり

* jQuery的解法
    * [選択済みのラジオボタンを押すと選択が解除される](https://teratail.com/questions/72228)
* これだけのためにjQuery?

>```html
>
><script type="text/javascript">
>    // ラジオボタングループ(name)ごとにプロパティを定義、初期化しておく
>    var checkedRadioValue = { 'hogehoge': '', 'foo': '' };
>
>    $('input[type="radio"]').on('click', function() {
>        var name = $(this).attr('name');
>        var value = $(this).val();
>
>        if (checkedRadioValue[name] === value) {
>            checkedRadioValue[name] = '';
>            $(this).prop('checked', false);
>        } else {
>            checkedRadioValue[name] = value;
>        }
>    });
></script>
>```

---
# クロージャにすればいいんちゃう

```javascript
function offRadio(){
  var checkedRadioValue = {}
  return function(evt){
    var name = this.name;    
    if (checkedRadioValue[name] === this.id) {
        checkedRadioValue[name] = '';
          this.checked = false
    } else {
        checkedRadioValue[name] = this.id;
    }    
  }
}

// 使い方
const off_radio = offRadio();
Array.prototype.filter.call(document.getElementsByTagName("input"), function(e){
  return e.type == "radio"
}).forEach(function(e){
  e.addEventListener('click', off_radio)
})
```
---
# なんかうまくない

* しかし、オブジェクトそのものが{}よな...
    * => クラス化すると**宣言不要になる**
* クロージャだけでは、引数で持ち回わるハメに
    * 複数のインスタンス必要なし
    * => シングルトン化

---
# ということでクラス化、ところが

* thisが衝突するやんけ
    * インスタンスのthis=チェック済状態の保持
    * これまでのthis=イベント発生元
        * ということはevt.targetやな
* シングルトン化はクラス定数で
    * なお、使用する時点まで、newを遅延(getter)
    * freezeしてもいいかもしれないね
    
---
# 最終
```javascript
class OffRadio {
  constructor(){
    this.onclick = (evt)=>{
      const sender = evt.target;
      const name = sender.name;
      if (this[name] === sender) { // 具体的には、Aがチェック済みならthis.group0 = Aのノード
        this[name] = null;
        sender.checked = false     // クリックイベントが発生したチェックボックスのチェックを外す
      } else {
        this[name] = sender;       // 例えばBをチェックしたらthis.group0 = Bのノード
      }
    }
  }
  static get onclick(){ // クラス定数でシングルトン
    if (!OffRadio.instance){
      OffRadio.instance = new OffRadio()
    }
    return OffRadio.instance.onclick
  }
  static attach(groupName=document.querySelector('input[type="radio"]')){
    Array.from(document.getElementsByName(groupName)).forEach((e)=>{
      e.addEventListener('click', OffRadio.onclick); //イベントを追加  
    })
  }
}
```
---
#まとめ

* ES6的、選択ラジオボタンのチェック外し
    * アロー関数
    * class
    * static, get
    * const
    * Array.from()
* ES6でなくても、クロージャ使えばできる
    * 関数型に慣れてる人はそちらでもどうぞ
    * クロージャがクラス定義と実質的に同等(!?)
    * でも、唯一性が担保されない    
* シングルトンまですると楽
        
* 参照情報は本文中に