サーバー処理の進捗バーを、（Javascript を使わず）progress 要素を使って表示する

## 常に最新の進捗バーだけを表示させる

Javascript を使わずに、サーバーからプッシュ型で進捗バーをブラウザに送信します。（この記事では、サーバー側のスクリプト言語は PHP を使っていますが、他の言語でも同様にできるはずです。）

下記のように、進捗バーが更新されていき、最新の進捗バーだけが表示されます。（画像は、Windows10, Firefox）

![_1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327025/68f43d6e-048a-a042-0af7-3f8250096189.jpeg "進捗20%")
![_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327025/107fa4fc-e656-f7df-fdca-5c05c9b408e1.jpeg "進捗40%")
![_3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327025/23b3b12a-9fab-0765-848e-0403079ba1ef.jpeg "進捗60%")
![_4.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327025/b3e13d61-d53d-feab-fc2c-5efc52515708.jpeg "進捗80%")
![end.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327025/e7b2a861-d82e-49fb-836a-39378ad74991.jpeg "進捗100%")

## 説明

PHP に限らず、時間のかかる大量のデータ処理を行う場合、進捗バーが表示できれば便利です。

最初、HTML5 の progress 要素を利用することを思いつきましたが、最新の進捗バーを自動更新する方法がなかなか見つかりませんでした。ネット検索では、iframe 要素とか、Javascript とか、面倒そうなものばかり。

「■」を表示させる方法が見つかりましたが、総数が大きな素数とか、又はとても小さな数の場合、進捗を表す「■」をどのタイミングでいくつ追加するかがとても煩雑です。

ところが、CSS3 を使えば、古い進捗バーを {display:none} で消してしまうことができます。この方法で、（過去の余分な progress 要素が残ってしまいますが、）
最新の進捗バーだけをリアルタイム表示させることができます。

```PHP:index.php
<?php
# データ処理すべき総数（ここでは 100 とします。）
$total = 100;
# 処理が済んだ件数は、$i とします。このコードでは下の for ループで 1 ずつカウントアップされます。

# ブラウザに style 要素と p 要素を送信する
echo <<<HTML
<style>
p progress:not(:last-of-type) {display:none}
</style>
<p>
HTML;

for ( $i = 1; $i <= $total; $i++ ) {
    if ($i % ($total / 5) == 0) { // 20 %ごとに進捗バーを更新する
        sleep(1);
        # ブラウザに progress 要素を送信する（親要素である p 要素に progress 要素を追加していく）
        echo '<progress value="'.$i.'" max="'.$total.'">'.($i / $total * 100).'%</progress>';
        if ($i===$total){ // 100% なら半角空白100% を追加
        	echo ' 100%';
        }
        ob_flush();
        flush();
    }
}
```

このコードでは、整数をカウントアップしていき、20 カウント(20%)ごとに 1 秒待って進捗バーを表示させます。

なお、古い進捗バー（progress 要素）は、最初に echo された style 要素の設定で非表示になり、常に最新の進捗バーだけが表示されることによって、あたかも自動更新されているように見えます。
（実際には古い進捗バーは、非表示にされていますが、HTML ソース上は下記のように残っています。）

```html
<style>p progress:not(:last-of-type) {display:none}</style>
<p><progress value="20" max="100">20%</progress><progress value="40" max="100">40%</progress><progress value="60" max="100">60%</progress><progress value="80" max="100">80%</progress><progress value="100" max="100">100%</progress> 100%
```

このコードの「ミソ」は、HTML5 の仕様「p 要素の終了タグは省略できる！」を利用して、p 要素の終了タグ（`</p>`）を書かないまま、progress 要素をどんどん p 要素に追加していくところです。ブラウザが自動的に p 要素の終了タグ（`</p>`）を補完して表示してくれます。

そして、style 要素で最新の progress 要素だけを表示させます。
```PHP
<style>
p progress:not(:last-of-type) {display:none}
</style>
```

この方法を使えば、例えば進捗バーでなくて「文字列」で「○○%」と表示させることもできます。この場合、progress 要素の代わりに、span 要素や i 要素、u 要素などを使います。

```PHP
<style>
p span:not(:last-of-type) {display:none}
</style>

echo '<span>'.floor($i / $total * 100).'%</span>';
```

また、両者を組み合わせれば、進捗バーの後に続けて文字列を表示させることもできます。
![両者の組合せ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/327025/b3a5127a-a12a-a099-ab32-49cb49172705.jpeg "両者の組合せ")

```PHP
echo <<<HTML
<style>
p progress:not(:last-of-type),
p span:not(:last-of-type) {display:none}
</style>
<p>
HTML;

# 下記は、ループ処理の中などで繰り返し実行する
echo '<progress value="'.$num_of_files.'" max="'.$total_files.'">'.($num_of_files / $total_files * 100).'%</progress><span> '.(time() - $time0).'秒： '.$num_of_files.' ファイル（'.number_format($total_file_size).'バイト）</span>';
ob_flush();
flush();
```

ちなみに、progress 要素の開始タグと終了タグに挟まれた部分は、進捗バーを表示できない環境で表示されるそうです。