Bootstrapのファイルをアップロードするボタンを標準デザインのボタンにする

2024年版を書きました

https://qiita.com/yo-naka/items/f3e43fe7202a8916eb32


---

bootstrapを使ってファイルをアップロードするフォームをつくるとき、デフォルトのボタンは小さくて、特にスマホでは表示する際に使いにくいと感じます

[こちらの投稿](http://qiita.com/QUANON/items/dbcd694a3b66eceb3023)で[Bootstrap File Input](http://plugins.krajee.com/file-input)を使った方法が紹介されています。見た目だけでなく、機能も豊富で参考になります。

**ただ今回はbootstrap標準デザインのボタンを使う方法を紹介します。**

[こちら](https://www.abeautifulsite.net/whipping-file-inputs-into-shape-with-bootstrap-3)を参考にしました。

---

### 標準の\<input type＝"file"\>

まず標準の\<input type＝"file"\>の確認です

```html
<input type="file">
```

<img width="203" alt="Screen Shot 2016-12-04 at 9.09.52 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/79562/692c5ada-c161-ddd2-609b-a307bd6e3dc1.png">

これです。

---

### \<span class="btn"\>で囲んでボタンにする

spanタグで囲むことでbootstrap標準デザインのボタンにできます。inputは`style="display:none"`で隠します。

```html
<span class="btn btn-primary">
    Choose File
    <input type="file" style="display:none">
</span>
```

<img width="188" alt="Screen Shot 2016-12-04 at 9.40.15 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/79562/3ada3bd9-f645-1f7b-b936-2aafd5e66521.png">


このようになります。

ただ、これではクリックしてもファイルを選択できません（Google Chromeで試しました）

さらにlabelタグで囲むと選択できるようになります。コードは下記のようになります。

```html
<label>
    <span class="btn btn-primary">
        Choose File
        <input type="file" style="display:none">
    </span>
</label>
```

---

### ファイル名を表示する

text-inputを使ってファイル名を表示します。下記のコードをみてください。

```html
<div class="input-group">
    <label class="input-group-btn">
        <span class="btn btn-primary">
            Choose File<input type="file" style="display:none">
        </span>
    </label>
    <input type="text" class="form-control" readonly="">
</div>
```

<img width="307" alt="Screen Shot 2016-12-04 at 10.57.20 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/79562/136410cc-14ee-384b-1874-35277b241b2d.png">

まだファイルを選んでも何も表示されません。jqueryを使ってファイル名を表示できるようにします

```js
$(document).on('change', ':file', function() {
    var input = $(this),
    numFiles = input.get(0).files ? input.get(0).files.length : 1,
    label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
    input.parent().parent().next(':text').val(label);
});
```

---

### おまけ：プレビューを表示する

画像を選択した際にプレビュー表示します。[こちら](http://www.phpgang.com/how-to-show-image-thumbnail-before-upload-with-jquery_573.html)を参考にしました

プレビューを表示するためのdivタグを追加します

```html
<div class="imagePreview"></div>
<div class="input-group">
    <label class="input-group-btn">
        <span class="btn btn-primary">
            Choose File<input type="file" style="display:none" class="uploadFile">
        </span>
    </label>
    <input type="text" class="form-control" readonly="">
</div>
```

```css
.imagePreview {
    width: 100%;
    height: 180px;
    background-position: center center;
    background-size: cover;
    -webkit-box-shadow: 0 0 1px 1px rgba(0, 0, 0, .3);
    display: inline-block;
}
```

<img width="298" alt="Screen Shot 2016-12-04 at 10.36.10 PM.png" src="https://qiita-image-store.s3.amazonaws.com/0/79562/d3398792-ced6-00a9-8f70-e663be0163d8.png">

こうなります。

FileReaderを使って画像を読み込み、divの背景に表示します

```js
$(document).on('change', ':file', function() {
    var input = $(this),
    numFiles = input.get(0).files ? input.get(0).files.length : 1,
    label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
    input.parent().parent().next(':text').val(label);

    var files = !!this.files ? this.files : [];
    if (!files.length || !window.FileReader) return; // no file selected, or no FileReader support
    if (/^image/.test( files[0].type)){ // only image file
        var reader = new FileReader(); // instance of the FileReader
        reader.readAsDataURL(files[0]); // read the local file
        reader.onloadend = function(){ // set image data as background of div
            input.parent().parent().parent().prev('.imagePreview').css("background-image", "url("+this.result+")");
        }
    }
});
```

後半を追記しています。

---

### サンプル

html全体は下記です。試してみてください

```html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<style>
    .imagePreview {
        width: 100%;
        height: 180px;
        background-position: center center;
        background-size: cover;
        -webkit-box-shadow: 0 0 1px 1px rgba(0, 0, 0, .3);
        display: inline-block;
    }
</style>
    </head>
    <body>
        <div class="container page-header">
            <div class="col-sm-4">
                <form action="" method="post" enctype="multipart/form-data">
                    <div class="imagePreview"></div>
                    <div class="input-group">
                        <label class="input-group-btn">
                            <span class="btn btn-primary">
                                Choose File<input type="file" style="display:none" class="uploadFile">
                            </span>
                        </label>
                        <input type="text" class="form-control" readonly="">
                    </div>
                </form>
            </div>
        </div>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
        <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
        <script>
        $(document).on('change', ':file', function() {
            var input = $(this),
            numFiles = input.get(0).files ? input.get(0).files.length : 1,
            label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
            input.parent().parent().next(':text').val(label);
        
            var files = !!this.files ? this.files : [];
            if (!files.length || !window.FileReader) return; // no file selected, or no FileReader support
            if (/^image/.test( files[0].type)){ // only image file
                var reader = new FileReader(); // instance of the FileReader
                reader.readAsDataURL(files[0]); // read the local file
                reader.onloadend = function(){ // set image data as background of div
                    input.parent().parent().parent().prev('.imagePreview').css("background-image", "url("+this.result+")");
                }
            }
        });
        </script>
    </body>
</html>
```

## 追記

最近のBootstrapではjqueryがデフォルトでは動作しないため、別の対策が必要です 

[ファイルアップロードのボタンに Bootstrap のデザインを適用する](https://tech.yo-naka.com/bootstrap/2024/07/05/bootstrap-input-file.html)