bootstrap-selectで作ったセレクトボックスを活性化できないときの対応方法

- 環境
    - CentOS Linux release 7.8.2003 (Core)
    - openjdk version "11.0.7" 2020-04-14 LTS
    - JSF 2.3.9
    - jquery 3.2.1
    - bootstrap 4.3.1
    - bootstrap-select 1.13.14

# 事象 : bootstrap-selectで作ったセレクトボックスを活性化できない

1. 初期表示時にセレクトボックスを非活性にする
1. チェックボックスを変更すると`onchange`イベントでセレクトボックスの活性非活性を切り替える

はずが活性化しない・・・・
![a.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/09dac3cf-d89e-3516-3048-b81f823c8e64.gif)

```xml:XHTML
<!--省略-->
<h:form id="formId">
<h:panelGroup>
  <ui:remove>[使う]チェックボックス</ui:remove>
  <h:selectBooleanCheckbox id="chekBox" value="#{sampleBean.chekBoxValue}" onchange="changeDisabled();" />
  <h:outputLabel value="使う" for="chekBox" />
</h:panelGroup>
<h:panelGroup>
  <ui:remove>セレクトボックス</ui:remove>
  <h:selectOneMenu id="select" styleClass="selectpicker" value="#{sampleBean.selected}">
    <f:selectItems value="#{sampleBean.selectItems}" />
  </h:selectOneMenu>
</h:panelGroup>
</h:form>
<!--省略-->
```

```js:JavaScript
$(document).ready(function(){
    changeDisabled();
});

/** セレクトボックスの活性非活性を切り替える. */
function changeDisabled() {
    /** @type {boolean} チェックボックスの選択状態. */
    var checked = $("#formId\\:chekBox").prop("checked");
    if (checked) {
        // 活性化する
        $("#formId\\:select").removeAttr('disabled');
        $("#formId\\:select").next().children().removeClass('disabled');
    } else {
        // 非活性にする
        $("#formId\\:select").attr('disabled', 'disabled');
        $("#formId\\:select").next().children().addClass('disabled');
    }
}
```

```java:SampleBean
// 省略
public class SampleBean implements Serializable {
    /** serialVersionUID. */
    private static final long serialVersionUID = -6782548672735889274L;
    /** セレクトボックスの選択肢. */
    @Getter
    private List<SelectItem> selectItems;
    /** セレクトボックスで選択した値. */
    @Getter
    @Setter
    private Integer selected;
    @Getter
    @Setter
    private Boolean chekBoxValue = false;

    /** Beanの初期化処理. */
    @PostConstruct
    public void init() {
        setSelectItems();
    }

    /** 選択肢を設定する. */
    private void setSelectItems() {
        selectItems = new ArrayList<SelectItem>();
        selectItems.add(new SelectItem(0, "   "));
        selectItems.add(new SelectItem(1, "いぬ"));
        selectItems.add(new SelectItem(2, "ねこ"));
        selectItems.add(new SelectItem(3, "さる"));
        selectItems.add(new SelectItem(4, "ぽんすけ"));
    }
}
```

# 原因 : 初期表示時に非活性にする処理を実行するタイミングが`ready`だから
JavaScriptをデバックしながらHTMLの出力を見ているとbootstrap-selectのHTMLが構築されるのは`onload`っぽかったのでいろいろ試してみた。
試してみると初期表示の非活性処理は同じでも、タイミングが`ready`と`onload`でdisabledが付く場所に違いがあった。
また、バージョンが異なると出力されるHTMLが変わるからかdisabledが付く場所も変わった。

```js:非活性処理
// selectタグにdisabled属性をつける
$("#formId\\:select").attr('disabled', 'disabled');
// selectの隣のタグの子供タグにclass属性のdisabbledをつける
$("#formId\\:select").next().children().addClass('disabled');
```

凡例) ★ : 出力されたHTMLで「selectの隣のタグの子供タグ」になるタグ

|bootstrap-select|1.13.14|1.13.14|1.6.3|1.6.3|
|:-:|:-:|:-:|:-:|:-:|
|disableするタイミング|ready|onload|ready|onload|
|disabled属性が付く|select|select|select|select|
|class属性にdisabbledがつく|一番上のdiv,<br>button|button直下のdiv★|button★,<br>li|button★,<br>buttonの隣のdiv|
|aria-disabled属性が付く|button|なし|なし|なし|

今回JavaScriptで活性非活性を切り替える際に行っている処理の場合、`onload`でdisabledが付く場所にしか対応していなかった

```js:活性化処理
$("#formId\\:select").removeAttr('disabled');
$("#formId\\:select").next().children().removeClass('disabled');
```

## bootstrap-select 1.13.14
- bootstrap 4.3.1
- bootstrap-select 1.13.14

### 非活性化しない場合に出力されるHTML
```html
<div class="dropdown bootstrap-select">
  <select id="formId:select" name="formId:select" class="selectpicker" size="1" tabindex="-98">
    <option value="0">   </option>
    <option value="1">いぬ</option>
    <option value="2">ねこ</option>
    <option value="3">さる</option>
    <option value="4">ぽんすけ</option>
  </select>
  <button type="button" class="btn dropdown-toggle btn-light" data-toggle="dropdown" role="combobox" aria-owns="bs-select-1" aria-haspopup="listbox" aria-expanded="false" data-id="formId:select" title="Nothing selected">
    <div class="filter-option"><div class="filter-option-inner"><div class="filter-option-inner-inner">Nothing selected</div></div></div>
  </button>
  <div class="dropdown-menu ">
    <div class="inner show" role="listbox" id="bs-select-1" tabindex="-1">
      <ul class="dropdown-menu inner show" role="presentation"></ul>
    </div>
  </div>
</div>
```

### readyで非活性化した場合に出力されるHTML

```html
<div class="dropdown bootstrap-select disabled">
  <select id="formId:select" name="formId:select" class="selectpicker" size="1" disabled="disabled" tabindex="-98">
<!-- 非活性化しない場合と同じなので省略 -->
  <button type="button" class="btn dropdown-toggle disabled btn-light" data-toggle="dropdown" role="combobox" aria-owns="bs-select-1" aria-haspopup="listbox" aria-expanded="false" data-id="formId:select" tabindex="-1" aria-disabled="true" title="Nothing selected">
<!-- 非活性化しない場合と同じなので省略 -->
```

### onloadで非活性化した場合に出力されるHTML
```html
<!-- 非活性化しない場合と同じなので省略 -->
  <select id="formId:select" name="formId:select" class="selectpicker" size="1" tabindex="-98" disabled="disabled">
<!-- 非活性化しない場合と同じなので省略 -->
    <div class="filter-option disabled"><div class="filter-option-inner"><div class="filter-option-inner-inner">Nothing selected</div></div></div>
<!-- 非活性化しない場合と同じなので省略 -->
```

## bootstrap-select 1.6.3
- bootstrap 3.3.0
- bootstrap-select 1.6.3

### 非活性化しない場合に出力されるHTML
```html
<select id="formId:select" name="formId:select" class="selectpicker" size="1" style="display: none;">
  <option value="0">   </option>
  <option value="1">いぬ</option>
  <option value="2">ねこ</option>
  <option value="3">さる</option>
  <option value="4">ぽんすけ</option>
</select>
<div class="btn-group bootstrap-select">
  <button type="button" class="btn dropdown-toggle selectpicker btn-default" data-toggle="dropdown" data-id="formId:select" title="   ">
    <span class="filter-option pull-left">   </span>&nbsp;<span class="caret"></span>
  </button>
  <div class="dropdown-menu open">
    <ul class="dropdown-menu inner selectpicker" role="menu">
      <li data-original-index="0" class="selected"><a tabindex="0" class="" data-normalized-text="<span class=&quot;text&quot;>   </span>"><span class="text">   </span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
      <li data-original-index="1"><a tabindex="0" class="" data-normalized-text="<span class=&quot;text&quot;>いぬ</span>"><span class="text">いぬ</span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
      <li data-original-index="2"><a tabindex="0" class="" data-normalized-text="<span class=&quot;text&quot;>ねこ</span>"><span class="text">ねこ</span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
      <li data-original-index="3"><a tabindex="0" class="" data-normalized-text="<span class=&quot;text&quot;>さる</span>"><span class="text">さる</span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
      <li data-original-index="4"><a tabindex="0" class="" data-normalized-text="<span class=&quot;text&quot;>ぽんすけ</span>"><span class="text">ぽんすけ</span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
    </ul>
  </div>
</div>
```

### readyで非活性化した場合に出力されるHTML
```html
<select id="formId:select" name="formId:select" class="selectpicker" size="1" disabled="disabled" style="display: none;">
<!-- 非活性化しない場合と同じなので省略 -->
  <button type="button" class="btn dropdown-toggle selectpicker disabled btn-default" data-toggle="dropdown" data-id="formId:select" tabindex="-1" title="   ">
<!-- 非活性化しない場合と同じなので省略 -->
      <li data-original-index="0" class="disabled selected"><a tabindex="-1" class="" data-normalized-text="<span class=&quot;text&quot;>   </span>" href="#"><span class="text">   </span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
      <li data-original-index="1" class="disabled"><a tabindex="-1" class="" data-normalized-text="<span class=&quot;text&quot;>いぬ</span>" href="#"><span class="text">いぬ</span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
      <li data-original-index="2" class="disabled"><a tabindex="-1" class="" data-normalized-text="<span class=&quot;text&quot;>ねこ</span>" href="#"><span class="text">ねこ</span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
      <li data-original-index="3" class="disabled"><a tabindex="-1" class="" data-normalized-text="<span class=&quot;text&quot;>さる</span>" href="#"><span class="text">さる</span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
      <li data-original-index="4" class="disabled"><a tabindex="-1" class="" data-normalized-text="<span class=&quot;text&quot;>ぽんすけ</span>" href="#"><span class="text">ぽんすけ</span><span class="glyphicon glyphicon-ok check-mark"></span></a></li>
<!-- 非活性化しない場合と同じなので省略 -->
```

### onloadで非活性化した場合に出力されるHTML
```html
<select id="formId:select" name="formId:select" class="selectpicker" size="1" disabled="disabled" style="display: none;">
<!-- 非活性化しない場合と同じなので省略 -->
  <button type="button" class="btn dropdown-toggle selectpicker btn-default disabled" data-toggle="dropdown" data-id="formId:select" title="   ">
<!-- 非活性化しない場合と同じなので省略 -->
  <div class="dropdown-menu open disabled">
<!-- 非活性化しない場合と同じなので省略 -->
```

# 対応方法 : 初期表示時に非活性にする処理を`onload`でやる
![a.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/159761/8d82771b-7544-321b-95af-949a740e7110.gif)

```js
$(window).on('load', function(){
    changeDisabled();
});
// 省略
```