Bookmarklet to de-cache CSS and images – Version 2

You may recall, in this previous post I showed you how to use a bookmarklet (typically a small piece of JavaScript you can save as a bookmark and run against a page) to get the latest versions of CSS files and images, and not use the cached files. The previous version had some limitations. Namely:

  • Background images set in CSS would not be refreshed
  • Frame and iframe contents would not be refreshed
  • In some instances it was one-hit only – i.e. you could only run it once, without having to manually refresh the page anyway

So, I would like to present to you – CacheBuster version 2. Or CacheBusterizer the second. Or MegaCacheKiller 2. You get the idea.

This time, I will step through the code and how it works.

Getting the current values of styles set via CSS can be a pain. Unless they were explicitly set via JavaScript, we can only fetch them using currentStyle or getComputedStyle, complete of course with their own browser idiosyncrasies. Therefore, the first thing we need is a function to do this for us in a cross-browser fashion. I have amended a version I found on Incoherent Babble.

function gcs(e,s) {
	if (typeof e.currentStyle != 'undefined') {
		return e.currentStyle[s];
	} else {
		return document.defaultView.getComputedStyle(e, null)[s];
	}
}

In this instance, e represents the element, and s is a string representing the style we are looking for. Remember that we are looking JavaScript termed styles, not CSS – so backgroundImage, rather than background-image.

Since I am going to loop through all of the frames in the page, as well as the main window itself, I have decided to join these together in a single array, rather than writing the code out twice.

var z =[];
z.push(self);
z.concat(self.frames);

To get the latest version of the files, I will just amend a querystring with the current time on the end of the filename.

var y = new Date().getTime();

And finally, we do the work:

for (k=0,l=z.length;k<l;k++) {
	x=z[k].document.getElementsByTagName('*');
	for (i=0,j=x.length;i<j;i++){
		bi = gcs(x[i],'backgroundImage');
		if (bi.indexOf(')') > -1){
			bi = bi.replace(/'/gi,'').replace(/"/gi,'');
			x[i].style.backgroundImage=bi.replace(')',(bi.indexOf('?')==-1?'?':'')+y+')');
		}
	}
	x=z[k].document.getElementsByTagName('link');
	for(i=0,j=x.length;i<j;i++){
		x[i].href+=(x[i].href.indexOf('?')==-1?'?':'')+y;
	}
	x=z[k].document.getElementsByTagName('img');
	for(i=0,j=x.length;i<j;i++){
		x[i].src+=(x[i].src.indexOf('?')==-1?'?':'')+y;
	}
}

This code:

  • Loops through all elements on the page, checks to see if they have a background image set and appends the time on the end of the URI if they do
  • Loops through all the link tags on a page for stylesheets, and appends the time on the end of the href of any it finds
  • Loops through all the img tags on a page, and appends the time on the end of the src of any it finds

Finally, we wrap the whole lot in a closure to make sure we don’t mess with the main window namespace, tidy up, and we get:

(function(){
	var gcs = function(e,s) {
	  if (typeof e.currentStyle != 'undefined')
	    { return e.currentStyle[s]; }
	  else
	    { return document.defaultView.getComputedStyle(e, null)[s]; }
	};
	var i,j,k,l,x,y,z,bi;
	z =[];
	z.push(self);
	z.concat(self.frames);
	y = new Date().getTime();
	for (k=0,l=z.length;k<l;k++) {
		x=z[k].document.getElementsByTagName('*');
		for (i=0,j=x.length;i<j;i++){
			bi = gcs(x[i],'backgroundImage');
			if (bi.indexOf(')') > -1){
				bi = bi.replace(/'/gi,'').replace(/"/gi,'');
				x[i].style.backgroundImage=bi.replace(')',(bi.indexOf('?')==-1?'?':'')+y+')');
			};
		};
		x=z[k].document.getElementsByTagName('link');
		for(i=0,j=x.length;i<j;i++){
			x[i].href+=(x[i].href.indexOf('?')==-1?'?':'')+y;
		};
		x=z[k].document.getElementsByTagName('img');
		for(i=0,j=x.length;i<j;i++){
			x[i].src+=(x[i].src.indexOf('?')==-1?'?':'')+y;
		};
	};
})();

The last thing to do, is stick it all on one line, and prefix javascript: and we’re done.

javascript:(function(){var gcs=function(e,s){if(typeof e.currentStyle!='undefined'){return e.currentStyle[s];}else{return document.defaultView.getComputedStyle(e,null)[s];}};var i,j,k,l,x,y,z,bi;z=[];z.push(self);z.concat(self.frames);y=new Date().getTime();for(k=0,l=z.length;k<l;k++){x=z[k].document.getElementsByTagName('*');for(i=0,j=x.length;i<j;i++){bi=gcs(x[i],'backgroundImage');if(bi.indexOf(')')>-1){bi=bi.replace(/'/gi,'').replace(/"/gi,'');x[i].style.backgroundImage=bi.replace(')',(bi.indexOf('?')==-1?'?':'')+y+')');}};x=z[k].document.getElementsByTagName('link');for(i=0,j=x.length;i<j;i++){x[i].href+=(x[i].href.indexOf('?')==-1?'?':'')+y;};x=z[k].document.getElementsByTagName('img');for(i=0,j=x.length;i<j;i++){x[i].src+=(x[i].src.indexOf('?')==-1?'?':'')+y;};};})();

Save this result as a bookmark, and any page you run the bookmark against will load all images and CSS anew, bypassing the cache! Phew.

The only remaining caveat I can think of: this won’t work for CSS included with the @import directive. Any ideas?

Comments, as always, are welcome.

2 thoughts on “Bookmarklet to de-cache CSS and images – Version 2

  1. Had this article among my bookmarks. Finally had time to read it through. Thank you for your implementation.

    Regarding the imported CSS, you can access the rules via document.styleSheets, which will give you access to the import rules. This will cut the code of regex involved. I’ve implemented a prototype that you can test at https://github.com/gajus/refresh-css. Haven’t tested it cross-browser, though. Hey – it is a starting point.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>