jQuery how to trigger event after form post returns?
Asked Answered
F

4

6

I have an expensive form action that builds a zip file on the server and returns it to the browser.

<form action='/download' method='post'>

<input type='submit' value='download'/>

</form>

I want to block the page on click of the button so that the user doesn't repeatably hit the button.

However I want to unblock the page after the form returns.

How can trigger an event on successful completion of the form?

(I know I can trigger this by changing the form to be an ajax submission but then the save file dialog does not appear...)

Any suggestions?

Thanks

Fix answered 23/11, 2012 at 13:4 Comment(8)
You could use this plugin malsup.com/jquery/form/#ajaxSubmit and do whatever you need on the "success" eventPeppy
You can trigger any set of functions in the callback for the jQuery Ajax function.Brisbane
Try: jsfiddle.net/gRoberts/Ca9nB - Really, all you need to do is disable the submit button once it's clicked to prevent it from being clicked again. When the page reloads, javascript will be undone and you can click again.Archaeology
@Gavin, the problem is that the page does not reload, the form action returns the zip file directly.Fix
@Jay Blanchard the problem then becomes how to handle the file download via ajaxFix
What do you mean by "how to handle the file download via Ajax"?Brisbane
@JayBlanchard Chris is using the form to initiate a file download. Using Ajax will simply create the zip file but not the download. The only other solution could be to use Ajax to create the zip and then return the path to that zip file as the response and then use Javascript to redirect?Archaeology
Thanks for clearing that up @Gavin. Your solution makes sense to me.Brisbane
A
5

One way you could handle this without using AJAX could be submitting the content of the form to an iframe element. If you attach an onsubmit function to the form that disables further submissions and attach an onload function to the iframe, you should be able to disable the user from submitting the form multiple times.

Example HTML:

<form action="/download" method="post" target="downloadFrame" onsubmit="return downloadFile();">
  <input type="submit" value="download" />
</form>
<iframe style="width: 0px; height: 0px;" scrolling="no" frameborder="0" border="0" name="downloadFrame" onload="downloadComplete();"></iframe>

Example Javascript:

var downloading = false;
function downloadFile() {
    var isDownloading = downloading;
    downloading = true;
    return !isDownloading;
}
function downloadComplete() {
    downloading = false;
}
Alphonso answered 23/11, 2012 at 13:7 Comment(4)
I believe onsubmit is triggered on submission, not on completion of the submit action.Fix
@Fix You are correct. This will handle further submissions from happening. I forgot that the page will not reload when a file download has happened. I will update my answer soon.Alphonso
This does work but is far from ideal, in certain error conditions my action will do a 301 redirect with a message, using a hidden iframe this is completely lost and to the user looks like nothing happened. Also there is no nice way that I can see to bind functions to the load event of an iframe, rather as you have done I need the iframe to call a function in the containing document, which means polluting the global namespace. So I think I will abandon this approach and do the download as a 2 step process. Thanks for the hack tho, hopefully someone will find it useful!Fix
@Fix Yes, this is far from ideal for a number of reasons, but is one of the only ways to do it as a complete 1 step process. If you don't want to use the inline function, you can always use jQuery to bind the load function to the iframe. Another approach might be to have the submission open to a new window using target="_blank" on the form.Alphonso
B
0

It appears no one has yet found a way to detect the post return in the browser itself, but there is another possibility using AJAX. It is a bit more involved though:

<script type="text/javascript">
    $(function () {
        $('#submitbtn').click (function () {
            window.setTimeout (dldone, 100);
            return true;
        });

        function dldone () {
            $.get ("/downloadstatus?rand="+$('#rand').val (), function (data) {
                if (data == 'done') {
                    // data generation finished, do something
                } else {
                   window.setTimeout (dldone, 100);
                }
            });
        }
    });
</script>
<form action="/generatedata" method="post">
    <input type="hidden" id="rand" value="[RANDOMVALUE]">
    <input type="submit" id="submitbtn" value="Download Data">
</form>

On the server, you would have to do some inter-process-communication to signal when the data generation is done. Since I already have a database, I did it like this:

public function downloadstatusAction () {
    if ($this->db->fetchOne ("SELECT rand FROM dlstatus WHERE rand = ?", (int) $_GET["rand"])) {
        $db->delete ("dlstatus", array ("rand = ?" => (int) $_GET["rand"]));
        print "done";
    } else {
        print "loading";
    }
}

public function generatedataAction () {
    // generate data
    $this->db->insert ("dlstatus", array ("rand" => (int) $_POST["rand"]));
    // output data
}

I am sure there are more elegant ways to do this, but you get the idea. This appears to work fine in all browsers I tested.

Bonnie answered 6/8, 2013 at 14:0 Comment(0)
C
0

I used this:

function generatePdfZipViaWS_ajax(theUrl) {

    //=========================
    // testé avec Chrome 37.0.2062.124 m, Firefox  32.0.3
    // ça block avec IE9 à cause du xmlHttp.overrideMimeType
    //=========================
    var xmlHttp = new XMLHttpRequest();

    var alert = document.getElementById("alertError");
    block_UI();

    var url = "undefined";

    xmlHttp.open("GET", theUrl, true);
    xmlHttp.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
    xmlHttp.overrideMimeType("application/octet-stream");
    xmlHttp.responseType = "blob";

    xmlHttp.onload = function(oEvent) {
        if (xmlHttp.status == 200) {

            deblock_UI();
            // a mettre apres un certain temps:  window.URL.revokeObjectURL(url);

        } else {
            alert.style.display = "block";
            deblock_UI();

            //  console.log("Error " + xmlHttp.status + " occurred downloading your file.<br \/>");
        }
    };

    xmlHttp.onreadystatechange = function() {

        if (xmlHttp.readyState == xmlHttp.DONE) {

            if (xmlHttp.status == 200) {

                var contentDisposition = xmlHttp.getResponseHeader("Content-Disposition");
                var type = xmlHttp.getResponseHeader("Content-Type");

                var reponseType = xmlHttp.responseType;

                var pos1 = contentDisposition.indexOf("archive");
                var pos2 = contentDisposition.lastIndexOf(".zip") + 4;
                var fileName = contentDisposition.substring(pos1, pos2);

                if (fileName === null) {
                    fileName = "archivexxxxxxxxxxxxxx.zip";
                }

                console.log("fileName:" + fileName);

                var blob = xmlHttp.response;
                url = URL.createObjectURL(blob);

                var a = document.createElement('a');
                a.style = "display: none";
                a.href = url;
                a.download = fileName;
                a.type = type;
                document.body.appendChild(a);
                a.click();
                //a.delete();

                deblock_UI();

            } else {

                var msg =" Une erreur "  + xmlHttp.status +" est apparue pendant que votre demande était traitée.\n"

                msg = msg + "Merci de réessayer plus tard!";

                alert.innerHTML = msg;
                alert.style.display = "block";

                deblock_UI();
                console.log(msg);
            }
        }
    };
    xmlHttp.send();
}
Carat answered 28/11, 2014 at 14:0 Comment(1)
IE et Safari var a = document.createElement('a'); var wasPageHide = 0; a.cssText= "display:none"; a.href = theUrl; document.body.appendChild(a); if (is_safari === true ){ var evObj = document.createEvent('MouseEvents'); evObj.initMouseEvent('click', true, true, window); a.dispatchEvent(evObj); }else{ a.click(); } if(window.addEventListener){ window.addEventListener('focus', deblock_UI, false); }else{ window.attachEvent('onfocus',deblock_UI); }Carat
T
0

I don't have time to write a proper answer right now, but since nobody else has a good answer, I think a "Mutation Observer" would work... https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver

Transonic answered 16/8, 2019 at 16:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.