Welcome to another addition of Beating IE into submission!
Before I get started though, I have to make a confession. This article won't be so much about beating IE into submission as it will about IE having beaten me into submission!
Unfortunately, this is one case I wasn't able to come up with any great solutions. All I'll be able to offer at the end of this article, are three (relatively unsatisfying) work-arounds. Which one is least-evil… I'll leave that up to you.
So what is this mysterious bug I'm referring to? Read on to find out.
First a little background on CSS custom cursors.
Suppose you've created a nifty custom cursor that you want to use as the default cursor in your web site. The simplest way to this would be to create a CSS style rule on the
<body> element that specified this custom cursor:
cursor: url("http://my.site.com/custom.cur"), auto;
This tells the browser to attempt to load
http://my.site.com/custom.cur and use it as the cursor whenever the mouse is over the <body> element. The
,auto is a fall-back, so in case the custom cursor can't load (for whatever reason) the next cursor in the list will be used.
So far so good. But suppose we don't want to specify a complete URI as I've done in the preceding example. No problem; simply change the path to be a relative one:
cursor: url("./custom.cur"), auto;
In this example, we're telling the browser to load the file,
custom.cur from the current folder (the one in which the CSS file resides).
We can go further with this relative "pathing":
cursor: url("../custom.cur"), auto;
Here we're telling the browser to go back one level, and load the file,
custom.cur from there. So if, for example, you keep all of your stylesheet files neatly organized in a directory called /css/, this would tell the browser to go back one level (to the root of the web site) and load the custom cursor from there.
Well… almost. That is what should happen, and is every worthwhile browser on earth, that is in fact what does happen. Not so in IE6 or 7.
If you were to take any of the above style rules, and paste them directly into your HTML source file inside of a
<style>...</style> block, all would work as expected.
However, the moment you take your style rules out of your HTML source file, and place them in an external file, all bets are off!
It seems that IE does not understand how to evaluate the relative path of a
cursor: rule, which is mind-boggling considering that IE (somehow) manages to get it right when it comes to CSS background images!
So what does IE do exactly? It evaluates the relative path of the
cursor: rule using the HTML source document's URI as the base URI.
This is so completely and totally, mind-numbingly, punch-yourself-in-the-face stupid, that it makes me want to punch myself in the face!
For one thing, it's a complete lack of consistency that IE handles relative paths differently for images than it does for cursors. For another thing the W3C specifications say quite clearly:
In order to create modular style sheets that are not dependent on the absolute location of a resource, authors may use relative URIs. Relative URIs (as defined in [RFC3986]) are resolved to full URIs using a base URI. RFC 3986, section 5, defines the normative algorithm for this process. For CSS style sheets, the base URI is that of the style sheet, not that of the source document. (emphasis mine).
So what does this mean in a real-world scenario? It means that the same style rule won't work in both IE and W3C browsers if your CSS lives in an external file. For example:
cursor: url("../custom.cur"), auto;
— index.html —
<link rel = "stylesheet" type = "text/css" href="./css/theme1/styles.css" />
In the preceding example, the proper evaluation of the
cursor: rule is as follows:
../custom.cur means "load custom.cur, which is located one level up from the CSS source file (styles.css)."
The path to
./css/theme1/style.css, which is itself, a relative path whose base URI is determined from the file linking it (in this case, index.html).
If we assume index.html lives at the root of the web site, then we can conclude that the absolute path to
cursor: indicates that the custom cursor is located one level up from the CSS source file, this would mean that the browser expects to find
Remember, this is what a good, W3C compliant browser would do. This is most definitely not what IE does.
IE evaluates the relative path incorrectly. It uses the HTML source file (index.html) to obtain the base URI for normalizing the URL specified in the CSS
cursor: rule. This means that IE will try to look one level up from the web site root for
Acute readers might have realized that one level up from the web site root is precisely nothing! It has no meaning, and it will of course, fail. But the relative URI need not resolve to something meaningless as it does in this example; any URI that IE gets a hold of is almost certainly going to resolve to the wrong URI.
"But wait," I can hear at least 50% of you shouting, "I've gotten relative cursor paths to work in IE."
To this I reply emphatically "No you haven't!"
Any time it has worked in IE is sheer coincidence. IE always interprets relative cursor paths incorrectly. Sometimes though, the incorrect interpretation just happens to be the same as the correct interpretation, which brings me to my first work-around.
Work Around Option 1:
Rather than putting your
cursor: rules in an external file, put them all in a
<style>...</style> directly in your HTML source file.
This works because when IE always resolves the base URI from the HTML source file no matter what. W3C browser will resolve it from the wherever the CSS style rules are located (which in this case happens to be the HTML source file). Thus, both browser start from the same base URI, and life is peachy.
Putting your style rules directly in your HTML file kinda sucks though, because it totally destroys style re-usability.
Work Around Option 2:
Instead of using relative paths, use absolute paths. This will work in all browsers, so that is the main advantage of this approach. However, it kills flexibility because if your web site structure changes, chances are good you'll have to manually update all your styles rules, which is tedious and error-prone. Boo!
Work Around Option 3:
Use IE conditional comments to load a specific IE stylesheet. Make sure your IE-specific stylesheet comes after all other stylesheet links, and you'll be able to override the correct
cursor: rules with some whacky
cursor: rules that will make silly 'ol IE happy as a clam.
So there you have it. Three crappy work-arounds for a crappy bug from a crappy browser.
But there is hope! Because when I'm President, I'm going to make IE illegal 🙂