To understand what needs to be done here, let's see what usually happens in this query.
The user clicks a button to request a file.
The file takes time to generate (the user does not receive feedback).
The file is completed and begins to be sent to the user.
What we would like to add is feedback for the user to know what we are doing ... Between steps 1 and 2, we need to respond to a click, and we need to find a way to detect when step 3 was done to remove the visual feedback. We will not inform the user about the download status, their browser will do it like with any other download, we just want to tell the user that we are working on their request.
To create a script file for communication with our script requester page, we will use cookies, this ensures that we are not dependent on the browser for events, iframes, etc. After testing several solutions, it seemed the most stable from IE7 to the latest mobile phones.
Step 1.5: Display graphic feedback.
We will use javascript to display notifications on the screen. I chose a simple transparent black overlay on the entire page, so that the user could not interact with other elements of the page, since the following link may deprive him of the ability to receive the file.
$('#downloadLink').click(function() { $('#fader').css('display', 'block'); });
#fader { opacity: 0.5; background: black; position: fixed; top: 0; right: 0; bottom: 0; left: 0; display: none; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <body> <div id="fader"></div> <a href="#path-to-file-generator" id="downloadLink">Click me to receive file!</a> </body>
Step 3.5: Removing the graphic display.
The simple part is done, now we need to notify javascript that the file is loading. When a file is sent to the browser, it is sent with the usual HTTP headers, this allows us to update client cookies. We will use this feature to provide correct visual feedback. Let me change the code above, we will need to set the initial cookie value and listen to its changes.
var setCookie = function(name, value, expiracy) { var exdate = new Date(); exdate.setTime(exdate.getTime() + expiracy * 1000); var c_value = escape(value) + ((expiracy == null) ? "" : "; expires=" + exdate.toUTCString()); document.cookie = name + "=" + c_value + '; path=/'; }; var getCookie = function(name) { var i, x, y, ARRcookies = document.cookie.split(";"); for (i = 0; i < ARRcookies.length; i++) { x = ARRcookies[i].substr(0, ARRcookies[i].indexOf("=")); y = ARRcookies[i].substr(ARRcookies[i].indexOf("=") + 1); x = x.replace(/^\s+|\s+$/g, ""); if (x == name) { return y ? decodeURI(unescape(y.replace(/\+/g, ' '))) : y;
#fader { opacity: 0.5; background: black; position: fixed; top: 0; right: 0; bottom: 0; left: 0; display: none; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <body> <div id="fader"></div> <a href="#path-to-file-generator" id="downloadLink">Click me to receive file!</a> </body>
Good thing we added here. I put the set / getCookie functions that I use, I don’t know if they are the best, but they work very well. We set the cookie value to 0 when we start the download, this ensures that any other past executions will not interfere. We also initiate a “timeout cycle” to check the cookie value every second. This is the most controversial part of the code, using a timeout to invoke loop functions awaiting a cookie change might not be the best, but it was the easiest way to implement this in all browsers. So, every second we check the cookie value and, if the value is set to 1, we hide the faded visual effect.
Change the server side of the cookie
In PHP, you can do this:
setCookie("downloadStarted", 1, time() + 20, '/', "", false, false);
In ASP.Net
Response.Cookies.Add(new HttpCookie("downloadStarted", "1") { Expires = DateTime.Now.AddSeconds(20) });
The cookie name is downloadStarted , its value is 1 , it expires in NOW + 20seconds (we check every second, so more than 20 is enough for this, change this value if you change the timeout value in javascript), its path is on the whole domain (change this to your liking), it is not protected, since it does not contain confidential data and is not HTTP, so our javascript will be able to see it.
Voila! This sums it up. Please note that the code provided works fine with the production application that I work with, but may not suit your specific needs, correct it to your taste.