LoginSignup
1
1

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-10-13

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

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

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

_1.jpg
_2.jpg
_3.jpg
_4.jpg
end.jpg

説明

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

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

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

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

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 ソース上は下記のように残っています。)

<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 要素だけを表示させます。

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

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

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

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

また、両者を組み合わせれば、進捗バーの後に続けて文字列を表示させることもできます。
両者の組合せ

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 要素の開始タグと終了タグに挟まれた部分は、進捗バーを表示できない環境で表示されるそうです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1