<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Lea Verou &#187; Original</title>
	<atom:link href="http://leaverou.me/category/original/feed/" rel="self" type="application/rss+xml" />
	<link>http://leaverou.me</link>
	<description>Thoughts on web design, web development and usability</description>
	<lastBuildDate>Sun, 22 Aug 2010 22:19:22 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0.1</generator>
		<item>
		<title>Organizing a university course on modern Web development</title>
		<link>http://leaverou.me/2010/07/organizing-a-university-course-on-modern-web-development/</link>
		<comments>http://leaverou.me/2010/07/organizing-a-university-course-on-modern-web-development/#comments</comments>
		<pubDate>Mon, 26 Jul 2010 12:03:27 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Original]]></category>
		<category><![CDATA[Personal]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[education]]></category>
		<category><![CDATA[ES5]]></category>
		<category><![CDATA[HTML]]></category>
		<category><![CDATA[HTML5]]></category>
		<category><![CDATA[SVG]]></category>
		<category><![CDATA[teaching]]></category>
		<category><![CDATA[XPath]]></category>
		<category><![CDATA[XSLT]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=576</guid>
		<description><![CDATA[About a year ago, prof. Vasilis Vassalos of Athens University of Economics and Business approached me and asked for my help in a new course they were preparing for their Computer Science department, which would introduce 4th year undergrads to various web development aspects. Since I was always complaining about how outdated higher education is when [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2010%252F07%252Forganizing-a-university-course-on-modern-web-development%252F%22%2C%20%22shorturl%22%3A%20%22http%3A%2F%2Fbit.ly%2F9j5Bz7%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Organizing%20a%20university%20course%20on%20modern%20Web%20development%22%20%7D);"></div>
<p>About a year ago, prof. <a href="http://twitter.com/vassalos" target="_blank">Vasilis Vassalos</a> of <a title="The university official website (yeah, I know...)" href="http://aueb.gr" target="_blank">Athens University of Economics and Business</a> approached me and asked for my help in a new course they were preparing for their <a href="http://www.cs.aueb.gr" target="_blank">Computer Science department</a>, which would introduce 4th year undergrads to various web development aspects. Since I was always complaining about how outdated higher education is when it comes to web development, I saw it as my chance to help things change for the better, so I agreed without a second thought.</p>
<p>This is one of the main reasons I didn&#8217;t have time to write many blog posts for the past months: This activity took up all my spare time. However, it proved to be an interesting and enlightening experience, in more than one ways. In this blog post I&#8217;ll describe the dilemmas we faced, the decisions we made and the insights I gained throughout these 6 months, with the hope that they&#8217;ll prove to be useful for anyone involved in something similar.</p>
<h3>Table of contents</h3>
<ol>
<li><a href="#course-content">Content</a></li>
<li><a href="#homework">Homework</a></li>
<li><a href="#labs">Labs</a></li>
<li><a href="#aftermath">Personal aftermath</a></li>
</ol>
<p><span id="more-576"></span></p>
<h3 id="course-content">Content</h3>
<p>The goals of a university course differ from the ones of a professional seminar or conference session in many ways, the key one being that most of its students will (professionally) utilize the things they learned in the future and not right after they walk away from class. So, the stuff being taught must be useful even after a couple years have passed. Also, issues of the present might not be issues of the future and what isn&#8217;t possible today (due to browser support issues) will probably be tomorrow.  These observations led us to decide <strong>against teaching proprietary stuff. Instead, we only included  things which come with a specification that has reached a fairly stable state</strong> (with the exception of <strong>very </strong>widespread non-standard stuff, such as <code>innerHTML</code>). We also decided <strong>not to address workarounds and browser incompatibilities at all</strong>, since these would probably be out of date in a few years. Also because, if we teach everything else right, they should be able to learn these by themselves, if needed (we did teach feature detection techniques though, those are timeless <img src='http://leaverou.me/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> ). We also included <strong>many cutting edge topics (CSS3, HTML5, ES5, SVG&#8230;) since we believe that they will be necessary tools of the trade tomorrow</strong>. To be pragmatic however, <strong>we did not teach stuff that no browser has implemented yet</strong>, besides perhaps a brief mention.</p>
<p>To make things easier for the students, we used <strong>Firefox 3.6</strong> for everything. We tested their assignments there, we used it to present something in the labs etc. Why Firefox?</p>
<ul>
<li>It&#8217;s at a quite good level of standards compliance and implements many modern technologies &amp; features</li>
<li>Fewer bugs (Webkit implements stuff faster, but in more buggy ways)</li>
<li>It has the best development tools (Firebug)</li>
<li>With Brendan Eich being Mozilla&#8217;s CTO, we all know how progressive Firefox is when it comes to JavaScript.</li>
</ul>
<p>Of course, this doesn&#8217;t mean it&#8217;s the only right choice. Google Chrome for example would be another good pick.</p>
<p>Another useful observation was that 4th year Computer Science students already know programming quite well, especially Java. So, <strong>we did not need to go through the basics of programming syntax</strong> like introductory books or seminars frequently do. Consequently, <strong>we skipped explaining how control structures or operators work</strong> in JavaScript or PHP and just <strong>focused on their differences</strong> from Java and other languages.</p>
<p>Another dilemma we faced was whether we should teach stuff on popular frameworks and whether we should allow them in the homeworks. We decided <strong>against allowing them in the homeworks</strong> because I believe that someone must not use a framework just to skip learning about the intricacies of a language. They should be used after the basics have been consolidated, in order to save time. Also because if everyone skips learning and just uses an abstraction to do the heavy lifting from the very beginning, who will write the abstractions after all? Another reason was that a large portion of every JavaScript framework is about handling cross-browser differences. However, these had no place in our course, so a JS framework wasn&#8217;t as necessary as it is in day to day web development. Regarding teaching them, <strong>we thought it would be a good idea to introduce students to the popular JS &amp; PHP frameworks in the last lectures</strong>, but there was no time left. Maybe next year.</p>
<p>To sum up, the course content ended up being (I&#8217;m listing client-side matters more extensively, since they are also the focus of this blog):</p>
<ul>
<li>General stuff about web application architecture and how the HTTP protocol works</li>
<li>We presented a small web application example (an AJAX shopping cart) in order for the students to get an idea about how everything clicks together</li>
<li>Markup languages
<ul>
<li>SGML</li>
<li>DTDs</li>
<li>HTML and XHTML
<ul>
<li>Basic structure of an (X)HTML document</li>
<li>Content model, block vs inline elements</li>
<li>Basic HTML elements
<ul>
<li>headings &amp; paragraphs</li>
<li>lists (ordered, unordered, definition lists)</li>
<li>tables</li>
<li>grouping elements (div &amp; span)</li>
</ul>
</li>
<li>Doctypes, the HTML5 doctype</li>
<li>The incentives behind XHTML &amp; the future ((X)HTML 5)</li>
<li>(X)HTML Validation</li>
<li>HTML forms
<ul>
<li>How forms work, GET vs POST</li>
<li>Form controls, shared attributes</li>
<li>The various input types (+ the new ones HTML5 brings)</li>
<li>Other form controls (buttons, &lt;select&gt; lists, textareas)</li>
<li>Basic form accessibility (labels &amp; fieldsets)</li>
</ul>
</li>
<li>Working with Multimedia (old methods, HTML5 video &amp; audio elements, comparison)</li>
</ul>
</li>
<li>XML and XPath, XQuery, XSLT</li>
</ul>
</li>
<li>CSS
<ul>
<li>CSS standards</li>
<li>CSS rules</li>
<li>Validation</li>
<li>Adding CSS to a page (linking/embedding methods)</li>
<li>Media targeting (The media attribute, @media rules, media queries)</li>
<li>CSS selectors
<ul>
<li>Introduction to the DOM</li>
<li>Basic selectors (Universal selector, Type selector, Class selector, Id selector)</li>
<li>Classes vs Ids</li>
<li>Attribute selectors (all 6)</li>
<li>Pseudo-classes (including most of the CSS3 ones)</li>
<li>Pseudo-elements</li>
<li>Simple selectors &amp; simple selector sequences</li>
<li>Combinators (all 4)</li>
<li>Selector grouping</li>
<li>XML namespaces &amp; CSS</li>
</ul>
</li>
<li>Cascading &amp; Inheritance
<ul>
<li>The problem: Conflicts</li>
<li>Specificity</li>
<li>Origin</li>
<li>!important</li>
<li>Inheritance</li>
<li>The special value <em>inherit</em></li>
</ul>
</li>
<li>Properties &amp; values
<ul>
<li>Keywords</li>
<li>Numerical values &amp; units</li>
<li>Colors (including CSS3 colors)</li>
<li>How shorthands work</li>
<li>Unsupported values &amp; providing fallbacks</li>
</ul>
</li>
<li>Box model
<ul>
<li>width &amp; height</li>
<li>Block level &amp; inline level elements (reminder from the HTML lectures)</li>
<li>The <em>display </em>property</li>
<li>border</li>
<li>padding</li>
<li>margin</li>
</ul>
</li>
<li>Positioning
<ul>
<li>The <em>position </em>property</li>
<li>Positioning types (absolute, relative, fixed)</li>
<li>z-index</li>
<li>float</li>
<li>Problems with floats, the <em>clear </em>property</li>
</ul>
</li>
<li>Generated content
<ul>
<li>::before and ::after</li>
<li>Static generated content</li>
<li>Dynamic generated content (attributes &amp; counters)</li>
</ul>
</li>
</ul>
</li>
<li>JavaScript
<ul>
<li>Adding JS to a document</li>
<li>Separation of concerns</li>
<li>A first, annotated, example (a simple script that generates tables of content from &lt;h2&gt; headings)</li>
<li>Basic syntax rules (including semicolons &amp; semicolon insertion)</li>
<li>Variables</li>
<li>Operators (including typeof, the comma operator, strict operators, differences of &amp;&amp;/|| in JS)</li>
<li>Primitives (String, Number, Boolean, null, undefined)</li>
<li>Conversion across primitives</li>
<li>Objects</li>
<li>The <em>in</em> &amp; <em>delete</em> operators</li>
<li>for&#8230;in loops</li>
<li>Native objects for primitives (eg the literal 5 vs new Number(5))</li>
<li>The global object</li>
<li>Functions (including function expressions vs function declarations)</li>
<li><em>this</em> &amp; changing execution context</li>
<li>Arrays (including .forEach() traversal)</li>
<li>Regular expressions in JavaScript</li>
<li>OOP in JavaScript
<ul>
<li>OOP concepts in JS</li>
<li>Constructors</li>
<li>Inheritance</li>
<li>Encapsulation (private, priviledged &amp; public properties)</li>
<li>Method overloading</li>
<li>JavaScript shortcomings when it comes to OOP</li>
<li>for&#8230;in loops, inherited properties &amp; [[Enumerable]], .hasOwnProperty()</li>
<li>Type detection based on [[Class]] detection (using Object.prototype.toString())</li>
</ul>
</li>
<li>DOM
<ul>
<li>Traversal</li>
<li>Node types</li>
<li>Selecting elements (getElementById, getElementsByClassName, getElementsByName, querySelector, using XPath to select elements)</li>
<li>DOM Manipulation</li>
<li>innerHTML, advantages &amp; criticism</li>
</ul>
</li>
<li>Events
<ul>
<li>Binding &amp; Removing event handlers</li>
<li>Traditional event binding</li>
<li>Capturing &amp; bubbling</li>
<li>Event objects</li>
<li>Event delegation</li>
<li>Firing events</li>
<li>Custom events</li>
<li>What if there&#8217;s no mouse?</li>
</ul>
</li>
<li>Client side storage
<ul>
<li>Cookies via HTTP headers, cookies in JavaScript</li>
<li>Problems with cookies</li>
<li>Web storage (localStorage, sessionStorage)</li>
<li>Client-side databases</li>
</ul>
</li>
<li>BOM
<ul>
<li>The window object, window names</li>
<li>Opening new windows</li>
<li>Cross-window communication</li>
<li>Closing windows, Focusing on windows</li>
<li>Cross-origin window communication</li>
<li><em>location</em> &amp; it&#8217;s components</li>
<li>The <em>history</em>, <em>screen</em> &amp; <em>navigator</em> objects</li>
<li>User Agent strings</li>
<li>Why you shouldn&#8217;t use browser detection</li>
<li>Built-in modal windows (alert, confirm, prompt)</li>
</ul>
</li>
<li>JavaScript &amp; CSS
<ul>
<li>CSS modification (className &amp; classList, inline styles)</li>
<li><em>CSSStyleDeclaration</em> objects</li>
<li>The document.styleSheets collection</li>
<li>Switching stylesheets</li>
<li><em>StyleSheet</em> objects</li>
<li><em>CSSStyleRule</em> objects</li>
<li>Computed style, getting the computed style</li>
</ul>
</li>
<li>Asynchronous execution
<ul>
<li>Timeouts &amp; Intervals</li>
<li>Background workers</li>
</ul>
</li>
<li>Graphics creation (canvas)</li>
<li>A brief mention of WebGL (we also showed the video of Google&#8217;s web based DOOM game)</li>
<li>Best practices
<ul>
<li>When JS is disabled</li>
<li>Feature detection</li>
</ul>
</li>
</ul>
</li>
<li>Regular expressions</li>
<li>Ajax (including data interchange formats, like JSON, other async data transmission techniques, including dynamic script loading &amp; JSONP, usability concerns)</li>
<li>SVG</li>
<li>Server side web development
<ul>
<li>PHP (also covering OOP in PHP extensively)</li>
<li>Database driven websites</li>
<li>State &amp; session management</li>
<li>REST</li>
<li>SOAP</li>
</ul>
</li>
<li>Web application security</li>
</ul>
<p><strong><em>Note:</em></strong><em> For brevity reasons, the lists above do not include introductory stuff such as:</em></p>
<ul>
<li><em>What&#8217;s X?</em></li>
<li><em>A brief history of X</em></li>
<li><em>Why use X?</em></li>
<li><em>etc</em></li>
</ul>
<h4>Lessons learned</h4>
<p>It&#8217;s very hard to momentarily change your mindset and try to imagine that you live in a modern, fully standards-based web development world, where old browsers, proprietary stuff, hacks and compatibility workarounds have no place. A world where IE doesn&#8217;t exist. However, it&#8217;s the world that all our material assumed, for the reasons stated above. And it&#8217;s beautiful, so much that it becomes addictive and makes you hate all these bugs &amp; incompatibilities that we have to face today even more.</p>
<h3 id="homework">Homework</h3>
<p>The students were given 3 assignments throughout the semester, each covering:</p>
<ul>
<li>1st assignment: HTML, CSS, XPath, XSLT</li>
<li>2nd assignment: JavaScript, Ajax, SVG</li>
<li>3rd assignment: Server side web dev + CSS, JavaScript, Ajax</li>
</ul>
<p>These homeworks accounted for 30% of their final grade (10% each), which probably should have been more.</p>
<p>We searched for exercises on these topics from other universities but couldn&#8217;t find anything, so we made our own. I&#8217;ve translated them, in case someone finds them useful, given that there&#8217;s a great shortage of such material in the intertubes. You can get them through the links below, along with their complementary files.</p>
<h4>1st assignment [<a href="http://leaverou.me/wdclass/assignment1_en.pdf" target="_blank">pdf</a>] [<a href="http://leaverou.me/wdclass/assignment1_en.zip" target="_blank">files</a>]</h4>
<ul>
<li> I think 1.A and 1.B are excellent exercises to make the students fully understand how CSS selectors work and avoid them resulting to only use the 4-5 basic ones just because they don&#8217;t understand the rest (like many web developers do). It&#8217;s a pity that many of them resulted to online scripts for the conversion (but luckily it was easy to spot: These answers were way more verbose than the corresponding &#8220;handmade&#8221; ones, and in some cases even incorrect!)</li>
<li>I also think 1.C is an excellent exercise for cascading &amp; inheritance practice. Some of the cases were even quite tricky (for instance, the way specificity works for :not() or how grouping works if one of the selectors is invalid) and treated almost all factors that someone should know to predict which rule &#8230;overrules. It&#8217;s important however that the student justifies the answer, because otherwise they can just test it in a browser and write down the result, without understanding why.</li>
<li>I&#8217;m not sure yet if freeform questions were a good idea, but (hopefully) they got them to practice their critical thinking and do some research (we hadn&#8217;t presented :checked and :lang() in class). We didn&#8217;t expect many to get the 3rd one right, but we were pleasantly surprised.</li>
<li>What I like in 3.A is that I believe it enforces the Separation of Concerns guideline, since they cannot alter the HTML file (something even professionals commonly do to get something done, the quick &amp; dirty way&#8230;) so they <strong>have</strong> to move all presentation to the CSS file. It also contained a quite tricky part: Maintaining state without JavaScript, by utilizing the <strong>:checked</strong> pseudo-class and some combinators (a technique made popular quite recently by <a href="http://www.thecssninja.com/">Ryan Seddon</a>). Obviously, this is not a good way to change views in a photo gallery (too much wasted bandwidth), but it was perfect as a CSS exercise. To my surprise, more than half of the students got it right, which indicates that we probably did a good job explaining CSS Selectors <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </li>
</ul>
<h4>2nd assignment [<a href="http://leaverou.me/wdclass/assignment2_en.pdf" target="_blank">pdf</a>] [<a href="http://leaverou.me/wdclass/assignment2_en.zip" target="_blank">files</a>]</h4>
<ul>
<li>I like exercise 1 because it teaches them how they can take somebody else&#8217;s work, extend it and make it more generic and useful. This is something that&#8217;s frequently done in web development. By the way, the deviation in the solutions was quite interesting. Others had implemented a recursive algorithm, others approached it in an Object Oriented manner and others took the classic iterative route.</li>
<li>Exercise 2 lets them practice event delegation, unobtrusive progressive enhancement via JavaScript, decisions to improve performance (and still, it&#8217;s unbelievable how many students made choices that were obviously terrible performance-wise. I still remember one script that created another DOM element <strong>on every mouseover</strong>!)</li>
<li>Exercise 3 combines many of the technologies they learned in the previous lectures. It also lets them practice their critical thinking by comparing the methods afterwards. Most students picked the CSS method, which would also be my choice, for such a simple bar chart (however, anything rational got full points, I don&#8217;t think there&#8217;s a correct answer here, it depends on many factors).</li>
<li>I like exercise 4 because it introduces them to the concept of writing JavaScript that is intended to be used by other developers, and not just in a particular project (along with 2 perhaps). However, none of the students fully understood what it was about. All of them fired the HTTP request when ajaxForm() was called and most of them also implemented callback() and errorCallback(), which wasn&#8217;t supposed to be their job.</li>
<li>Exercise 5, besides serving well as regular JavaScript practice, it also lets them learn more about cutting edge technologies such as <strong>localStorage</strong>, <strong>Web databases</strong> or <strong>offline web apps</strong>.</li>
</ul>
<h4>3rd assignment [<a href="http://leaverou.me/wdclass/assignment3_en.pdf" target="_blank">pdf</a>] [<a href="http://leaverou.me/wdclass/assignment3_en.zip" target="_blank">files</a>]</h4>
<p>In this assignment, the students practiced in PHP, combined everything else they&#8217;ve learned and understood better how everything clicks together to bring a fully-fledged web application to life. We didn&#8217;t get many submissions, since most students were busy with other assignments these days but most of the ones we got were awesome, I had an extremely hard time picking the best one.</p>
<h4>Lessons learned</h4>
<ul>
<li>Most mistakes are not very original: They tend to appear over and over again in unrelated assignments. Most of them are caused either by ambiguities in the description or because the student didn&#8217;t bother to read all of it. Also, the most frequent excuse for not doing something right is &#8220;it wasn&#8217;t in the description!&#8221;. So, they have to be as detailed as possible, including even stuff that&#8217;s obvious to someone more experienced.</li>
<li>Plagiarism is not a myth, but a real and frequent problem. Students copy from other students, from scripts posted online and from any source they can get their hands on. <img src='http://leaverou.me/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' />  However, only teaching the standards makes it much easier to spot (at least when it comes to copying from the internet) since most scripts posted online have to account for browser incompatibilities.</li>
</ul>
<h3 id="labs">Labs</h3>
<p>We only held 3 hands-on lectures (2 hours each), due to time availability issues of everyone involved in the course. I taught the first 2 and another TA was responsible for the 3rd one. Details below:</p>
<h4>1st lab [<a href="http://leaverou.me/wdclass/lab1/">final result</a>]</h4>
<p>The students had to write an HTML file for the single page personal website of some fictional web developer and then use CSS to style it in a certain way. The process was guided, in order to keep all of them on the same track. The site was carefully designed to demonstrate many key CSS concepts &amp; features at once.</p>
<h4>2nd lab [<a href="http://leaverou.me/wdclass/rating-widget/" target="_blank">final result</a>] [<a href="http://leaverou.me/wdclass/rating-widget/rating-widget.js">JS code</a>] [<a href="http://leaverou.me/wdclass/rating-widget/rating-widget-incomplete.js" target="_blank">incomplete JS code</a>]</h4>
<p>The students were given an HTML and a CSS file and they had to fill in a .js file that had some parts missing (replaced by TODO comments as placeholders) to complete a very simple ajax rating widget.</p>
<h4>Lessons learned</h4>
<ul>
<li>Never provide downloadable slides with the things the students must write by themselves prior to the lecture. They&#8217;ll just copy-paste everything from the pdf, even if they have to fix spacing afterwards. If you absolutely have to, make sure the text is not selectable.</li>
<li>It takes students far more time to write code than you planned for</li>
<li>When the students don&#8217;t understand something, most of them won&#8217;t ask. <img src='http://leaverou.me/wp-includes/images/smilies/icon_sad.gif' alt=':(' class='wp-smiley' />  It&#8217;s best if you personally explain things to anyone having difficulties, but there&#8217;s usually not enough time for that</li>
</ul>
<h3 id="aftermath">Personal aftermath</h3>
<ul>
<li>I found out that I <strong>love</strong> teaching. Successfully helping a student with a problem they had or something they did not understand was sometimes enough to make my day. Preparing material for the course &#8211;although exhausting&#8211; was one of the most interesting and creative things I have ever done. Even the actual teaching is thrilling. It&#8217;s very challenging to try to keep the students&#8217; interest, since most of them will resort to chatting with their buddies instead of paying attention way more easily than professionals would during a conference talk. However, if you manage to do so, it can be quite rewarding.</li>
<li>I <strong>hate</strong> grading. It&#8217;s boring, time-consuming, carries a lot of responsibility and you have to ensure every point you deduct is justified, because you might have to defend your judgement in case a student complains. Sometimes it can also freak you out completely (&#8220;OMGWTF, how could they understand it so wrong?? Why didn&#8217;t they ask?&#8221;) These strips sum it up perfectly (and with a good dose of humor):</li>
</ul>
<p><a href="http://www.phdcomics.com/comics.php?f=1319"><img class="alignnone" title="Grading Rubric" src="http://www.phdcomics.com/comics/archive/phd051910s.gif" alt="Grading Rubric" width="600" height="500" /></a></p>
<p><a href="http://www.phdcomics.com/comics.php?f=1320"><img class="alignnone" title="If only" src="http://www.phdcomics.com/comics/archive/phd052110s.gif" alt="If only" width="600" height="260" /></a></p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2010/07/organizing-a-university-course-on-modern-web-development/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>On CSS counters plus a CSS3 Reversi UI</title>
		<link>http://leaverou.me/2010/04/on-css-counters-plus-a-css3-reversi-ui/</link>
		<comments>http://leaverou.me/2010/04/on-css-counters-plus-a-css3-reversi-ui/#comments</comments>
		<pubDate>Thu, 01 Apr 2010 07:44:02 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Original]]></category>
		<category><![CDATA[CSS Counters]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[User Interfaces in CSS]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=540</guid>
		<description><![CDATA[CSS Counters have a lot more potential than most web developers seem to think. The common use case consists of something like: somecontainer { counter-reset: foocount; } Ε { counter-increment: foocount; } Ε::before { content: counter(foocount) ". "; } commonly used to add numbering to section headings or re-create an &#60;ol&#62;&#8217;s counters in order to [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2010%252F04%252Fon-css-counters-plus-a-css3-reversi-ui%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22On%20CSS%20counters%20plus%20a%20CSS3%20Reversi%20UI%22%20%7D);"></div>
<p>CSS Counters have a lot more potential than most web developers seem to think. The common use case consists of something like:</p>
<pre>somecontainer { counter-reset: foocount; }
Ε { counter-increment: foocount; }
Ε::before { content: counter(foocount) ". "; }</pre>
<p>commonly used to add numbering to section headings or re-create an &lt;ol&gt;&#8217;s counters in order to style them (since browser support for ::marker is ridiculous).</p>
<p>Have you ever thought of applying the counter to <strong>different </strong>elements than the ones being counted? This way we&#8217;re able to count elements and display their total count somewhere with CSS alone! (and with the variety of selectors in CSS3, I see great potential here&#8230;). I&#8217;m referring to something like:</p>
<pre>ul { counter-reset:foo; }
li { counter-increment:foo; }
p::after { content:counter(foo); }</pre>
<p>From my tests, this works flawlessly in Firefox, Safari, Opera and Chrome (I&#8217;ve only checked the latest stable though), <strong>as long as the element that displays the count comes after the elements being counted (in the markup)</strong>.</p>
<p>Another underutilized aspect of CSS counters (well, far less underused than the above, but still) is how we can combine multiple in the same pseudoelement. For instance, to count rows and cells of a table and display the count inside each cell:</p>
<pre>table {
	counter-reset:row;
}

tr {
	counter-increment:row;
	counter-reset:cell;
}

td {
	counter-increment:cell;
}

td::after {
	content:counter(row, upper-alpha) counter(cell);
}</pre>
<p>Which displays counters like A1, A2, A3, B1, B2, B3, etc in the cells. When the content property is more properly implemented, you wouldn&#8217;t even need the last rule.</p>
<p>Last but not least, a <a style="font-size: larger;" href="http://leaverou.me/demos/Reversi/">CSS3 Reversi UI</a> (no images used!) I created a while ago that demonstrates the above (and various other things, like &#8211;finally&#8211; a use case for <strong>:empty</strong> <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' />  ). <em>Looks</em> fine only in Firefox and Opera 10.5, due to lack of support for inset box shadows in Safari and <a href="https://bugs.webkit.org/show_bug.cgi?id=36939" target="_blank">buggy</a> support in Chrome. <em>Works</em> fine in all 4 of them (IE is out of the question anyway).</p>
<p><a href="http://leaverou.me/demos/Reversi/"><img class="alignnone" title="Screenshot from Firefox 3.6" src="http://leaverou.me/demos/Reversi/screenshot.png" alt="Screenshot of the UI" width="512" height="575" /></a></p>
<p>The displayed counts of each player&#8217;s pieces (top right corner) are just CSS counters. Same goes for every cell&#8217;s name. This is mostly a proof of concept, since it&#8217;s impossible to determine if someone won by CSS alone, so we would have to count the pieces in JS too.</p>
<p>As a game it&#8217;s not finalized, you are basically only able to play against yourself and it doesn&#8217;t know when somebody won, so it&#8217;s not very useful or enjoyable. If someone wants to take it up and develop it further be my guest.</p>
<p><strong>Note to avoid confusion: </strong>CSS Counters are <strong>not </strong>CSS 3. They are perfectly valid <strong>CSS 2.1</strong>. The &#8220;CSS3&#8243; in the title (&#8220;CSS3 Reversi&#8221;) is due to other techniques used in it&#8217;s UI.</p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2010/04/on-css-counters-plus-a-css3-reversi-ui/feed/</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>CSS3 structural pseudo-class selector tester</title>
		<link>http://leaverou.me/2010/03/css3-structural-pseudo-class-selector-tester/</link>
		<comments>http://leaverou.me/2010/03/css3-structural-pseudo-class-selector-tester/#comments</comments>
		<pubDate>Sun, 14 Mar 2010 05:50:22 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[CSS3 selectors]]></category>
		<category><![CDATA[educational]]></category>
		<category><![CDATA[nth-child]]></category>
		<category><![CDATA[nth-last-child]]></category>
		<category><![CDATA[nth-last-of-type]]></category>
		<category><![CDATA[nth-of-type]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=521</guid>
		<description><![CDATA[I was doing some research today about how people explain the CSS3 structural* pseudo classes and I stumbled upon this demo by CSS tricks: http://css-tricks.com/examples/nth-child-tester/ I thought the idea is awesome, but lacks a few features: It doesn&#8217;t use the native browser algorithm for selecting the elements. Granted, it&#8217;s not that tough to code your [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2010%252F03%252Fcss3-structural-pseudo-class-selector-tester%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22CSS3%20structural%20pseudo-class%20selector%20tester%22%20%7D);"></div>
<p>I was doing some research today about how people explain the CSS3 structural* pseudo classes and I stumbled upon this demo by CSS tricks: <a href="http://css-tricks.com/examples/nth-child-tester/">http://css-tricks.com/examples/nth-child-tester/</a></p>
<p>I thought the idea is <strong>awesome</strong>, but lacks a few features:</p>
<ul>
<li>It doesn&#8217;t use the native browser algorithm for selecting the elements. Granted, it&#8217;s not that tough to code your own properly, but I trust a browser implementation more (IE doesn&#8217;t support these altogether, so it&#8217;s out of the question anyway).</li>
<li>Doesn&#8217;t allow you to test for nth-last-child, nth-of-type, nth-last-of-type (and especially the last two are a lot harder to understand for most people)</li>
<li>Doesn&#8217;t allow you to add/remove list items to see the effects of the selector with different numbers of elements (especially needed if nth-last-child, nth-of-type, nth-last-of-type were involved)</li>
</ul>
<p>So, I decided to code my own. It allows you to test for all 4 nth-something selectors, supports adding/removing elements (the selected elements update instantly) and uses the native browser implementation to select them (so it won&#8217;t work on IE and old browsers).</p>
<p>Enjoy: <a href="http://leaverou.me/demos/nth.html"><strong style="font-size:x-large">CSS3 structural pseudo-class selector tester</strong></a> <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>*Yes, :root and :empty also belong to those, but are rarely used. All other structural pseudoclasses are actually shortcuts to some particular case of the aforementioned 4 <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2010/03/css3-structural-pseudo-class-selector-tester/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>iPhone keyboard with CSS3 &#8212; no images</title>
		<link>http://leaverou.me/2010/02/iphone-keyboard-with-css3-no-images/</link>
		<comments>http://leaverou.me/2010/02/iphone-keyboard-with-css3-no-images/#comments</comments>
		<pubDate>Thu, 18 Feb 2010 04:25:00 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[@font-face]]></category>
		<category><![CDATA[box-shadow]]></category>
		<category><![CDATA[CSS gradients]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[iPhone]]></category>
		<category><![CDATA[media queries]]></category>
		<category><![CDATA[text-shadow]]></category>
		<category><![CDATA[User Interfaces in CSS]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=490</guid>
		<description><![CDATA[Yeap, this is yet another of those things that make no practical sense but are fun to make just to see whether it can actually be done. It&#8217;s also a proof of the fact that when I have too many things to do, I tend to procrastinate more. Here it is (resize the window to [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2010%252F02%252Fiphone-keyboard-with-css3-no-images%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22iPhone%20keyboard%20with%20CSS3%20--%20no%20images%22%20%7D);"></div>
<p>Yeap, this is yet another of those things that make no practical sense but are fun to make just to see whether it can actually be done. It&#8217;s also a proof of the fact that when I have too many things to do, I tend to procrastinate more. <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> </p>
<p>Here it is (resize the window to get the narrow version <img src='http://leaverou.me/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> ):</p>
<p><a style="font-size: larger;" href="http://leaverou.me/demos/iphone-keyboard/" target="_blank">http://leaverou.me/demos/iphone-keyboard/</a></p>
<p>It should look correct in <strong>Firefox 3.6, Chrome 4 and Safari 4</strong>. It looks best on Firefox 3.6 due to it&#8217;s ability to render subpixel distances, whereas other browsers just round everything to the closest pixel. It also looks best in computers with <strong>Helvetica </strong>installed (it&#8217;s installed by default on macs btw) but it should look sufficiently OK with Arial too, since it&#8217;s a rip-off of Helvetica <img src='http://leaverou.me/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' />  (the only problem with Arial is that the line-height of the buttons with the symbols will be slightly different since the custom font&#8217;s measurements are based on Helvetica Bold) Also, ironically, it doesn&#8217;t look ok in the iPhone!</p>
<p>For those of you that don&#8217;t use one of the aforementioned browsers as your primary and are way too bored to switch (or don&#8217;t even have them installed (!)), here are two screenshots from Firefox 3.6 (nicely cropped to only contain the keyboard):</p>
<div class="wp-caption aligncenter" style="width: 490px"><img title="Screenshot of the wide version" src="http://leaverou.me/demos/iphone-keyboard/css_wide_keyboard.png" alt="" width="480" height="162" /><p class="wp-caption-text">Screenshot of the wide version</p></div>
<div class="wp-caption aligncenter" style="width: 330px"><img title="Screenshot of the narrow version" src="http://leaverou.me/demos/iphone-keyboard/css_narrow_keyboard.png" alt="" width="320" height="215" /><p class="wp-caption-text">Screenshot of the narrow version</p></div>
<p><span id="more-490"></span>As for how it&#8217;s done, as you can easily see, most of it is run-of-the-mill for someone with a decent grasp on CSS3: media queries, CSS gradients, shadows, border-radiuses and RGBA. The only tricky part is the symbols for <kbd>shift</kbd>, <kbd>backspace</kbd> and <kbd>international</kbd>. I have to admit I cheated a bit here: I didn&#8217;t use images, but I used @font-face with a custom font that just contains these 3 symbols. The reasons behind that are that this way I wouldn&#8217;t have to create 2 versions of the symbols (light and dark, for pressed and normal states respectively) and that they are vector, so they scale (try zooming in).</p>
<p>Please note that there&#8217;s no functionality attached to it. It&#8217;s just an interface. I wasn&#8217;t interested at making an on-screen keyboard in general, I was just interested to see if a keyboard visually identical to iPhone&#8217;s is possible with CSS alone. If someone wants to actually use it and/or develop it further, you&#8217;re free to do so, as long as you keep the comment at the start of the css file. <img src='http://leaverou.me/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>
<p>An interesting discussion about this could be &#8220;What would be the ideal markup to semantically style a keyboard?&#8221;. Personally, I just paid attention to the more pragmatic objectives of making the keys focusable, and keeping the complexity of the DOM tree to a minimum, so you might find it semantically wrong (I used a &lt;ul&gt; for the container, &lt;li&gt;s for the rows and &lt;button&gt;s for the keys) &#8212; but what is right actually in this case? Is a keyboard a list or a table of keys? I don&#8217;t think so&#8230;</p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2010/02/iphone-keyboard-with-css3-no-images/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Quickly find the Gravatar that cor­res­ponds to a given email</title>
		<link>http://leaverou.me/2009/12/quickly-find-the-gravatar-that-corresponds-to-a-given-email/</link>
		<comments>http://leaverou.me/2009/12/quickly-find-the-gravatar-that-corresponds-to-a-given-email/#comments</comments>
		<pubDate>Mon, 07 Dec 2009 21:34:23 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[Gravatar]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=470</guid>
		<description><![CDATA[Today I needed to quickly find the Gravatars that corresponded to a bunch of email addresses for some reason (don&#8217;t ask). After a bit of googling and wandering around in Gravatar&#8217;s official site and others, I firgured out it&#8217;s probably much quicker to write a short PHP script for that myself, than keep looking. Here [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F12%252Fquickly-find-the-gravatar-that-corresponds-to-a-given-email%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Quickly%20find%20the%20Gravatar%20that%20cor%C2%ADres%C2%ADponds%20to%20a%20given%20email%22%20%7D);"></div>
<p>Today I needed to quickly find the Gravatars that corresponded to a bunch of email addresses for some reason (don&#8217;t ask). After a bit of googling and wandering around in <a href="http://gravatar.com" target="_blank">Gravatar&#8217;s official site</a> and others, I firgured out it&#8217;s probably much quicker to write a short PHP script for that myself, than keep looking.</p>
<p>Here it is, in case someone ever needs to do something similar: (click on the screenshot)</p>
<p><a href="http://leaverou.me/demos/gravatar.php"><img class="aligncenter size-full wp-image-471" title="Quickly find the Gravatar that cor­res­ponds to a given email" src="http://leaverou.me/wp-content/uploads/2009/12/gravatar.png" alt="Quickly find the Gravatar that cor­res­ponds to a given email" width="450" height="315" /></a></p>
<p>(has anyone noticed my latest love affair with Helvetica/Arial? <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' />  )</p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/12/quickly-find-the-gravatar-that-corresponds-to-a-given-email/feed/</wfw:commentRss>
		<slash:comments>9</slash:comments>
		</item>
		<item>
		<title>Yet another email hiding technique?</title>
		<link>http://leaverou.me/2009/11/yet-another-email-hiding-technique/</link>
		<comments>http://leaverou.me/2009/11/yet-another-email-hiding-technique/#comments</comments>
		<pubDate>Sun, 29 Nov 2009 06:36:26 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[spam]]></category>
		<category><![CDATA[Unicode]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=443</guid>
		<description><![CDATA[While exploring browser-supported Unicode characters, I noticed that apart from the usual @ and . (dot), there was another character that resembled an @ sign (0xFF20 or ＠) and various characters that resembled a period (I think 0&#215;2024 or ․ is closer, but feel free to argue). I&#8217;m wondering, if one could use this as [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F11%252Fyet-another-email-hiding-technique%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Yet%20another%20email%20hiding%20technique%3F%22%20%7D);"></div>
<p>While <a href="http://leaverou.me/2009/11/exploring-browser-supported-unicode-characters-and-a-tweet-shortening-experiment/">exploring browser-supported Unicode characters</a>, I noticed that apart from the usual @ and . (dot), there was another character that resembled an @ sign (0xFF20 or ＠) and various characters that resembled a period (I think 0&#215;2024 or ․ is closer, but feel free to argue).</p>
<p>I&#8217;m wondering, if one could use this as another way of email hiding. It&#8217;s almost as easy as the foo [at] bar [dot] com technique, with the advantage of being far less common (I&#8217;ve never seen it before, so there&#8217;s a high chance that spambot developers haven&#8217;t either) and I think that the end result is more easily understood by newbies. To encode <a class="linkification-ext" title="Linkification: mailto:foo@bar.com" href="mailto:foo@bar.com">foo@bar.com</a> this way, we&#8217;d use (in an html page):</p>
<pre>foo&amp;#xFF20;bar&amp;#x2024;com</pre>
<p>and the result is: <span style="font-size:1.5em">foo＠bar․com</span></p>
<p>I used that technique on the <a href="http://leaverou.me/demos/ligatweet/#conversions">ligatweet page</a>. Of course, if many people start using it, I guess spambot developers will notice, so it won&#8217;t be a good idea any more. However, for some reason I don&#8217;t think it will ever become that mainstream <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> </p>
<p>By the way, if you&#8217;re interested in other ways of email hiding, <a href="http://www.csarven.ca/hiding-email-addresses">here&#8217;s an extensive article on the subject</a> that I came across after a quick googlesearch (to see if somebody else came up with this first &#8212; I didn&#8217;t find anything).</p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/11/yet-another-email-hiding-technique/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Exploring browser-supported Unicode characters and a tweet shortening experiment</title>
		<link>http://leaverou.me/2009/11/exploring-browser-supported-unicode-characters-and-a-tweet-shortening-experiment/</link>
		<comments>http://leaverou.me/2009/11/exploring-browser-supported-unicode-characters-and-a-tweet-shortening-experiment/#comments</comments>
		<pubDate>Sun, 29 Nov 2009 06:10:20 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Original]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[ligatweet]]></category>
		<category><![CDATA[twitter]]></category>
		<category><![CDATA[Unicode]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=438</guid>
		<description><![CDATA[I recently wanted to post something on twitter that was just slightly over the 140 chars limit and I didn&#8217;t want to shorten it by cutting off characters (some lyrics from Pink Floyd&#8217;s &#8220;Hey You&#8221; that expressed a particular thought I had at the moment &#8212; it would be barbaric to alter Roger Waters&#8217; lyrics [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F11%252Fexploring-browser-supported-unicode-characters-and-a-tweet-shortening-experiment%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Exploring%20browser-supported%20Unicode%20characters%20and%20a%20tweet%20shortening%20experiment%22%20%7D);"></div>
<p>I recently wanted to post something on twitter that was just slightly over the 140 chars limit and I didn&#8217;t want to shorten it by cutting off characters (some lyrics from Pink Floyd&#8217;s &#8220;Hey You&#8221; that expressed a particular thought I had at the moment &#8212; it would be barbaric to alter Roger Waters&#8217; lyrics in any way, wouldn&#8217;t it? <img src='http://leaverou.me/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> ). I always knew there were some ligatures and digraphs in the Unicode table, so I thought that these might be used to shorten tweets, not only that particular one of course, but any tweet. So I wrote <a title="Browser supported unicode characters" href="http://leaverou.me/scripts/unicode.html" target="_blank">a small script</a> (warning: very rough around the edges) to explore the Unicode characters that browsers supported, find the replacement pairs and build the tweet shortening script (I even thought of a name for it: ligatweet, LOL I was never good at naming).<span id="more-438"></span></p>
<p>My observations were:</p>
<ul>
<li>Different browsers support different Unicode characters. I think Firefox has the best support (more characters) and Chrome the worst. By the way, it&#8217;s a shame that Chrome doesn&#8217;t support the Braille characters.</li>
<li>The appearance of the same characters, using the same font has huge differences across browsers. A large number of glyphs are completely different. This is very apparent on dingbats (around 0&#215;2600-0&#215;2800).</li>
<li>For some reason unknown to me, hinting suffers a great deal in the least popular characters (common examples are the unit ligatures, like ㏈ or ㎉). Lots of them looked terribly unlegible and pixelated in small sizes (and only in small sizes!!). Typophiles feel free to correct me if I&#8217;m mistaken, but judging by my brief experience with font design, I don&#8217;t think bad hinting (or no hinting at all) can do that sort of thing to a glyph. These characters appeared without any anti-aliasing at all! Perhaps it has to do with Cleartype or Windows (?). If anyone has any information about the cause of this issue, I would be greatly interested.</li>
<li>It&#8217;s amazing what there&#8217;s in the Unicode table! There are many dingbats and various symbols in it, and a lot of them work cross browser! No need to be constrained by the small subset that html entities can produce!</li>
</ul>
<p>The tweet shortening script is here: <a class="linkification-ext" title="Linkification: http://leaverou.me/demos/ligatweet/" href="http://leaverou.me/demos/ligatweet/">http://leaverou.me/demos/ligatweet/</a></p>
<p>I might as well write a bookmarklet in the future. However, I was a bit disappointed to find out that even though I got a bit carried away when picking the replacement pairs, the gains are only around 6-12% for most tweets (case sensitive, of course case insensitive results in higher savings, but the result makes you look like a douchebag), but I&#8217;m optimistic that as more pairs get added (feel free to suggest any, or improvements on the current ones) the savings will increase dramatically. And even if they don&#8217;t I really enjoyed the trip.</p>
<p>Also, exploring the Unicode table gave me lots of ideas about scripts utilizing it, some of which I consider far more useful than ligatweet (although I&#8217;m not sure if I&#8217;ll ever find the time to code them, even ligatweet was finished because I had no internet connection for a while tonight, so I couldn&#8217;t work and I didn&#8217;t feel like going to sleep)</p>
<p>By the way, In case you were wondering, I didn&#8217;t post the tweet that inspired me to write the script. After coding for a while, It just didn&#8217;t fit my mood any more. <img src='http://leaverou.me/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/11/exploring-browser-supported-unicode-characters-and-a-tweet-shortening-experiment/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
		<item>
		<title>A different approach to elastic textareas</title>
		<link>http://leaverou.me/2009/11/a-different-approach-to-elastic-textareas/</link>
		<comments>http://leaverou.me/2009/11/a-different-approach-to-elastic-textareas/#comments</comments>
		<pubDate>Fri, 13 Nov 2009 22:06:22 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[usability]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=425</guid>
		<description><![CDATA[I loved elastic textareas since the very first moment I used one (at facebook obviously). They let you save screen real estate while at the same time they are more comfortable for the end user. It&#8217;s one of the rare occasions when you can have your UI cake and eat it too! However, I never [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F11%252Fa-different-approach-to-elastic-textareas%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22A%20different%20approach%20to%20elastic%20textareas%22%20%7D);"></div>
<p>I loved elastic textareas since the very first moment I used one (at facebook obviously). They let you save screen real estate while at the same time they are more comfortable for the end user. It&#8217;s one of the rare occasions when you can have your UI cake and eat it too!</p>
<p>However, I never liked the implementation of the feature. In case you never wondered how it&#8217;s done, let me explain it in a nutshell: All elastic textarea scripts (or at least all that I know of) create a hidden (actually, absolutely positioned and placed out of the browser window) div, copy some CSS properties from the textarea to it (usually padding, font-size, line-height, font-family, width and font-weight) and whenever the contents of the textarea change they copy them to the hidden div and measure it&#8217;s dimensions. It might be good enough for facebook, where the styling of those textareas is fairly simple and consistent throughout the site, or any other particular site, but as a generic solution? I never liked the idea.<span id="more-425"></span></p>
<p>So, I tried to explore a different approach. As Andrea Giammarchi <a href="http://webreflection.blogspot.com/2009/11/google-closure-im-not-impressed.html" target="_blank">recently wrote</a><em> &#8220;This is almost intrinsic, as developers, in our DNA: we spot some interesting concept? We rewrite it from scratch pretending we are doing it better!&#8221;</em> and I&#8217;m no exception (although in this case I don&#8217;t think I did it better, I just think it has potential). The basic idea is quite naive, but it works quite well in most browsers (Internet Explorer being the black sheep as usual): Test if the textarea is scrollable, and if so, increase it&#8217;s <code>rows</code> attribute and try again. If it&#8217;s not scrollable initially, try decreasing it&#8217;s <code>rows</code> attribute until it becomes scrollable (and then ++ it).</p>
<p>It works flawlessly on Firefox and quite well on Safari, Chrome and Opera (it just slightly twitches when it enlarges in those). Stupid Internet Explorer though repaints too many times, causing a flicker at the bottom when the user is typing, something really disturbing, so I can&#8217;t consider the script anything above <strong>experimental </strong>at the moment. I&#8217;m just posting it in case anyone has an idea of how to fix the aforementioned issues, because apart from those it has quite a few advantages:</p>
<ul>
<li>Should work with any CSS styles</li>
<li><strong>No</strong> library requirements (unlike all the others I know of)</li>
<li>Only<strong> 800 bytes</strong> minified (2.4KB originally)</li>
</ul>
<p>So, here it is:</p>
<ul>
<li><a href="http://leaverou.me/scripts/elastic-textarea/">Demo</a></li>
<li><a href="http://leaverou.me/scripts/elastic-textarea/elastic-textarea.js">elastic-textarea.js</a></li>
<li><a href="http://leaverou.me/scripts/elastic-textarea/elastic-textarea-min.js">elastic-textarea-min.js</a></li>
</ul>
<p><em>For the record, I <strong>don&#8217;t </strong>think that a script <strong>should </strong>be needed for things like that. This looks like something that should be handled by CSS alone. We basically want the height of an element to adjust as necessary for it&#8217;s contents to fit. We already use CSS for these things on other elements, why not form controls as well?</em></p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/11/a-different-approach-to-elastic-textareas/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>New version of rgba.php is out!</title>
		<link>http://leaverou.me/2009/10/new-version-of-rgba-php-is-out/</link>
		<comments>http://leaverou.me/2009/10/new-version-of-rgba-php-is-out/#comments</comments>
		<pubDate>Sun, 25 Oct 2009 07:44:57 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[colors]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[CSS3 values]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[RGBA]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=418</guid>
		<description><![CDATA[It&#8217;s been a while since I posted my little server-side solution for cross-browser RGBA colors (in a nutshell: native rgba for the cool browsers that support it, a PHP-generated image for those that don&#8217;t). For features, advantages, disadvantages etc, go see the original post. In this one I&#8217;ll only discuss the new version. So, since [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F10%252Fnew-version-of-rgba-php-is-out%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22New%20version%20of%20rgba.php%20is%20out%21%22%20%7D);"></div>
<p><a href="http://leaverou.me/2009/02/bulletproof-cross-browser-rgba-backgrounds/">It&#8217;s been a while since I posted my little server-side solution for cross-browser RGBA colors</a> (in a nutshell: native rgba for the cool browsers that support it, a PHP-generated image for those that don&#8217;t). For features, advantages, disadvantages etc, go see the original post. In this one I&#8217;ll only discuss the new version.</p>
<p>So, since it&#8217;s release I&#8217;ve received suggestions from many people regarding this script. Some other ideas were gathered during troubleshooting issues that some others faced while trying to use it. I hope I didn&#8217;t forget anything/anyone <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> <span id="more-418"></span></p>
<h3>Changelog (+credits):</h3>
<ol>
<li>You may now specify the size of the generated image (thanks <em><strong><a href="http://leaverou.me/2009/02/bulletproof-cross-browser-rgba-backgrounds/#comment-122">Sander Arts</a></strong></em>!)</li>
<li>If the PHP version is below 5.1.7 the call to imagepng() uses 2 parameters instead of 4, to workaround the bug found by <em><strong><a href="http://leaverou.me/2009/02/bulletproof-cross-browser-rgba-backgrounds/#comment-103">Bridget</a></strong></em> (thanks <strong><em>Chris Neale</em></strong> for suggesting the use of phpversion()!)</li>
<li>Added error_reporting() to only allow for fatal errors and parse errors to go through (I should had done this anyway but I completely forgot). This solves an issue that <strong><em>Erin Doak</em></strong> pointed out, since they had set up notices to be displayed and even a reference to an undefined index made the whole script collapse.</li>
<li><em><strong>Mariotti Raffaele </strong></em>pointed out that apache_request_headers() was not defined in all PHP installations. After looking into it a bit, I found out that it&#8217;s available only when PHP is installed as an Apache module. After some more research it turned out that the only way to get the If-Modified-Since header otherwise is an .htaccess, so I  ruled that out (It would complicate the workaround I think and I doubt all hosts allow .htaccess (?). On the other hand, an .htacess would also allow for some URL rewriting goodness&#8230; Hmmm&#8230; Should I consider this?). So, if the function is not available, it serves the file with an 200 response code every time, instead of just sending a 304 response when the If-Modified-Since header is present.</li>
<li><a href="http://leaverou.me/2009/10/new-version-of-rgba-php-is-out/#comment-893"><strong>Igor Zevaka</strong></a> for pointing out that the Expires header wasn&#8217;t a valid HTTP date.</li>
</ol>
<h3>Links</h3>
<p><strong><a href="http://leaverou.me/wp-content/uploads/2009/10/rgba.zip">rgba.php</a></strong></p>
<p><strong><a href="http://leaverou.me/wp-content/themes/leaverou/images/rgba.php?r=255&amp;g=0&amp;b=100&amp;a=80">Demo</a></strong></p>
<p>Enjoy <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  and please report any bugs!</p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/10/new-version-of-rgba-php-is-out/feed/</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Bevels in CSS3</title>
		<link>http://leaverou.me/2009/07/bevels-in-css3/</link>
		<comments>http://leaverou.me/2009/07/bevels-in-css3/#comments</comments>
		<pubDate>Thu, 23 Jul 2009 19:24:22 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[box-shadow]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[CSS3 properties]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=368</guid>
		<description><![CDATA[Yeah, yeah I know, bevels are soooo 1996. And I agree. However, it&#8217;s always good to know the capabilities of your tools. Talented designers will know when it&#8217;s suitable to use a certain effect and incapable ones will abuse whatever is given to them, so after a lot of thought, I decided to blog about [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F07%252Fbevels-in-css3%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Bevels%20in%20CSS3%22%20%7D);"></div>
<p>Yeah, yeah I know, bevels are soooo 1996. And I agree. However, it&#8217;s always good to know the capabilities of your tools. Talented designers will know when it&#8217;s suitable to use a certain effect and incapable ones will abuse whatever is given to them, so after a lot of thought, I decided to blog about my discovery.<span id="more-368"></span></p>
<p>Even though not directly mentioned in the spec, CSS3 is capable of easily creating a bevel effect on any element. Moreover, if the element has rounded corners, the bevel follows that as well. Before explaining the technique, let&#8217;s think about how a bevel actually gets drawn. It&#8217;s essentially two inner shadows, that when combined, create the illusion of a 3d appearance: a light one from the top left corner and a dark one from the bottom right corner. CSS3 includes the ability to create inner shadows, if you specify the keyword &#8220;inset&#8221; in the box-shadow declaration (currently only supported by Firefox 3.5). Moreover, the CSS3 spec allows for multiple box shadows on the same elements.</p>
<p>Now, let&#8217;s examine an example (only works in Firefox 3.5):</p>
<pre>button {
 background:#f16;
 color:white;
 padding:6px 12px 8px 12px;
 border:none;
 font-size:18px;
 -moz-border-radius:10px;
 -moz-box-shadow: -2px -2px 10px rgba(0,0,0,.25) inset, 2px 2px 10px white inset;
}</pre>
<p>which produces this result:</p>
<p><img class="alignnone size-full wp-image-369" title="css3bevel" src="http://leaverou.me/wp-content/uploads/2009/07/css3bevel.png" alt="css3bevel" width="110" height="52" /></p>
<p>If we want, we can also create a &#8220;pressed&#8221; button state, in a similar fashion:</p>
<pre>button:active {
 -moz-box-shadow: 2px 2px 10px rgba(0,0,0,.25) inset, -2px -2px 10px white inset;
 padding:7px 11px 7px 13px;
}

button::-moz-focus-inner { border: 0; }</pre>
<p>which produces this pressed state:</p>
<p><img class="alignnone size-full wp-image-370" title="css3bevel_pressed" src="http://leaverou.me/wp-content/uploads/2009/07/css3bevel_pressed.png" alt="css3bevel_pressed" width="116" height="56" /></p>
<p>See it in action here (only for Firefox 3.5): <a class="linkification-ext" title="Linkification: http://leaverou.me/demos/css3bevel.html" href="http://leaverou.me/demos/css3bevel.html">http://leaverou.me/demos/css3bevel.html</a></p>
<p>Of course, if implemented in a real world website, you should also add the -webkit- and -o- CSS3 properties to provide a closer effect for the other browsers and be ready for the time when the ones that aren&#8217;t implemented yet in them will finally make it (for instance, when Webkit implements inset box shadows, it will work in it as well).</p>
<p>Enjoy <strong>responsibly</strong>. <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> </p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/07/bevels-in-css3/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Cross-browser imageless linear gradients v2</title>
		<link>http://leaverou.me/2009/04/cross-browser-imageless-linear-gradients-v2/</link>
		<comments>http://leaverou.me/2009/04/cross-browser-imageless-linear-gradients-v2/#comments</comments>
		<pubDate>Fri, 24 Apr 2009 00:47:57 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[gradient]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=316</guid>
		<description><![CDATA[A while ago, I posted a script of mine for creating 2-color cross-browser imageless linear gradients. As I stated there, I needed them for a color picker I have to create. And even though 2-color gradients are sufficient for most components, in most color spaces, I had forgotten an important one: Hue. You can&#8217;t represent [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F04%252Fcross-browser-imageless-linear-gradients-v2%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Cross-browser%20imageless%20linear%20gradients%20v2%22%20%7D);"></div>
<p>A while ago, I posted <a title="Cross-browser imageless linear gradients v1" href="http://leaverou.me/2009/03/cross-browser-imageless-linear-gradients/" target="_blank">a script of mine for creating 2-color cross-browser imageless linear gradients</a>. As I stated there, I needed them for a color picker I have to create. And even though 2-color gradients are sufficient for most components, in most color spaces, I had forgotten an important one: <strong>Hue</strong>. You can&#8217;t represent Hue with a 2-color gradient! So, I had to revise the script, and make it able to produce linear gradients of more than 2 colors. Furthermore, I needed to be able to specify a fully transparent color as one of the gradient colors, in order to create the photoshop-like 2d plane used by the picker (and no, a static image background like the one used in most JS color pickers wouldn&#8217;t suffice, for reasons irrelevant with this post). I hereby present you <em>Cross-browser, imageless, linear gradients <strong>v2</strong></em>!</p>
<p><span id="more-316"></span>The API has stayed just the same, with the following differences:</p>
<ul>
<li>You may specify the keyword &#8220;transparent&#8221; instead of a #RRGGBB color (that was such a pain to implement btw!).</li>
<li>When creating a Gradient object, color strings are now defined in an array. Example:
<pre>var g = new Gradient(200, 100, ['#000000', '#ff1166', '#23ff46'], true);</pre>
</li>
<li>When calling <code>g.paint()</code> it now takes <strong>2</strong> arguments instead of 3: The new color array (or null if you don&#8217;t want that to change) and the direction (true for vertical, false for horizontal). For example:
<pre>g.paint(['#000000', '#ff1166', '#23ff46'], true);</pre>
</li>
<li>2 new methods have been added: <code>g.setColorAt(index, color)</code> and <code>g.direction(newDirection)</code>. The first allows you to set a particular gradient color (index starting from 0) and the second to alter or toggle the direction (if you specify a direction parameter, you set the direction, if you call it with no parameters, it toggles from horizontal to vertical).</li>
<li>The fields <code>g.startColor</code> and <code>g.endColor</code> have been replaced by the array <code>g.colors</code>.</li>
</ul>
<p><strong>Update</strong>: <strong>v2.0.1</strong> Fixed a small bug with the &#8216;transparent&#8217; keyword that affected multi-color gradients in browsers != IE when the transparent color wasn&#8217;t first or last.</p>
<p>Enjoy:</p>
<p><a href="http://leaverou.me/scripts/gradient2/gradient.js" target="_blank">gradient.js (5.1 KB)</a></p>
<p><a href="http://leaverou.me/scripts/gradient2/gradient-min.js" target="_blank">gradient-min.js (2.7 KB)</a></p>
<p><a href="http://leaverou.me/scripts/gradient2/" target="_blank">Test page</a></p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/04/cross-browser-imageless-linear-gradients-v2/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Java pretty dates</title>
		<link>http://leaverou.me/2009/04/java-pretty-dates/</link>
		<comments>http://leaverou.me/2009/04/java-pretty-dates/#comments</comments>
		<pubDate>Tue, 21 Apr 2009 18:05:52 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[Java]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=312</guid>
		<description><![CDATA[First of all, sorry for not posting as frequently as before. I&#8217;m feverishly working on a new project with a really tight deadline and I don&#8217;t have as much time as I previously did. For reasons that are irrelevant to this post, I have to write lots of Java code. So, sorry if I disappoint [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F04%252Fjava-pretty-dates%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Java%20pretty%20dates%22%20%7D);"></div>
<p>First of all, sorry for not posting as frequently as before. I&#8217;m feverishly working on a new project with a really tight deadline and I don&#8217;t have as much time as I previously did.</p>
<p>For reasons that are irrelevant to this post, I have to write lots of Java code. So, sorry if I disappoint my fellow readers, but this post isn&#8217;t about JavaScript or CSS, it&#8217;s about Java. I wanted to display &#8220;pretty dates&#8221; (a bit like Twitter&#8217;s, for example <em>&#8220;yesterday&#8221;</em>, <em>&#8220;5 minutes ago&#8221;</em>, <em>&#8220;last year&#8221;</em> and so on) in a few places and I couldn&#8217;t find a Java implementation, so I decided to code my own.<span id="more-312"></span></p>
<p>For anyone that might need it, here it is:</p>
<pre>import java.util.Date;

/**
 * Class for human-readable, pretty date formatting
 * @author Lea Verou
 */
public class PrettyDate
{
	private Date date;

	public PrettyDate() {
		this(new Date());
	}

	public PrettyDate(Date date) {
		this.date = date;
	}

	public String toString() {
		long	current = (new Date()).getTime(),
			timestamp = date.getTime(),
			diff = (current - timestamp)/1000;
		int	amount = 0;
		String	what = "";

		/**
		 * Second counts
		 * 3600: hour
		 * 86400: day
		 * 604800: week
		 * 2592000: month
		 * 31536000: year
		 */

		if(diff &gt; 31536000) {
			amount = (int)(diff/31536000);
			what = "year";
		}
		else if(diff &gt; 31536000) {
			amount = (int)(diff/31536000);
			what = "month";
		}
		else if(diff &gt; 604800) {
			amount = (int)(diff/604800);
			what = "week";
		}
		else if(diff &gt; 86400) {
			amount = (int)(diff/86400);
			what = "day";
		}
		else if(diff &gt; 3600) {
			amount = (int)(diff/3600);
			what = "hour";
		}
		else if(diff &gt; 60) {
			amount = (int)(diff/60);
			what = "minute";
		}
		else {
			amount = (int)diff;
			what = "second";
			if(amount &lt; 6) {
				return "Just now";
			}
		}

		if(amount == 1) {
			if(what.equals("day")) {
				return "Yesterday";
			}
			else if(what.equals("week") || what.equals("month") || what.equals("year")) {
				return "Last " + what;
			}
		}
		else {
			what += "s";
		}

		return amount + " " + what + " ago";
	}
}</pre>
<p>Hope someone finds it useful. <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/04/java-pretty-dates/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Better usability in 5 minutes</title>
		<link>http://leaverou.me/2009/04/better-usability-in-5-minutes/</link>
		<comments>http://leaverou.me/2009/04/better-usability-in-5-minutes/#comments</comments>
		<pubDate>Fri, 10 Apr 2009 00:36:06 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Original]]></category>
		<category><![CDATA[CSS]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[CSS3 selectors]]></category>
		<category><![CDATA[CSS3 transitions]]></category>
		<category><![CDATA[usability]]></category>
		<category><![CDATA[Webkit]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=296</guid>
		<description><![CDATA[In this post I&#8217;m going to share some tips to increase a site&#8217;s usability that are very quick to implement. Not all of them are cross-browser, but they are the icing on the cake anyway, nobody would mind without them. 1. Make buttons and button-like links appear pressed This is a personal favorite. When you [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F04%252Fbetter-usability-in-5-minutes%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Better%20usability%20in%205%20minutes%22%20%7D);"></div>
<p>In this post I&#8217;m going to share some tips to increase a site&#8217;s usability that are very quick to implement. Not all of them are cross-browser, but they are the icing on the cake anyway, nobody would mind without them.<span id="more-296"></span></p>
<h3>1. Make buttons and button-like links appear pressed</h3>
<p>This is a personal favorite. When you use CSS to style a button, or when you use an image (either as a background image or in the &lt;img /&gt; tag) to depict a fancy button, it will remain the same when being pressed in some or all browsers (depending on the case). You can use this easy trick to let the user know that he actually clicked something that is, indeed, clickable:</p>
<pre>.mybutton:active {
	position:relative;
	top: 1px;
	left: 1px;
}</pre>
<p>which actually moves the button 1 pixel to the right and 1 pixel to the bottom when it&#8217;s being clicked. Try it, it&#8217;s actually quite convincing.</p>
<p>Other, equally quick options are: making the border inset, giving to the text a text-indent of 1px, reversing a gradient background (if you already use the reversed version somewhere else in the site, it is quick since you don&#8217;t have to use an image editor just for that), or a combination of them.</p>
<h3>2. Smooth transitions</h3>
<p>This is a webkit-only tip, but as I said, it&#8217;s just the icing on the cake, so who cares? If a smooth transition is crucial to your design, by all means, write a script for that or use a library. If you were planning to go the CSS-only way anyway, this will significantly increase the user experience for webkit users.</p>
<p>Let&#8217;s suppose that the links in your page are normally blue, and red on hover. To make the transition from blue to red smooth for webkit users, only 2 lines are needed in the CSS:</p>
<pre>a {
	color:blue;
<strong>	-webkit-transition-property: color;
	-webkit-transition-duration: 1s;</strong>
}

a:hover {
	color:red;
}</pre>
<p>The first one (<code>-webkit-transition-property</code>) tells the browser which CSS property to smoothly transition and the second one (<code>-webkit-transition-duration</code>)<strong></strong><strong> </strong>how long you want the whole effect to last. It&#8217;s important to place those in the normal CSS rule and not the one with the :hover pseudoclass, because otherwise there will be no transition when the user mouses out of the element.</p>
<h3>3. Add dingbats to buttons that depict their functionality</h3>
<p>We all know that most browsers don&#8217;t like dingbat-only fonts. However, there are some dingbats that are available in most web-safe unicode fonts. For instance, review the following examples:</p>
<h4>Without dingbats:</h4>
<p><button>Next</button><br />
<button>Previous</button><br />
<button>Done</button><br />
<button>Favorite</button></p>
<h4>With dingbats:</h4>
<p><button>Next →</button><br />
<button>← Previous</button><br />
<button>✔ Done</button><br />
<button>♥ Favorite</button></p>
<p>There are named html entities for some of them, others have to be used by their hex unicode index like &amp;#xABCD;  (you have to test the last ones a lot, since not all are web-safe enough).</p>
<p>You can find many such dingbats with their unicode hex codes in <a class="linkification-ext" title="Linkification: http://www.copypastecharacter.com/" href="http://www.copypastecharacter.com/">http://www.copypastecharacter.com/</a> and http://www.alanwood.net/unicode/dingbats.html.</p>
<p>Of course, if you have the time, by all means, use normal icons. If you don&#8217;t however, I find symbols to be a handy alternative. Sometimes I also use them as icon placeholders in work in progress until I find the time to design real icons.</p>
<h3>4. Zebra rows</h3>
<p>This won&#8217;t work on IE and Firefox 3. You can increase readability of tables and some types of lists by slightly alternating the background color of the rows. You&#8217;ve probably seen this effect numerous times and it&#8217;s usually done via JavaScript or the server side code that generates the table. You can quickly do it with plain CSS3 however, if you don&#8217;t mind it not working in IE and older browser versions or don&#8217;t have the time for a complete cross-browser solution:</p>
<pre>table.stats tr {
	background:white;
}

table.stats tr:nth-child(odd) {
	background:#f4f4f4;
}</pre>
<h3>5. Highlight the current target</h3>
<p>This won&#8217;t work in IE and older browser versions. If a particular page has lots of content, navigable by anchors (for example a FAQ page), you can use the CSS3 :target pseudo-class to let the user know where they landed:</p>
<pre>h3:target {
	background:#FFFBCC;
}</pre>
<p>The h3 will only get a #FFFBCC background when it&#8217;s actually the landing point for the user. For example, if it has the id &#8220;foo&#8221;, it will get an #FFFBCC background when the user navigates to #foo.</p>
<h3>That&#8217;s all folks</h3>
<p>Did it actually take more than 5 minutes? <img src='http://leaverou.me/wp-includes/images/smilies/icon_wink.gif' alt=';)' class='wp-smiley' /> </p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/04/better-usability-in-5-minutes/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Cross browser, imageless linear gradients</title>
		<link>http://leaverou.me/2009/03/cross-browser-imageless-linear-gradients/</link>
		<comments>http://leaverou.me/2009/03/cross-browser-imageless-linear-gradients/#comments</comments>
		<pubDate>Fri, 20 Mar 2009 07:51:53 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[canvas]]></category>
		<category><![CDATA[gradient]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=249</guid>
		<description><![CDATA[I have to write a color picker in the near future and I wanted it to have those little gradients on top of the sliders that show you the effect that a slider change will have on the selected color. Consequently, I needed to create imageless gradients, in order to easily change them. My very [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F03%252Fcross-browser-imageless-linear-gradients%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Cross%20browser%2C%20imageless%20linear%20gradients%22%20%7D);"></div>
<p>I have to write a color picker in the near future and I wanted it to have those little gradients on top of the sliders that show you the effect that a slider change will have on the selected color. Consequently, I needed to create imageless gradients, in order to easily change them. My very first thought was creating many div or span elements in order to show the gradient. I rejected it almost instantly, for ovbious reasons (*cough* performance *cough*). My second thought was SVG for the proper browsers, and gradient filters for IE. As it turned out,<a href="http://wiki.svg.org/Inline_SVG" target="_blank"> inline SVG was too much of a hassle</a> and I didn&#8217;t want to use Data URIs. My final thought was canvas for the proper browsers and gradient filters for IE.</p>
<p>Since I consider such a script very entertaining, I didn&#8217;t google it at all, I started coding right away. Time to have fun! <img src='http://leaverou.me/wp-includes/images/smilies/icon_biggrin.gif' alt=':D' class='wp-smiley' />  After finishing it though, I googled it just out of curiosity and didn&#8217;t like the other solutions much (either the solution itself, or the code), so I decided to post it in case it helps someone. I also made a little test page, so that you may test out how it works. <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> <span id="more-249"></span></p>
<p>The script is a class for the creation of linear 2-color gradients in any browser. It&#8217;s used like this:</p>
<pre>var g = new Gradient(200, 100, '#000000', '#ff1166', true);
document.body.appendChild(g.canvas);</pre>
<p>You can create and manipulate the Gradient object at any point (during or after DOM parsing) but you have to insert the element somewhere in the DOM after the DOM has finished parsing (which is common sense).</p>
<p>All the parameters in the constructor are optional and can be manipulated later. Their order is <code>width, height, startColor, endColor, vertical</code>.</p>
<p>Some notes:</p>
<ul>
<li>Its object oriented and doesn&#8217;t throw any strict warnings</li>
<li>Tested in IE6, IE7, IE8, Firefox 3, Safari 4b and Opera 9.6. Probably works with older versions of Firefox, Opera and Safari as well (as long as they support &lt;canvas&gt;), I&#8217;m just not able to test in them currently.</li>
<li>All it&#8217;s methods return the object, so they can be chained.</li>
<li>You can modify it to support RGBA as well, but you&#8217;d have to use a different format for IE (extended hex) and a different one for the proper browsers. I didn&#8217;t need that and it would make the script unnecessarily complex, so I didn&#8217;t implement it.</li>
</ul>
<p>Limitations (<strong>all</strong> these limitations are enforced by IE&#8217;s gradient filter):</p>
<ul>
<li>Only does linear gradients</li>
<li>The gradient can be either vertical or horizontal. No other angles.</li>
<li>The only color format supported is #RRGGBB.</li>
</ul>
<h3>Properties</h3>
<h4>canvas (HTMLElement)</h4>
<p>The HTML Element that is being used to render the gradient. Either a &lt;canvas&gt; or a &lt;span&gt;. You have to use it at least once, in order to insert the element in the DOM. I preferred not to do this automatically, since it would be too restrictive.</p>
<h4>startColor (String)</h4>
<p>The current start color of the gradient.</p>
<h4>endColor (String)</h4>
<p>The current end color of the gradient.</p>
<h4>vertical (Boolean)</h4>
<p>True if the gradient is vertical, false if it&#8217;s horizontal.</p>
<h4>width (Number)</h4>
<p>The width of the gradient in pixels</p>
<h4>height (Number)</h4>
<p>The height of the gradient in pixels</p>
<h3>Methods</h3>
<h4>paint(startColor, endColor, vertical)</h4>
<p>Used to change the colors and/or the orientation of the gradient. All parameters are optional.</p>
<h4>resize(width, height)</h4>
<p>Used to change the size of the gradient. Both parameters are optional.</p>
<h4>flip()</h4>
<p>Reverses the gradient (swaps endColor with startColor)</p>
<h4>rotate()</h4>
<p>Rotates the gradient by 90 degrees clockwise (should I add CCW too?)</p>
<h3>Download</h3>
<p><a href="http://leaverou.me/scripts/gradient/gradient.js" target="_blank">gradient.js (2.7 KB)</a></p>
<p><a href="http://leaverou.me/scripts/gradient/gradient-min.js" target="_blank">gradient-min.js (1.4 KB)</a></p>
<p><a href="http://leaverou.me/scripts/gradient/" target="_blank">Test page</a></p>
<p>Hope you find it useful <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/03/cross-browser-imageless-linear-gradients/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>Mockup viewer bookmarklet</title>
		<link>http://leaverou.me/2009/03/mockup-viewer-bookmarklet/</link>
		<comments>http://leaverou.me/2009/03/mockup-viewer-bookmarklet/#comments</comments>
		<pubDate>Wed, 18 Mar 2009 21:23:37 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[bookmarklets]]></category>
		<category><![CDATA[JavaScript]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=241</guid>
		<description><![CDATA[I usually view mockups in a browser, so that the impression I get is as close as possible to reality (I learned this the hard way: A mockup that seemed great in the neutral and minimalistic environment of a picture viewer, ended up looking way too fancy when viewed in a browser, something that I [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F03%252Fmockup-viewer-bookmarklet%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Mockup%20viewer%20bookmarklet%22%20%7D);"></div>
<p>I usually view mockups in a browser, so that the impression I get is as close as possible to reality (I learned this the hard way: A mockup that seemed great in the neutral and minimalistic environment of a picture viewer, ended up looking way too fancy when viewed in a browser, something that I realized after having worked for 6 months on the site). If you do the same, I&#8217;m sure you&#8217;ll feel my pain: Every time I do that, I have to carefully scroll down just as much as to hide the margin that the browser adds, and left just as much as to center the image. Not to mention the click required to enlarge the image to full-size.</p>
<p>Not any more!<span id="more-241"></span> I was so fed up today, that I wrote a little bookmarklet that does this. It enlarges the image to full size, removes the margins and scrolls the page left so that the image is centered. It works on any recent browser I&#8217;ve tested, and I think it will probably work in most browsers that web designers use (hint: not old IEs) <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> </p>
<p>Enjoy.</p>
<p><a href="javascript:(function(){%20document.body.style.margin%20=%200;%20var%20inner%20=%20window.innerWidth%20||%20document.body.clientWidth,%20img%20=%20document.getElementsByTagName('img')[0];%20img.removeAttribute('width');%20img.removeAttribute('height');%20document.body.scrollLeft%20=%20(img.offsetWidth%20-%20inner)/2;%20})();">Mockup viewer</a></p>
<p>JS code:</p>
<pre>(function(){
	document.body.style.margin = 0;
	var inner = window.innerWidth || document.body.clientWidth, img = document.getElementsByTagName('img')[0];
	img.removeAttribute('width');
	img.removeAttribute('height');
	document.body.scrollLeft = (img.offsetWidth - inner)/2;
})();</pre>
<p>If only it could also write the XHTML &amp; CSS for the site&#8230; <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> </p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/03/mockup-viewer-bookmarklet/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Check whether the browser supports RGBA (and other CSS3 values)</title>
		<link>http://leaverou.me/2009/03/check-whether-the-browser-supports-rgba-and-other-css3-values/</link>
		<comments>http://leaverou.me/2009/03/check-whether-the-browser-supports-rgba-and-other-css3-values/#comments</comments>
		<pubDate>Sun, 01 Mar 2009 14:44:14 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[Tips]]></category>
		<category><![CDATA[colors]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[CSS3 values]]></category>
		<category><![CDATA[feature detection]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[RGBA]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=203</guid>
		<description><![CDATA[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&#8217;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 [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F03%252Fcheck-whether-the-browser-supports-rgba-and-other-css3-values%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Check%20whether%20the%20browser%20supports%20RGBA%20%28and%20other%20CSS3%20values%29%22%20%7D);"></div>
<p>When using CSS, we can just include both declarations, one using rgba, and one without it, as mentioned in my post on <a href="http://leaverou.me/2009/02/bulletproof-cross-browser-rgba-backgrounds/" target="_blank">cross-browser RGBA backgrounds</a>. When writing JavaScript however, it&#8217;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 <a href="http://leaverou.me/2009/02/check-if-a-css-property-is-supported/" target="_blank">whether it suppports a given property</a>. We can even follow the same technique to detect the support of other CSS3 values (for instance, <a href="http://www.css3.info/preview/multiple-backgrounds/">multiple backgrounds</a> support, <a href="http://www.css3.info/preview/hsla/" target="_blank">HSLA</a> support, etc).<span id="more-203"></span></p>
<p>The technique I&#8217;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:</p>
<ol>
<li>Get the <code>color</code> (or <code>backgroundColor</code>, or <code>borderColor</code> or any property that is widely supported and accepts color values) value of the <code>style</code> object of any element that exists in the page for sure (for instance, the first script tag) and store it in a variable.</li>
<li>Assign an RGBA color to the <code>color</code> property of that element and catch any errors produced.</li>
<li>Assign to a variable whether the <code>color</code> of that element did change (boolean <code>true</code> or <code>false</code>).</li>
<li>Restore the previous color to the <code>color</code> property, so that our script doesn&#8217;t interfere with the designer&#8217;s decisions.</li>
<li>Return the stored result.</li>
</ol>
<p>and it would result in the following code:</p>
<pre>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;
}</pre>
<h3>Performance improvements</h3>
<p>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.</p>
<p>This can be achieved in many ways. My personal preference is to store the result as a property of the function called, named <code>'result'</code>:</p>
<pre>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;
}</pre>
<h3>Making it bulletproof</h3>
<p>There is a rare case where the script element might <strong>already</strong> have <code>rgba(0,0,0,0.5)</code> set as it&#8217;s color value (don&#8217;t ask me why would someone want to do that <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' />  ), in which case our function will return <code>false</code> even if the browser actually supports RGBA. To prevent this, you might want to check whether the <code>color</code> property is already set to <code>rgba(0,0,0,0.5)</code> and return <code>true</code> if it is (because if the browser doesn&#8217;t support rgba, it will be blank):</p>
<pre>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;
}</pre>
<p>Done!</p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/03/check-whether-the-browser-supports-rgba-and-other-css3-values/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Bulletproof, cross-browser RGBA backgrounds, today</title>
		<link>http://leaverou.me/2009/02/bulletproof-cross-browser-rgba-backgrounds/</link>
		<comments>http://leaverou.me/2009/02/bulletproof-cross-browser-rgba-backgrounds/#comments</comments>
		<pubDate>Sun, 15 Feb 2009 16:38:50 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Original]]></category>
		<category><![CDATA[colors]]></category>
		<category><![CDATA[CSS3]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[RGBA]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=70</guid>
		<description><![CDATA[UPDATE: There is a new version, here First of all, happy Valentine&#8217;s day for yersterday. This is the second part of my &#8220;Using CSS3 today&#8221; series. This article discusses current RGBA browser support and ways to use RGBA backgrounds in non-supporting browsers. Bonus gift: A PHP script of mine that creates fallback 1-pixel images on [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F02%252Fbulletproof-cross-browser-rgba-backgrounds%252F%22%2C%20%22shorturl%22%3A%20%22http%3A%2F%2Fbit.ly%2Fcng9HM%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Bulletproof%2C%20cross-browser%20RGBA%20backgrounds%2C%20today%22%20%7D);"></div>
<p><strong>UPDATE:</strong> There is a new version, <a href="http://leaverou.me/2009/10/new-version-of-rgba-php-is-out/">here</a></p>
<p>First of all, happy Valentine&#8217;s day for yersterday. <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' />  This is the second part of my &#8220;<a href="http://leaverou.me/2009/02/css3-border-radius-today/" target="_blank">Using CSS3 today</a>&#8221; series. This article discusses current RGBA browser support and ways to use RGBA backgrounds in non-supporting browsers. Bonus gift: A PHP script of mine that creates fallback 1-pixel images on the fly that allow you to easily utilize RGBA backgrounds in any browser that can support png transparency. In addition, the images created are forced to be cached by the client and they are saved on the server&#8217;s hard drive for higher performance.<span id="more-70"></span></p>
<h3>Browsers that currently support RGBA</h3>
<p>These are:</p>
<ul>
<li>Firefox 3+</li>
<li>Safari 2+</li>
<li>Opera 10 (still in beta)</li>
<li>Google Chrome</li>
</ul>
<p>In these browsers you can write CSS declarations like:</p>
<pre>background: rgba(255,200,35,0.5) url(somebackground.png) repeat-x 0 50%;
border: 1px solid rgba(0,0,0,0.3);
color: rgba(255,255,255,0.8);</pre>
<p>And they will work flawlessly.</p>
<h3>Internet Explorer</h3>
<p>Surprisingly, it seems that <a href="http://www.hedgerwow.com/360/dhtml/rgba/demo.php">Internet Explorer supported RGBA backgrounds long before the others</a>. Of course, with it&#8217;s <a href="http://msdn.microsoft.com/en-us/library/ms532997.aspx">very own properietary syntax</a>, as usual:</p>
<pre>filter: progid:DXImageTransform.Microsoft.gradient(startColorstr=#550000FF, endColorstr=#550000FF);</pre>
<p>And since nothing is ever simple with IE, IE8 <a href="http://blogs.msdn.com/ie/archive/2008/09/08/microsoft-css-vendor-extensions.aspx">requires a special syntax which has to be put before the first one</a> to work properly in IE8 beta1:</p>
<pre>-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#550000FF, endColorstr=#550000FF)";</pre>
<p>The code above actually draws a gradient from <code>rgba(0,0,255,0.33)</code> to <code>rgba(0,0,255,0.33)</code> using a Microsoft-proprietary &#8220;extended&#8221; hex format that places the Alpha parameter first (instead of last) and in the range of 00-FF (instead of 0-1). The rest is a usual hex color, in that case #0000FF.</p>
<p><strong>Caution: </strong>The &#8220;gradients&#8221; that are created via the gradient filter are placed <strong>on top</strong> of any backgrounds currently in effect. So, if you want to have a background image as well, the result may not be what you expected. If you provide a solid color as a background, it will also not work as expected (no alpha transparency), since the gradients created are not exactly backgrounds, they are just layers <strong>on top</strong> of backgrounds.</p>
<h3 id="filter-issues">Problems with the filter method</h3>
<ul>
<li>Filters are bad for client-side performance.</li>
<li>Filters cause the text rendering to be aliased and especially when it&#8217;s bold and there is no background-color set it becomes completely unreadable. (the worst disadvantage if you ask me)</li>
<li>Filters only work with IE. What about Firefox 2- and Opera 9.6-?</li>
<li>Filters are lengthy (especially now that you have to include 2 different syntaxes) so they significantly increase the size of your CSS when used frequently.</li>
<li>You have to convert the red, green and blue values to hex to use that method.</li>
<li>To use a filter, the element has to <a href="http://haslayout.net/" target="_blank">have Layout</a>. This is usually done via zoom:1. More non-standard clutter in your CSS.</li>
<li>Doesn&#8217;t play along well with other workarounds, since it doesn&#8217;t modify the background of the element.</li>
</ul>
<p>So, personally, I only use that approach sparingly, in particular, only when &#8220;no/minimum external files&#8221; is a big requirement.</p>
<h3>A bulletproof solution</h3>
<p>My favored approach is to use rgba() for all RGBA-capable browsers and fallback pngs for the ones that don&#8217;t support RGBA. However, creating the pngs in Photoshop, or a similar program and then uploading them is too much of a fuss for me to bare (I get bored easily <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' />  ). So, I created a small PHP script that:</p>
<ul>
<li>Creates a 1-pixel png image with the parameters passed for red, green, blue and alpha. No need to convert to hex.</li>
<li>Supports named colors, to speed up typing even more for colors that you use commonly in a site (it includes white and black by default, but you may easily add as many as you like).</li>
<li>Stores the generated images on the server, so that they don&#8217;t have to be created every time (generating images on the fly has quite an important performance impact).</li>
<li>Forces the images to be cached on the browser so that they don&#8217;t have to be generated every time (even though their size is very small, about 73 bytes).</li>
</ul>
<p>Here it is: <a href="http://leaverou.me/wp-content/uploads/2009/02/rgba.zip">rgba.php</a></p>
<p>You use it like this:</p>
<pre>background: url(rgba.php?r=255&amp;g=100&amp;b=0&amp;a=50) repeat;
background: rgba(255,100,0,0.5);</pre>
<p>or, for named colors:</p>
<pre>background: url(rgba.php?name=white&amp;a=50) repeat;
background: rgba(255,255,255,0.5);</pre>
<p>Browsers that are RGBA-aware will follow the second background declaration and will not even try to fetch the png. Browsers that are RGBA-incapable will ignore the second declaration, since they don&#8217;t understand it, and stick with the first one. <strong>Don&#8217;t change the order of the declarations: The png one goes first, the rgba() one goes second.</strong> If you put the png one second, it will always be applied, even if the browser <strong>does </strong>support rgba.</p>
<p>Before you use it, open it with an editor to specify the directory you want it to use to store the created pngs (the default is <code>'colors/'</code>) and add any color names you want to be able to easily address (the defaults are white and black). If the directory you specify does not exist or isn&#8217;t writeable you&#8217;ll get an error.</p>
<p><strong>Caution: </strong>You have to enter the alpha value in a scale of 0 to 100, and not from 0 to 1 as in the CSS. This is because you have to urlencode dots to transfer them via a URI and it would complicate things for anyone who used this.</p>
<p><strong>Edit: </strong>It seems that IE8 sometimes doesn&#8217;t cache the image produced. I should investigate this further.</p>
<p><strong><span style="color: #ff0000;">IMPORTANT:</span> If your PHP version is below 5.1.2 perform <a href="http://leaverou.me/2009/02/bulletproof-cross-browser-rgba-backgrounds/#comment-101">this change</a> in the PHP file or it won&#8217;t work.</strong></p>
<h3>Why not data:// URIs?</h3>
<p>Of course, you could combine the IE gradient filter, rgba() and <code>data://</code> URIs for <a href="http://www.webdevelopedia.com/better_opacity.html">a cross-browser solution that does not depend on external files</a>. However, this approach has some disadvantages:</p>
<ul>
<li>All the <a href="#filter-issues">disadvantages of filters mentioned above</a>.</li>
<li>You can&#8217;t be spontaneous in your CSS and changes are difficult. Every time you want to use RGBA, you have to resort to some converter to create the png and it&#8217;s <code>data://</code> URI. Unless you are some kind of a cyborg with an embedded base64 encoder/decoder in your head <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' /> </li>
<li>Larger filesize (you have to use 4-5 declarations (the rgba() one, the <code>data://</code> one, 2 filters, one for IE7- and one for IE8 and a <code>zoom:1;</code> to give the element &#8220;layout&#8221; so that filters can be applied) instead of 2, and the data:// URI has the same size as the png). Also, the <code>data://</code> URI can not be cached so every time you use it, you increase the filesize even more.  Ok, you save an http request per use, but is it worth it?</li>
</ul>
<p>and some advantages:</p>
<ul>
<li>You will not see the site without a background for even a single millisecond. Since the png is embedded in the CSS, it&#8217;s loaded as soon as the CSS itself is loaded. If your site background is too dark and you rely on the RGBA background to make the content legible, you might want to consider this solution.</li>
<li>No external files, no extra http requests.</li>
<li>The filter method works in IE6- without the script for transparent PNGs.</li>
</ul>
<p>Choose the method that fits your needs better. <img src='http://leaverou.me/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<h3>RGBA is not only for backgrounds!</h3>
<p>It&#8217;s also for every CSS property that accepts color values. However, backgrounds in most cases are the easiest to workaround. As for borders, if you want solid ones, you can simulate them sometimes by wrapping a padded container with an RGBA background around your actual one and giving it as much padding as your desired border-width. For text color, sometimes you can fake that with opacity. However, these &#8220;solutions&#8221; are definitely incomplete, so you&#8217;d probably have to wait for full RGBA support and provide solid color fallbacks for those (unless someone comes up with an ingenious solution in &lt;canvas&gt;, it&#8217;s common these days <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' />  ).</p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/02/bulletproof-cross-browser-rgba-backgrounds/feed/</wfw:commentRss>
		<slash:comments>33</slash:comments>
		</item>
		<item>
		<title>Find the vendor prefix of the current browser</title>
		<link>http://leaverou.me/2009/02/find-the-vendor-prefix-of-the-current-browser/</link>
		<comments>http://leaverou.me/2009/02/find-the-vendor-prefix-of-the-current-browser/#comments</comments>
		<pubDate>Fri, 13 Feb 2009 10:08:25 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[CSS properties]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Vendor prefixes]]></category>
		<category><![CDATA[Webkit bugs]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=48</guid>
		<description><![CDATA[As you probably know already, when browsers implement an experimental or proprietary CSS property, they prefix it with their &#8220;vendor prefix&#8221;, so that 1) it doesn&#8217;t collide with other properties and 2) you can choose whether to use it or not in that particular browser, since it&#8217;s support might be wrong or incomplete. When writing [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F02%252Ffind-the-vendor-prefix-of-the-current-browser%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Find%20the%20vendor%20prefix%20of%20the%20current%20browser%22%20%7D);"></div>
<p>As you probably know already, when browsers implement an experimental or proprietary CSS property, they prefix it with their &#8220;vendor prefix&#8221;, so that 1) it doesn&#8217;t collide with other properties and 2) you can choose whether to use it or not in that particular browser, since it&#8217;s support might be wrong or incomplete.</p>
<p>When writing CSS you probably just include all properties and rest in peace, since browsers ignore properties they don&#8217;t know. However, when changing a style via javascript it&#8217;s quite a waste to do that.</p>
<p>Instead of iterating over all possible vendor prefixes every time to test if a prefixed version of a specific property is supported, we can create a function that returns the current browser&#8217;s prefix and caches the result, so that no redundant iterations are performed afterwards. How can we create such a function though?<span id="more-48"></span></p>
<h3>Things to consider</h3>
<ol>
<li>The way CSS properties are converted their JS counterparts: Every character after a dash is capitalized, and all others are lowercase. The only exception is the new <code>-ms-</code> prefixed properties: Microsoft did it again and made their JS counterparts start with a lowercase <code>m</code>!</li>
<li>Vendor prefixes always start with a dash and end with a dash</li>
<li>Normal CSS properties never start with a dash</li>
</ol>
<h3>Algorithm</h3>
<ol>
<li>Iterate over all supported properties and find one that starts with a known prefix.</li>
<li>Return the prefix.</li>
<li>If no property that starts with a known prefix was found, return the empty string.</li>
</ol>
<h3>JavaScript code</h3>
<pre>function getVendorPrefix()
{
	var regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/;

	var someScript = document.getElementsByTagName('script')[0];

	for(var prop in someScript.style)
	{
		if(regex.test(prop))
		{
			// test is faster than match, so it's better to perform
			// that on the lot and match only when necessary
			return prop.match(regex)[0];
		}

	}

	// Nothing found so far?
	return '';
}</pre>
<p><strong>Caution: </strong>Don&#8217;t try to use someScript.style.hasOwnProperty(prop). It&#8217;s missing on purpose, since if these properties aren&#8217;t set on the particular element, hasOwnProperty will return false and the property will not be checked.</p>
<h3>Browser bugs</h3>
<p>In a perfect world we would be done by now. However, if you try running it in Webkit based browsers, you will notice that the empty string is returned. This is because for some reason, Webkit does not enumerate over empty CSS properties. To solve this, we&#8217;d have to check for the support of a property that exists in all webkit-based browsers. This property should be one of the oldest -webkit-something properties that were implemented in the browser, so that our function returns correct results for as old browser versions as possible. <code>-webkit-opacity</code> seems like a good candidate but I&#8217;d appreciate any better or more well-documented picks. We&#8217;d also have to test <code>-khtml-opacity</code> as <a href="http://webkit.org/blog/22/css3-goodies-borders-and-backgrounds/#comment-121" target="_blank">it seems that Safari had the -khtml- prefix before the -webkit- prefix</a>. So the updated code would be:</p>
<pre>function getVendorPrefix()
{
	var regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/;

	var someScript = document.getElementsByTagName('script')[0];

	for(var prop in someScript.style)
	{
		if(regex.test(prop))
		{
			// test is faster than match, so it's better to perform
			// that on the lot and match only when necessary
			return prop.match(regex)[0];
		}

	}

	// Nothing found so far? Webkit does not enumerate over the CSS properties of the style object.
	// However (prop in style) returns the correct value, so we'll have to test for
	// the precence of a specific property
	if('WebkitOpacity' in someScript.style) return 'Webkit';
	if('KhtmlOpacity' in someScript.style) return 'Khtml';

	return '';
}</pre>
<p>By the way, if Webkit ever fixes that bug, the result will be returned straight from the loop, since we have added the Webkit prefix in the regexp as well.</p>
<h3>Performance improvements</h3>
<p>There is no need for all this code to run every time the function is called. The vendor prefix does not change, especially during the session <img src='http://leaverou.me/wp-includes/images/smilies/icon_razz.gif' alt=':P' class='wp-smiley' />  Consequently, we can cache the result after the first time, and return the cached value afterwards:</p>
<pre>function getVendorPrefix()
{
	if('result' in arguments.callee) return arguments.callee.result;

	var regex = /^(Moz|Webkit|Khtml|O|ms|Icab)(?=[A-Z])/;

	var someScript = document.getElementsByTagName('script')[0];

	for(var prop in someScript.style)
	{
		if(regex.test(prop))
		{
			// test is faster than match, so it's better to perform
			// that on the lot and match only when necessary
			return arguments.callee.result = prop.match(regex)[0];
		}

	}

	// Nothing found so far? Webkit does not enumerate over the CSS properties of the style object.
	// However (prop in style) returns the correct value, so we'll have to test for
	// the precence of a specific property
	if('WebkitOpacity' in someScript.style) return arguments.callee.result = 'Webkit';
	if('KhtmlOpacity' in someScript.style) return arguments.callee.result = 'Khtml';

	return arguments.callee.result = '';
}</pre>
<h3>Afterthoughts</h3>
<ul>
<li>Please don&#8217;t use this as a browser detection function! Apart from the fact that browser detects are a bad way to code 99.9% of the time, it&#8217;s also unreliable for IE, since Microsoft added a vendor prefix in IE8 only. Before that it followed the classic attitude &#8220;We have a large market share so standards and conventions don&#8217;t apply to us&#8221;.</li>
<li>There are some browsers that support multiple prefixes. If that is crucial for you, you may want to return an array with all prefixes instead of a string. It shouldn&#8217;t be difficult to alter the code above to do that. I&#8217;ll only inform you that from my tests, Opera also has <code>Apple</code>, <code>Xn</code> and <code>Wap</code> prefixes and Safari and Chrome also have <code>Khtml</code>.</li>
<li>I wish there was a list somewhere with ALL vendor prefixes&#8230; If you know such a page, please leave a comment.</li>
</ul>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/02/find-the-vendor-prefix-of-the-current-browser/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Extend Math.round, Math.ceil and Math.floor to allow for precision</title>
		<link>http://leaverou.me/2009/02/extend-mathround-mathceil-and-mathfloor-to-allow-precision/</link>
		<comments>http://leaverou.me/2009/02/extend-mathround-mathceil-and-mathfloor-to-allow-precision/#comments</comments>
		<pubDate>Thu, 12 Feb 2009 09:40:25 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Math]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=27</guid>
		<description><![CDATA[Math.round, Math.ceil and Math.floor are very useful functions. However, when using them, I find myself many times needing to specify a precision level. You don&#8217;t always want to round to an integer, you often just want to strip away some of the decimals. We probably all know that if we have a function to round [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F02%252Fextend-mathround-mathceil-and-mathfloor-to-allow-precision%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22Extend%20Math.round%2C%20Math.ceil%20and%20Math.floor%20to%20allow%20for%20precision%22%20%7D);"></div>
<p><code>Math.round</code>, <code>Math.ceil</code> and <code>Math.floor</code> are very useful functions. However, when using them, I find myself many times needing to specify a precision level. You don&#8217;t always want to round to an integer, you often just want to strip away <strong>some</strong> of the decimals.</p>
<p>We probably all know that if we have a function to round to integers, we can round to X decimals by doing <code>Math.round(num*Math.pow(X,10)) / </code><code>Math.pow(X,10)</code>. This kind of duck typing can get tedious, so usually, you roll your own function to do that. However, why not just add that extra functionality to the functions that already exist and you&#8217;re accustomed to?<span id="more-27"></span></p>
<p>Let&#8217;s start with <code>Math.round</code>. It&#8217;s the most needed one anyway.</p>
<p>Firstly we&#8217;ll have to store the native function somewhere, since we&#8217;re going to replace it. So we do something along the lines of:</p>
<pre>Math._round = Math.round;</pre>
<p>Now let&#8217;s *sigh* replace the native <code>Math.round</code> with our own:</p>
<pre>Math.round = function(number, precision)
{
	precision = Math.abs(parseInt(precision)) || 0;
	var coefficient = Math.pow(10, precision);
	return Math._round(number*coefficient)/coefficient;
}</pre>
<p>And guess what? It still works the old way too, so your old scripts won&#8217;t break.</p>
<p>So now, let&#8217;s go to <code>Math.ceil</code> and <code>Math.floor</code>. If you notice, the only thing that changes is the function name. Everything else is the same. So, even though we could copy-paste the code above and change the names, we would end up with triple the size of the code that we need and we would have also violated the <a title="Don't Repeat Yourself" href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself" target="_blank">DRY</a> principle. So we could put the names of the functions in an array, and loop over it instead:</p>
<pre>(function(){
	var MathFns = ['round', 'floor', 'ceil' ];
	for(var i = MathFns.length; i&gt;-1; i--)
	{
		Math['_' + MathFns[i]] = Math[MathFns[i]];
		Math[MathFns[i]] = function(number, precision)
		{
			precision = Math.abs(parseInt(precision)) || 0;
			var coefficient = Math.pow(10, precision);
			return Math['_' + MathFns[i]](number*coefficient)/coefficient;
		}
   }
})();</pre>
<p>Why the closure? To allow us to be free in defining our variables without polluting the global namespace. In case <code>Array.prototype.forEach()</code> was cross-browser or if you have mutated the <code>Array</code> prototype to add it for non-supporting ones, you could easily do that:</p>
<pre>['round', 'floor', 'ceil' ].forEach(function(funcName){
	Math['_' + funcName] = Math[funcName];
	Math[funcName] = function(number, precision)
	{
		precision = Math.abs(parseInt(precision)) || 0;
		var coefficient = Math.pow(10, precision);
		return Math['_' + funcName](number*coefficient)/coefficient;
	}
});</pre>
<p>No closures and much easier to read code.</p>
<p>However, nothing comes without a cost. In this case, the cost is performance. In my tests, the new function takes about twice the time of the native one. Adding a conditional to check if the precision is falsy and use the native function directly if so, doesn&#8217;t improve the results much, and it would slow the function down for precision values &gt; 0. Of course the speed would be just as much if the function was a normal one and not a replacement for Math[something], that doesn&#8217;t have anything to do with it.</p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/02/extend-mathround-mathceil-and-mathfloor-to-allow-precision/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>JS library detector</title>
		<link>http://leaverou.me/2009/02/js-library-detector/</link>
		<comments>http://leaverou.me/2009/02/js-library-detector/#comments</comments>
		<pubDate>Wed, 11 Feb 2009 03:03:00 +0000</pubDate>
		<dc:creator>Lea Verou</dc:creator>
				<category><![CDATA[Original]]></category>
		<category><![CDATA[bookmarklets]]></category>
		<category><![CDATA[dojo]]></category>
		<category><![CDATA[DOMAssistant]]></category>
		<category><![CDATA[ExtJS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[JavaScript libraries]]></category>
		<category><![CDATA[jQuery]]></category>
		<category><![CDATA[MooTools]]></category>
		<category><![CDATA[Prototype]]></category>
		<category><![CDATA[YUI]]></category>

		<guid isPermaLink="false">http://leaverou.me/?p=15</guid>
		<description><![CDATA[Ever wondered which JavaScript library (if any) is hidden beneath the bells &#38; whistles of each site you gazed at? Since I am a curious person, I find myself wondering every time, so after a bit of research, I wrapped up a little bookmarklet that instantly told me the answer every time. The logic behind [...]]]></description>
			<content:encoded><![CDATA[
<div class="topsy_widget_data topsy_theme_mustard" style="float: right;margin-left: 0.75em; background: url(data:,%7B%20%22url%22%3A%20%22http%253A%252F%252Fleaverou.me%252F2009%252F02%252Fjs-library-detector%252F%22%2C%20%22style%22%3A%20%22big%22%2C%20%22title%22%3A%20%22JS%20library%20detector%22%20%7D);"></div>
<p>Ever wondered which JavaScript library (if any) is hidden beneath the bells &amp; whistles of each site you gazed at? Since I am a curious person, I find myself wondering every time, so after a bit of research, I wrapped up a little bookmarklet that instantly told me the answer every time.<span id="more-15"></span></p>
<p>The logic behind it is that every JavaScript library creates at least one global variable with an easily recognizable name. For most JavaScript libraries, this is simply their name (Prototype, jQuery, DOMAssistant, MooTools, dojo). For some others, its something close enough to their name (YAHOO for YUI, Scriptaculous for script.aculo.us, Ext for ExtJS). So if you check the precence of this global variable, you are effectively checking for the precence of the related framework. Most of them also contain a property with their version (which is usually named &#8216;version&#8217; or &#8216;Version&#8217; or &#8216;VERSION&#8217; (in YUI)) &#8211; in fact the only library that did not contain such a property was DOMAssistant. So, after a sneak peek at their code, I could easily set up some conditionals that check whether a certain library exists in the page and if so, alert its name and version. If multiple libraries exist at the same page, multiple popups will appear.</p>
<p>So, here is the bookmarklet: <a href="javascript:if('Prototype'%20in%20window)%20{%20var%20ret%20=%20'Prototype%20'%20+%20Prototype.Version;%20if('Scriptaculous'%20in%20window)%20ret%20+=%20'%20with%20script.aculo.us%20'%20+%20Scriptaculous.Version;%20alert(ret);%20}%20if('jQuery'%20in%20window)%20alert('jQuery%20'%20+%20jQuery.fn.jquery);%20if('MooTools'%20in%20window)%20alert('MooTools%20'%20+%20MooTools.version);%20if('YAHOO'%20in%20window)%20alert('YUI%20'%20+%20YAHOO.VERSION);%20if('dojo'%20in%20window)%20alert('Dojo%20'%20+%20dojo.version);%20if('Ext'%20in%20window)%20alert('ExtJS%20'%20+%20Ext.version);%20if('DOMAssistant'%20in%20window)%20alert('DOMAssistant');">JS library detector</a></p>
<p>Just drag it to your bookmarks toolbar and it&#8217;s ready.</p>
<p>And here is the human-readable code:</p>
<pre>if('Prototype' in window)
{
	var ret = 'Prototype ' + Prototype.Version;
	if('Scriptaculous' in window) ret += ' with script.aculo.us ' + Scriptaculous.Version;
	alert(ret);
}
if('jQuery' in window) alert('jQuery ' + jQuery.fn.jquery);
if('MooTools' in window) alert('MooTools ' + MooTools.version);
if('YAHOO' in window) alert('YUI ' + YAHOO.VERSION);
if('dojo' in window) alert('Dojo ' + dojo.version);
if('Ext' in window) alert('ExtJS ' + Ext.version);
if('DOMAssistant' in window) alert('DOMAssistant');</pre>
<p>Am I nuts? Certainly. Has it been useful to me? Absolutely.</p>

]]></content:encoded>
			<wfw:commentRss>http://leaverou.me/2009/02/js-library-detector/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
