【问题标题】:Update HTML progress bar within JavaScript double for loop?在 JavaScript 双循环中更新 HTML 进度条?
【发布时间】:2018-04-19 18:55:48
【问题描述】:

我有一个 Html 输入文件,可以上传图片,并使用 ajax 沙子到 asp.net MVC 控制器,一切正常。

但我无法更新简单的进度条。

这是简单的Html,

<input type="file" multiple="multiple" id="file_upload" />
<br />
<input type="button" id="upload" name="upload" value="Upload" class="btn btn-primary btn-block" />
<br />
<div class="outter">
    <div class="inner" id="progress"></div>
</div>
<br />

在我的 javascript 中,我将请求拆分为一次上传 10 张图片,如下所示,

<script>
var counter = 0;

$("#upload").click(function () {
    var fileUpload = $("#file_upload").get(0);
    var files = fileUpload.files;
    var devide = devider(files.length);

    for (var x = 0; x < devide; x++) {
        var fileData = new FormData();
        for (var i = 0; i < files.length / devide; i++) {
            if (counter < files.length) {
                fileData.append(files[counter].name, files[counter]);
                counter++;                  
            }
        }

        $.ajax({
            type: "POST",
            url: "/Upload/GetFiles",
            data: fileData,
            cache: false,
            contentType: false,
            processData: false,
            async: false,
            success: function (result) {
                console.log(result);
            },
            error: function (xhr, status, error) { console.log('Error:' + error); },
            timeout: 0
        });
    }
});

function devider(number) {
    var count = 0;
    for (var i = 0; i < number; i+=10) {
        count++;
    }
    return count;
}
</script>

我只需要在每次上传文件时更新&lt;div id="progress"&gt; 的宽度,这可能意味着在第二个 for 循环中。 但我不能,因为它处于循环中,它没有更新 HTML?

附言我知道 setInterval 但我不知道如何在这种情况下实现它!?

【问题讨论】:

  • async: false, 是它似乎没有更新 html 的原因。它可能正在更新 html,但由于您正在使用同步 ajax 请求锁定浏览器,因此在所有 ajax 完成之前它不会呈现更改。
  • 重做逻辑,这样就不会有问题了。进度条不能用于同步代码。
  • @KevinB 即使async: true, 在循环完成之前它也不会更新 HTML :( 我在下面提到我认为 for 循环正在阻塞页面。
  • 是的,for循环也必须去。

标签: javascript html ajax


【解决方案1】:

这个问题的答案可能是,您不能在 JavaScript for 循环中更新任何 HTML!

我不知道为什么没有更多地介绍它,但我尝试了所有可能的方法,我在 Chrome 和 Fiddle 中尝试过,页面上的 HTML 在循环完成执行之前不会更新,如果您从循环本身尝试,或者您调用外部函数,再次在循环执行完成之前,HTML 不会重绘。

然后我找到了this post,这有点解释了

这是一个报价,

JavaScript 执行和页面渲染在同一个执行中完成 线程,这意味着当您的代码正在执行时,浏览器将 不是重绘页面。 (虽然即使它正在重绘页面 对于 for 循环的每次迭代,它都会如此之快,以至于你 真的没有时间查看各个数字。)

所以我不得不放弃 for 循环来更新 HTML,这就是我的 JavaScript 现在的样子。

<script>

    var counter = 0;
    var index = 0;
    var fileUpload;
    var files;
    var devide;
    var add_width;
    var update_width = 0;
    var fileData;
    var interval;

    document.getElementById("file_upload").onchange = function () {
        fileUpload = $("#file_upload").get(0);
        files = fileUpload.files;
        devide = devider(files.length);
        add_width = 100 / devide;
        $("#progress").css({ "width": "0%", "max-width": "100%" });
    }

    $("#upload").click(function () {
        if (fileUpload) {
            $("#outter").css({ "display": "inline-block" });
            prepareUpload();
        }        
    })

    function prepareUpload() {

        fileData = new FormData();
        for (var i = 0; i < 10; i++) {
            if (counter < files.length) {
                fileData.append(files[counter].name, files[counter]);
                counter++;
            }      
        }
        update_width += add_width;
        document.getElementById("progress").style.width = update_width + "%";
        document.getElementById("text").innerHTML = Math.round(update_width) + " % Completed";
        interval = setInterval(upload, 90);  
    }

    function upload() {
        if (index < devide) {
            $.ajax({
                type: "POST",
                url: "/Upload/GetFiles",
                data: fileData,
                cache: false,
                contentType: false,
                processData: false,
                async: false,
                complete: function () {
                },
                success: function (result) {
                    if (result < files.length) {
                        prepareUpload();
                    } else {
                        clearInterval(interval);
                    }
                },
                error: function (xhr, status, error) { console.log('Error:' + error); },
                timeout: 0
            });
        } else {
            clearInterval(interval);
        }
        index++;       
    }

    function devider(number) {
        var count = 0;
        for (var i = 0; i < number; i += 10) {
            count++;
        }
        return count;
    }

</script>

【讨论】:

  • 我不知道这个!哇!
【解决方案2】:

我会使用already nicely made progressbar。但这里是您问题的答案

编辑:为你添加了一个JSFiddle,带有一个虚拟进度条:https://jsfiddle.net/strauman/wy7w015e/

您说您希望在文件上传成功后更新进度条。所以让我们创建一个函数prepare_progressbar 来准备步数,并创建一个函数update_progressbar 来更新它。

首先,我们创建变量以包含我们在两个函数中都需要的值:

var width_per_file;
var number_of_files_completed;
var number_of_files_to_upload=0;

现在我们通过检查上传了多少文件并初始化变量来“准备”它。

function prepare_progressbar(number_of_files){
  var max_progressbar_width=$(window).width();
  // Reset the completed-counter
  number_of_files_completed=0;
  // How much should it increase per file completed?
  width_per_file=$(window).width()/number_of_files;
  // Set the global number of files
  number_of_files_to_upload=number_of_files;
  // Make progress bar "hidden"
  $("#progress").css({"max-width": "0px"});
}

现在,我们要创建一个更新进度条的函数。每次 ajax 完成一个请求时,我们希望它调用这个函数:

function update_progressbar(){
  number_of_files_completed+=1;
  var new_progressbar_width=number_of_files_completed*width_per_file;
  $("#progress").css({"width":new_progressbar_width+"px", "max-width":new_progressbar_width+"px"});
}

现在剩下的唯一事情就是将这些函数插入代码中的适当位置。这一切都像这样结合在一起:

var width_per_file;
var number_of_files_completed;
var number_of_files_to_upload=0;

function prepare_progressbar(number_of_files){
  var max_progressbar_width=$(window).width();
  // Reset the completed-counter
  number_of_files_completed=0;
  // How much should it increase per file completed?
  width_per_file=$(window).width()/number_of_files;
  // Set the global number of files
  number_of_files_to_upload=number_of_files;
  // Make progress bar "hidden"
  $("#progress").css({"max-width": "0px"});
}

function update_progressbar(){
  number_of_files_completed+=1;
  var new_progressbar_width=number_of_files_completed*width_per_file;
  $("#progress").css({"width":new_progressbar_width+"px", "max-width":new_progressbar_width+"px"});
}

var counter = 0;

$("#upload").click(function () {
    var fileUpload = $("#file_upload").get(0);
    var files = fileUpload.files;
    var devide = devider(files.length);
    prepare_progressbar(devide);
    for (var x = 0; x < devide; x++) {
        var fileData = new FormData();
        for (var i = 0; i < files.length / devide; i++) {
            if (counter < files.length) {
                fileData.append(files[counter].name, files[counter]);
                counter++;
            }
        }

        $.ajax({
            type: "POST",
            url: "/Upload/GetFiles",
            data: fileData,
            cache: false,
            contentType: false,
            processData: false,
            async: false,
            // On complete, it will call our update_progressbar
            complete: update_progressbar,
            success: function (result) {
                console.log(result);
            },
            error: function (xhr, status, error) { console.log('Error:' + error); },
            timeout: 0
        });
    }
});

function devider(number) {
    var count = 0;
    for (var i = 0; i < number; i+=10) {
        count++;
    }
    return count;
}

【讨论】:

  • 谢谢!我现在正在尝试您的代码。但我不明白为什么当你调用这个函数prepare_progressbar(devide); 时你会传入devide?这不是文件的数量,为什么不是files.length!?
  • 对不起,我的坏了,现在明白了。它只更新每个块而不是每个文件。
  • @scg 如果满意,别忘了点赞和/或接受答案!
  • 因为您从 ajax 调用中调用 update 函数,它只运行与 devide 变量相同的数量,所以第一个版本更有意义。这是我的错误。除非您从第二个循环运行更新功能...
  • 又更新了:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-09-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多