Category Archives: CSS

CSS3 Snippets in TextMate – Linear Gradients

Gradients are great. Linear gradients are super-great, because (as long as you don’t want funky angles) they work on pretty much all current browsers if you can remember the syntax. I can’t remember the syntax, but with a little home-schooling TextMate can…

Create these 2 snippets within TextMate’s CSS scope:

Name:
background: vertical linear gradient
Tab trigger:
background
Scope Selector:
source.css
Snippet:
background: ${1:top-color};
background: -webkit-gradient(linear, left top, left bottom, from(${1:top-color}), to(${2:bottom-color}));
background: -moz-linear-gradient(top, ${1:top-color}, ${2:bottom-color});
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=${1:top-color}, endColorstr=${2:bottom-color});
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=${1:top-color}, endColorstr=${2:bottom-color})";
$3

Name:
background: horizontal linear gradient
Tab trigger:
background
Scope Selector:
source.css
Snippet:
background: ${1:left-color};
background: -webkit-gradient(linear, left top, right top, from(${1:left-color}), to(${2:right-color}));
background: -moz-linear-gradient(left top, ${1:left-color}, ${2:right-color});
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=${1:left-color}, endColorstr=${2:right-color}, GradientType=1);
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=${1:left-color}, endColorstr=${2:right-color}, GradientType=1)";
$3

Now, typing background within a CSS context and pressing tab should show something like this:

background.jpg

Choosing one of the two new options at the bottom will give you all the code you need for most browsers to show a horizontal or vertical linear gradient. The first colour you enter (either the top-most or left-most colour) will become the default background for browsers that don’t support gradients, which is hopefully usually what you want. In case you were wondering, -ms-filter is specifically for IE8 (in IE8-mode) which requires quotes around the contents.

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.