So I have an <img>
tag on my page.
<img id="the_image" src="/imageServlet">
Let's assume that the /imageServlet is responding with a few HTTP response headers that indicate that the browser should never cache this image. The reason, in this case, is that the image may contain sensitive employee information and there are laws in certain countries that the browser should not (without user consent) save the image on the client's hard drive in case the machine is stolen. I believe the response header is Cache-Control: no-cache
All I really want to do is duplicate this image tag, but I do not want another HTTP request to be made for this image.
My application is a single page, using javascript I dynamically these image tags - but now I would like to display this same image in more than one place. For example the image could be a thumbnail version of a user's profile picture. In one place I display the thumbnail as a small list, and then I display the same image in a popup when you click that user.
Every time I create a new <img>
tag with this src, the browser is making a fresh call to the server to retrieve the image. I don't care if the user presses "refresh" on the browser and a new HTTP request for that image should load - but within a single page load, shouldn't the Image data be available in memory, so couldn't I reuse this?
How can I get around this? This greatly impacts the performance of the page, where images are reused and displayed in multiple places, and they need to reload every time (even though user has not navigated to any other page or refresh browser).
Here is some code that I wrote just now with an idea where any time the application wants to load an image it would call "loadImage" which would eventually call the callback with some HTML element that it can freely use. The idea would be this central function would create the Image but it would make sure that for any unique src
only one single HTTP request would be made - regardless if the HTTP response headers contain Cache-Control: no-cache
or not.
(function(){
var HANDLERS = {};
/**
* I want to keep around a cached "Image" then the "callback" will be called with a
* duplicate somehow - whatever comes back from here can be inserted somewhere, but
* hopefully no additional HTTP requests needs to be made!!
* @param {Image} img
* @return {Image} A copy of the incoming img
*/
function duplicateImage(img) {
// Does not work!! D'oh, is this even possible to achieve??
return img.cloneNode(true);
}
/**
* Users can call this to load the image. The callback is called when the image is ready.
* In case the image fails the callback will be given an object that contains a success flag.
*
* Example:
* loadImage('/imageServlet?xxxx', function(result) {
* if (result.success) { $(body).append(result.image); }
* else { console.log("The image failed to load!") }
* });
*
* @param {string} src The src for the image
* @param {function({success:boolean, image:Image})} callback Give a callback to be called when ready
*/
window.loadImage = function(src, callback) {
if (!HANDLERS[src]) {
var queue = [];
// This loadImage can be called more than once with the same src
// before the image has successfully loaded. We will queue up any
// callbacks and call them later, then replace this with function
// that will directly invoke the callback later.
HANDLERS[src] = function(callback) {
queue.push(callback);
}
// Create the image here, but do it only once!!
var el = new Image();
// When the image loads, we will keep it around in the el
// variable, but the callback will be called with a copy
el.onload = function() {
el.onload = el.onerror = null;
var call = HANDLERS[src] = function(callback) {
callback({
success: true,
image: duplicateImage(el) // This is where the image is duplicated!
});
}
for (var i=0; i<queue.length; i++) {
call(queue[i]);
}
}
// This is just in case the image fails to load. Call any
// queued callbacks with the success false.
el.onerror = function() {
el.onload = el.onerror = null;
var call = HANDLERS[src] = function(callback) {
callback({success: false});
}
for (var i=0; i<queue.length; i++) {
call(queue[i]);
}
}
el.src = src;
}
HANDLERS[src](callback);
}
})();