5 posts on Feature Detection

CSS3 colors, today (MediaCampAthens session)

1 min read 0 comments Report broken page

Yesterday, I had a session at MediaCampAthens (a BarCamp-style event), regarding CSS3 colors. If you’ve followed my earlier posts tagged with “colors”, my presentation was mostly a sum-up of these.

It was my first presentation ever, actually, the first time I talked to an audience for more than 1 minute :P . This caused some goofs:

  • When introducing myself, I said completely different things than I intended to and ended up sounding like an arrogant moron :P
  • I tried not to look at the audience too much, in order to avoid sounding nervous, and this caused me to completely ignore 2 questions (as I found out afterwards)! How embarrasing!
  • At a certain point, I said “URL” instead of “domain” :P

Also, I had prepared some screenshots (you’ll see them in the ppt) and the projector completely screwed them up, as it showed any dark color as black.

Apart from those, I think it went very well, I received lots of positive feedback about it and the audience was paying attention, so I guess they found it interesting (something that I didn’t expect :P ).

Here is the presentation:

Please note that Slideshare messed up slide #8 and the background seems semi-transparent grey instead of semi-transparent white.

By the way, I also thought afterwards that I had made a mistake: -ms-filter is not required if we combine the gradient filter with Data URIs, since IE8 supports Data URIs (for images at least). Oops, I hate making mistakes that I can’t correct.

Here are some photos from my session. If I did it correctly, every facebook user can see them. If I messed things up, tell me :P

News, Personal, CMYK, Colors, CSS, CSS Values, Feature Detection, Hsla, JS, PHP, RGBA, Speaking
Edit post on GitHub

On native, single-input, multiple file uploads

2 min read 0 comments Report broken page

If you are following the current news on web development, you probably heard that the new Safari 4 has a great feature: It natively allows the user to select multiple files via a single input control, if you specify a value for the attribute multiple:

<input type="file" multiple>

or, in XHTML:

<input type="file" multiple="multiple" />

You might not know that Opera supported multiple file uploads for a while now, based on the earlier Web Forms 2.0 standard in a slightly different (and more flexible) format:

<input type="file" min="1" max="9999″ />

Can we use those currently?

Sure we can, but we should provide fallbacks for the other browsers. Using these features will put pressure on the other browser vendors to implement them as well and generally, native is always better.

How can we find out whether the browser supports them?

Opera

Opera supports accessing those min and max properties as properties of the element. So, it’s quite trivial to check whether Opera-style multiple inputs are supported:

var supportsMin = (function(){
	var fi = document.createElement('input');
	fi.type = 'file';
	return fi.min === '' && fi.max === '';
})();

Safari 4

In Safari 4 the check would be equally simple, if it supported accessing the multiple attribute as a property. Then we could easily check whether it’s boolean and conclude that Safari-style multiple inputs are supported:

var supportsMultiple = (function(){
	var fi = document.createElement('input');
	fi.type = 'file';
	// The second check is probably redundant but what if in the future an implementor
	// decides to make the file inputs to handle multiple selections by default?
	// Yeah, it's not likely, but it's not entirely impossible.
	return fi.multiple === false || fi.multiple === true;
})();

However, that’s currently not the case. The good news are that I reported this as a bug today, and the Webkit team fixed it, so it will be possible in the next Webkit nightly!

Combining the two

You can easily combine these two together with the workaround you prefer:

// Create a file input that allows multiple file selection
var fi = document.createElement('input');
fi.type = 'file';

if(fi.multiple === false || fi.multiple === true) { fi.multiple = true; } else if(fi.min === ‘’ && fi.max === ‘’) { fi.min = 1; fi.max = 9999; } else { // Our preferred workaround here }

What about Mozilla?

Ok, we all know that IE will probably take years to implement similar functionality. But usually, the Mozilla team implements new and exciting stuff quite fast.

As it turns out, there is a relevant ticket sitting in their Bugzilla for a while now. If you want them to implement it, vote for it so that it’s priority increases.

If they do implement it in the way suggested, the code posted above will work for that too, without any changes - The advantages of feature detection baby! ;)

Articles, News, Browsers, Feature Detection, HTML, JS, WebKit, WebKit Bugs
Edit post on GitHub

Check whether the browser supports RGBA (and other CSS3 values)

2 min read 0 comments Report broken page

When using CSS, we can just include both declarations, one using rgba, and one without it, as mentioned in my post on cross-browser RGBA backgrounds. When writing JavaScript however, it’s a waste of resources to do that (and requires more verbose code), since we can easily check whether the browser is RGBA-capable, almost as easily as we can check whether it suppports a given property. We can even follow the same technique to detect the support of other CSS3 values (for instance, multiple backgrounds support, HSLA support, etc).

The technique I’m going to present is based on the fact that when we assign a non-supported CSS value on any supported CSS property, the browser either throws an error AND ignores it (IE-style), or simply ignores it (Firefox-style). Concequently, to check whether RGBA is supported, the algorithm would be:

  1. Get the color (or backgroundColor, or borderColor or any property that is widely supported and accepts color values) value of the style object of any element that exists in the page for sure (for instance, the first script tag) and store it in a variable.
  2. Assign an RGBA color to the color property of that element and catch any errors produced.
  3. Assign to a variable whether the color of that element did change (boolean true or false).
  4. Restore the previous color to the color property, so that our script doesn’t interfere with the designer’s decisions.
  5. Return the stored result.

and it would result in the following code:

function supportsRGBA()
{
	var scriptElement = document.getElementsByTagName('script')[0];
	var prevColor = scriptElement.style.color;
	try {
		scriptElement.style.color = 'rgba(1,5,13,0.44)';
	} catch(e) {}
	var result = scriptElement.style.color != prevColor;
	scriptElement.style.color = prevColor;
	return result;
}

Performance improvements

The code above works, but it wastes resources for no reason. Every time the function is called, it tests RGBA support again, even though the result will never change. So, we need a way to cache the result, and return the cached result after the first time the function is called.

This can be achieved in many ways. My personal preference is to store the result as a property of the function called, named 'result':

function supportsRGBA()
{
	if(!('result' in arguments.callee))
	{
		var scriptElement = document.getElementsByTagName('script')[0];
		var prevColor = scriptElement.style.color;
		try {
			scriptElement.style.color = 'rgba(0, 0, 0, 0.5)';
		} catch(e) {}
		arguments.callee.result = scriptElement.style.color != prevColor;
		scriptElement.style.color = prevColor;
	}
	return arguments.callee.result;
}

Making it bulletproof

There is a rare case where the script element might already have rgba(0,0,0,0.5) set as it’s color value (don’t ask me why would someone want to do that :P ), in which case our function will return false even if the browser actually supports RGBA. To prevent this, you might want to check whether the color property is already set to rgba(0,0,0,0.5) and return true if it is (because if the browser doesn’t support rgba, it will be blank):

function supportsRGBA()
{
	if(!('result' in arguments.callee))
	{
		var scriptElement = document.getElementsByTagName('script')[0];
		var prevColor = scriptElement.style.color;
		var testColor = 'rgba(0, 0, 0, 0.5)';
		if(prevColor == testColor)
		{
			arguments.callee.result = true;
		}
		else
		{
			try {
				scriptElement.style.color = testColor;
			} catch(e) {}
			arguments.callee.result = scriptElement.style.color != prevColor;
			scriptElement.style.color = prevColor;
		}
	}
	return arguments.callee.result;
}

Done!


20 things you should know when not using a JS library

5 min read 0 comments Report broken page

You might just dislike JavaScript libraries and the trend around them, or the project you’re currently working on might be too small for a JavaScript library. In both cases, I understand, and after all, who am I to judge you? I don’t use a library myself either (at least not one that you could’ve heard about  ;) ), even though I admire the ingenuity and code quality of some.

However, when you take such a brave decision, it’s up to you to take care of those problems that JavaScript libraries carefully hide from your way. A JavaScript library’s purpose isn’t only to provide shortcuts to tedious tasks and allow you to easily add cool animations and Ajax functionality as many people (even library users) seem to think. Of course these are things that they are bound to offer if they want to succeed, but not the only ones. JavaScript libraries also have to workaround browser differences and bugs and this is the toughest part, since they have to constantly keep up with browser releases and their respective bugs and judge which ones are common enough to deserve workaround and which ones are so rare that would bloat the library without being worth it. Sometimes I think that nowadays, how good of a JavaScript developer you are doesn’t really depend on how well you know the language, but rather on how many browser bugs you’ve heard/read/know/found out. :P

The purpose of this post is to let you know about the browser bugs and incompatibilities that you are most likely to face when deciding againist the use of a JavaScript library. Knowledge is power, and only if you know about them beforehand you can workaround them without spending countless debugging hours wondering “WHAT THE…”. And even if you do use a JavaScript library, you will learn to appreciate the hard work that has been put in it even more.

Some of the things mentioned below might seem elementary to many of you. However, I wanted this article to be fairly complete and contain as many common problems as possible, without making assumptions about the knowledge of my readers (as someone said, “assumption is the mother of all fuck-ups” :P ). After all, it does no harm if you read something that you already know, but it does if you remain ignorant about something you ought to know. I hope that even the most experienced among you, will find at least one thing they didn’t know very well or had misunderstood (unless I’m honoured to have library authors reading this blog, which in that case, you probably know all the facts mentioned below :P ) . If you think that something is missing from the list, feel free to suggest it in the comments, but have in mind that I conciously omitted many things because I didn’t consider them common enough.

DOM

  1. getElementById('foo') also returns elements with name="foo" in IE and older versions of Opera.
  2. getElementsByTagName('*') returns no elements in IE5.5 and also returns comment nodes in all versions of IE (In case you’re wondering: DOCTYPE declaration will count, Conditional comments will not).
  3. getElementsByClassName() in Opera (even Opera 10 Alpha) doesn’t match elements with 2 or more classes when the one you’re looking for is not the first but it’s also a substring of the first. Read the discussion between me and John Resig on the latter’s blog post mentioned below if this seems a bit unclear.
  4. There is no element.children collection in Firefox 3-. You have to create it yourself by filtering the childNodes collection if it doesn’t exist.
  5. If your code involves preformatted elements (for instance if you are making a syntax highlighter), beware when setting the innerHTML of those: IE won’t preserve line breaks (\r\n s) and whitespace. You have to use outerHTML, which will actually replace the element so you should find a way to obtain a reference to the newly created one in case you still need to perform stuff on it.
  6. To get the dimensions of the viewport, standard compliant browsers use window.innerWidth (and innerHeight) whereas IE uses document.documentElement.clientWidth (and clientHeight).
  7. To get the scroll offsets of the current page, standard compliant browsers use window.pageXOffset (and pageYOffset) whereas IE uses document.documentElement.scrollTop (and scrollLeft).
  8. To make matters worse, in both cases above, you need to use document.body instead of document.documentElement when in Quirks mode.

John Resig (of the jQuery fame), recently posted a great presentation, which summarized some browser bugs related to DOM functions. A few of the bugs/inconsistencies mentioned above are derived from that presentation.

Events

  1. When using IE’s attachEvent() the this reference inside the callback refers to the useless window object
  2. eventObject.target is eventObject.srcElement in IE
  3. eventObject.stopPropagation() is eventObject.cancelBubble = true; in IE
  4. eventObject.preventDefault() is eventObject.returnValue = false; in IE
  5. There are many more event object incompatibilities for specific events (the ones above are for all events). Take a trip to QuirksMode for more information.
  6. IE leaks horribly (especially IE6) in various cases.
  7. If you register the same event handler X times, IE fires it X times.
  8. Determining when the DOM is ready is a complete mess. Firefox and Opera 9+ support the DOMContentLoaded event, Safari doesn’t but you can check it’s document.readyState property and in IE document.readyState is unreliable and you should either inject a deferred script, either poll the DOM untill there are no errors or use an external behavior file. Of course you could always just put a script tag at the bottom of the page, just before the body closing tag, which will fire all attached handlers which is actually the best approach in terms of which way fires earliest (but not too early) according to my tests, but that hardly qualifies as unobtrusive…
  9. (edit, thanks Sobral!) The Event object is not passed as a parameter to the callback but resides in window.event in older versions of IE

Type detection

  1. The typeof operator is almost useless:

    • typeof null == 'object'
    • typeof new String('foo') == 'object'
    • typeof [] == 'object'

    Use Object.prototype.toString instead.

CSS

  1. Although most CSS properties are converted to their JavaScript equivalents in a standard way (characters after dashes are Uppercase, others are lowercase, the dashes get removed), float is an exception: It’s converted to cssFloat in most browsers and styleFloat in IE. Check which one exists and use that.
  2. Getting the current (computed) style of an element is another complete mess. IE uses element.currentStyle[propertyJS] whereas standard compliant browsers use document.defaultView.getComputedStyle(element, null).getPropertyValue(propertyCSS). And as if this wasn’t enough, there are various problems associated with specific properties or browsers, like:
    • IE returns the cascaded values and not the computed ones (for instance, it might return ems for a property that was specified in ems, and not pixels). Dean Edwards has thought a very clever hack to workaround this and didn’t even blog about it (it’s simply a comment in a blog post of Erik Arvidsson’s!).
    • Any hidden (via display:none;) element, yields a width/height/top/right/bottom/left value of zero.
    • auto or normal might be returned for properties that are left at their defaults. For instance, IE does this with width/height for elements that don’t have dimensions explicitly set via CSS.
    • In most browsers, shorthands (like border) will yield a blank string. You’d have to use the most specific property (for instance, border-left-width).
    • Colors will be returned in different formats across browsers. For instance, IE uses #RRGGBB whereas Mozilla uses rgb(red, green, blue).

So, what now?

Never, EVER use a browser detect to solve the problems mentioned above. They can all be solved with feature/object detection, simple one-time tests or defensive coding. I have done it myself (and so did most libraries nowadays I think) so I know it’s possible. I will not post all of these solutions to avoid bloating this post even more. You can ask me about particular ones in the comments, or read the uncompressed source code of any library that advertises itself as “not using browser detects”. JavaScript Libraries are a much more interesting read than literature anyway. :P

Are the facts mentioned above actually 20?

I’m not really sure to be honest, it depends on how you count them. I thought that if I put a nice round number in the title, it would be more catchy :P


Check whether a CSS property is supported

1 min read 0 comments Report broken page

Sometimes when using JavaScript, you need to determine whether a certain CSS property is supported by the current browser or not. For instance when setting opacity for an element, you need to find out whether the property that the browser supports is opacity, -moz-opacity (MozOpacity), -khtml-opacity (KhtmlOpacity) or the IE proprietary filter.

Instead of performing a forwards incompatible browser detect, you can easily check which property is supported with a simple conditional. The only thing you’ll need is a DOM element that exists for sure. A DOM element that exists in every page and is also easily accessible via JS (no need for getElementsByTagName), is the body element, but you could use the <head> or even a <script> tag (since there is a script running in the page, a <script> tag surely exists). In this article we’ll use document.body, but it’s advised that you use the head or script elements, since document.body may not exist at the time your script is run.

So, now that we have an element to test at, the test required is:

if('opacity' in document.body.style)
{
	// do stuff
}

Of course you’d change document.body with a reference to the element you’d like to test at (in case it’s not the body tag)  and 'opacity' with the name of the actual property you want to test. You can even wrap up a function to use when you want to check about the support of a certain property:

function isPropertySupported(property)
{
	return property in document.body.style;
}

The only thing you should pay attention to, is using the JavaScript version of the CSS property (for example backgroundColor instead of background-color)

Wasn’t it easy?