Category Archives: javascript

jQuery & others vs. Vanilla Web

disclaimer: although debating the usefulness of full-featured libraries in general, the arguments are based on many aspects of the most popular one, jQuery. They should still hold mostly true for the other ones.

A very interesting conversation just happened as Christian Heilman proposed the idea of a book about modern Web authoring without libraries such as jQuery. It turned into a heated debate between Vanilla Web and full-featured libraries supporters. It is obvious that both sides share the common goal of improving the Web and helping Web authors, they also both have valid arguments to defend their vision.

I personally don’t know where to stand so I tried to list the arguments that have been raised by libraries and Vanilla proponents there and in other places.

File-size. Size does matter?

jQuery weighs around 32Kb minified and gzipped. At the time of writing, the average weight of a page is 1 239Kb and it only keeps increasing. The size of a single lib appears to be insignificant and a developer should first look into image compression and lazy-loading to reduce the payload.

Average page size: 1239Kb

On the other-hand, it is quite common to pick UI components that requires jQuery UI, some Bootstrap plugins and Raphael based visualizations. Soon enough, the file-size of script and style-sheet dependencies can become significant. Using CDN hosted libraries (from Google’s CDN or cdnjs) increase the chances for these resources to be loaded from cache, but version fragmentation mitigates that benefit; and modular builds aren’t available for the libs that allow it (jQuery UI, dojo).

Conclusion

Most of the time, using one library in a website isn’t a problem, but dependencies can add up to the point where their size becomes a problem.

Performances. It’s how you use it.

There’s a funny “Speed Comparison” table on vanilla-js.com that shows how far better document.getElementById("test") performs compared to jQuery("#test"). That is undeniable, but what matters is that any code outside hot loops doesn’t need the extra-speed; that best-practices can help avoid most bottlenecks (use event delegation, modify class names instead of inline styles, …); and that libraries benefit from many clever optimizations that are not always found in Vanilla code.

Conclusion

By using best-practices, it is possible to write code that performs equally well with or without libraries. The opposite is true.

YAGNI vs. Don’t Reinvent the Wheel

A single UI component doesn’t need all features exposed by generalist libraries. There might even be a lot of useless features for an entire application: code for backward compatibility with IE6-7 (e.g. a selector engine), JS animations or complex XHR. All libs have switched to a modular architecture, but their granularity will never be good enough to bundle “just what this component needs”.

On the other hand, there are still many features offered by these libs that aren’t trivial to implement even when targeting the most recent browsers: delegated event listener, node-list looping, white-listed DOM builder or offset getter, to name a few. Embedding those features into a component isn’t reasonable: developers need reusable, reliable, documented and maintained utilities implementing these features. Isn’t it what our current libraries are?

Conclusion

Current libraries are bloated… but they’re so useful and their level of quality so hard to match!

Moving the Web forward.

Time spent rewriting features already available in current libs is time not spent building tools that have never been seen before. And time spent learning about low-level DOM stuff is time not spent learning about the tools that can improve productivity.

But as the Web changes, being able to question the way websites are built is fundamental: “Can I adopt a gracefully degrading approach and use CSS here instead of JS?”, “How hard would it be to implement X when targeting only IE8 (or 9) and better browsers?”. If this leads to better websites and tools, who would complain?

Conclusion

Learning what picks one’s curiosity is a good way to move the Web forward, whether that is low-level or high-level knowledge doesn’t really matter.

General conclusion

Including a full-featured library or sticking to Vanilla Web in a project shouldn’t be a reflex but the result of a well informed choice. A good lib will still prove useful in most cases, but I personally appreciate all efforts to build lib-independent components that give developers the ultimate choice of using a library or not.

You see additional reasons to use full-featured libraries or to leave them behind? Feel free to share!

Mozilla addons: jQuery Injection / Script File Injection

UPDATE: @ZER0 suggested to use data.url instead of data.load

In my last blog post I described different ways of interaction between content scripts and pages. One of them was to inject inline scripts in the page. Today I describe a way to inject scripts files such as jQuery in a page.

If the value or function you want to insert in the page is not dynamically generated (i.e. it never changes), you’d better put it in its own file. To do that, you simply need to retrieve the url of the script file from the addon code, pass it to the content script and then inject it.

Passing The URL

The script that you want to inject in the page should be stored in the “data” folder, just like a content script. The URL is retrieved with the usual data.url utility and then passed to a content script using “port.emit” / “port.on“.

// in main.js
require("page-mod").PageMod({
  include: "http://example.com/",
  contentScriptWhen: "start",
  contentScriptFile: data.url( "injecter.js" ),
  onAttach: function( worker ) {
    	worker.port.emit( "init", data.url( "toBeInjected.js" ) );
  }
});
// in injecter.js
...
self.port.on("init", function( scriptURL ) {
  // use the URL
  ...
});

Using an “init” event just to pass a value is rather annoying, but a “contentScriptGlobals” should be added to a future version of the SDK to simplify this, see bug 688127.

Injecting the Script

The code of the injecter is pretty straightforward.

// injecter.js
var script = document.createElement( "script" );
script.type = "text/javascript";

self.port.on("init", function( scriptURL ) {
  script.src = scriptURL;
  window.document.body.appendChild( script );
});

If you have a cleaner way to inject a script in a page, I’d love to hear from it!

Offline Web Applications, we're not there yet.

Being able to use applications offline is an important feature in the quest for native like (mobile) web apps. The offline mechanisms currently available are indexedDB and appcache. Developers who’ve already used the latter know that it’s at least clumsy, and sometimes useless. If you haven’t, here’s why.

Use case, use case, use case

I want to create a mobile client for Twitter, no wait! I want to create a mobile client for Google Maps, no wait! I want to create a mobile news reader, no wait! …
Anyway, I’m a strong believer believer in the Open Web and a challenge taker, I don’t even want to use phonegap.
To display the home page and the different views of my app, I need a way to store the HTML, CSS and JS. To store the tweets, maps or news items of my app in a manageable way, I need a database. This content should be updated as soon as a connection is available; same goes for the HTML, CSS and JS that runs the app. More importantly: my tweets, maps or news items aren’t only text, I need to store the associated images or profile pictures.

What appcache offers

The main way to use the appcache is the cache manifest: a text file associated to an HTML page using the manifest attribute.

<!DOCTYPE html>
<html manifest="index.appcache">
<head>
...

The manifest itself consists mainly in a list of resources that should be cached and made available for offline use.

CACHE MANIFEST

CACHE
# resources listed in this section are cached
styles.css
script.js
jquery.js
logo.png

NETWORK
# resources matching those prefixes will be available when online
http:/search.twitter.com/*
http:/api.twitter.com/*

Here’s how a browser deals with appcache (simplified):

For more info, have a look at appacachefacts or the spec.

If the appcache has been udated, the browser does not refresh the page to display the new content. It is however possible to listen to updateready events and propose users to reload the page:

if (window.applicationCache) {
    applicationCache.addEventListener('updateready', function() {
        if (confirm('An update is available. Reload now?')) {
            window.location.reload();
        }
    });
}

Is that an acceptable user experience?
Having to do that every time the content of your app is updated is not acceptable (that’s why appcache should not be considered as a way to improve performances of normal websites). It would be acceptable if all the content of the app was updated using XHR, and only the structural HTML, CSS and JS was listed in the cache and updated occasionally (this would actually be a simpler update mechanism than the ones used by native apps). But current websites are usually not developed this way, as this can cause some accessibility and crawling problems.

Storing and deleting content

To store the content of my app in a manageable way, some kind of database is required. Luckily enough, IndexedDB will soon be available in the five major browsers. It will allow tweets, maps or news items to be first stored in plain text, as JSON or HTML; and later listed, sorted, updated and deleted.

What about the associated images?
That’s the tricky part when building a third-party app for a popular API such as Twitter or Google Maps: images loaded from a foreign origin cannot be turned to data-URLs for security reasons (unless they’re served with CORS headers, but they’re not; and using a server as a proxy to set CORS headers doesn’t scale, as those APIs have IP based usage limits).

Those images can still be listed in the cache manifest: a manifest server could be built to keep track of what should be cached for each user; it would dynamically generate a new manifest every time an image has to be added or removed; and once the new manifest is available, the client would do a applicationCache.update().
This is however absolutely unrealistic: First, a server is required, and that’s very much unfortunate. Then, when updating the cache, the browser throws away all of its current content and loads all resources again… This means that every-time a single image has to be added or removed from the cache, all other resources are downloaded again!

Can I build offline web apps, now?

App logic available offline appcache
Text content available offline indexedDB
App up-to-date when online appcache + prompt user to reload
Content up-to-date when online indexedDB + update using XHR
Foreign images available offline not yet

How should appcache be improved?

Content update

Yehuda Katz’s suggestion is to let developers specify that the manifest should be checked for updates before the page is loaded from the cache. If it has been updated, the page should be downloaded from the network.
This sounds like a good idea when XHR updates are not an option, and would probably yield good performances. I’m just afraid this could be abused by many websites just to improve web perfs, and users would end up with 1MB of HTML, CSS, JS and images on their disk for every site they ever visited in their life :-/
Ian Hickson suggested to rename the appcache to “offline application store”. Although this is unlikely to happen, I would also advocate using it only for offline purposes, and leaving caching strategies to the browser.
A compromise could be to adopt Yehuda’s solution, but always prompt users for permission to use the appcache. This should effectively prevent cache bloat. Not the perfect solution, sometime ago I actually opened a bug in Firefox to remove the user prompt: application cache should not bother users.

Dynamic cache

The best suggestion I’ve seen to address the need for a dynamic cache is the defunct DataCache proposition. The idea is to add an independant and dynamic cache to the static appcache, to let developers add and remove resources from the cache at will.
I confess I had trouble understanding all the details of the spec, but here’s a naive and rough API I’m proposing:

applicationCache.dynamicStore.add( uri );
applicationCache.dynamicStore.remove( uri );
applicationCache.dynamicStore.update( uri );

var cacheTransaction = new applicationCache.dynamicStore.transaction();
cacheTransaction.add( uri );
cacheTransaction.add( anotherUri );
cacheTransaction.run();

Of course, it should be possible to listen to events such as “updated” on applicationCache.dynamicStore.

This spec also introduces the interesting concept of offlineHandlers that allows XHRs to be rerouted to client-side functions when offline.

navigator.registerOfflineHandler( "search.twitter.com/*", function( request, response ) {
  // search for results in indexedDB instead
  ...
});

I definitely advise you to take a look at the code examples.

Conclusion

We’re not there yet and unfortunately I’ve got no ideal solution to the content update problem that provides both a good developer and user experience. “What Yehuda says + permission prompt” is the least worst option I can think of.
To make the appcache dynamic, I would strongly suggest giving a second chance to DataCache, and maybe simplify the API a bit.

The W3C will soon gather to think about the future of offline web app, so if you’ve got any valuable input, speak up!

Ups and downs

I’m stuck in my home-town because of an annoying backache, I’ll take this opportunity to write a small post.

Down

So I went to Boston and things did not go as planned.

First there was the rain: I ended up walking a quarter of an hour under a heavy rain and arrived at the venue just as if I crossed the Charles by swimming. I was so grateful for the dry jQuery T-shirt they gave us! My laptop was OK but I was still freezing. Was I going to give my first talk in wet pants? Not such an enjoyable perspective…

I was after all dry for the talk, but that did not help much. I was afraid the biggest problem would be my nervousness and my accent, but it turned out that the demo I prepared was too abstract, distracting and did not match the expectations of the audience , probably composed of intermediate/advanced jQuery developers. In the thirty minutes or so that followed the conference I realized all the mistakes I did, and yet it was too late.

I therefore asked Karl Swedberg to write a follow-up post on learningjquery.com with the content that I felt, retrospectively, appropriate for the conf.

Up

You can now read my first post, More Event Delegation with jQuery, on this official jQuery blog. I actually decided to split the original content. There will be a part 4 explaining the way non-bubbling events are made compatible with .live() in jQuery 1.4 (the sections about focus/blur and mouseenter/mouseleave are already written, but the implementation of submit/change/select is not ready yet). The fifth part is going to be shorter, discussing the performance in event delegation with jQuery.

The conference was also a good opportunity to get to meet members of the jQuery community. The few people I had a chance to chat with (mostly foreigners, what a coincidence!) were really nice people. I was thrilled with the announcement of an up-coming jQuery conf in London, I hope I’ll be able to join once again.

What’s next?

First things first, I hope I will heal quickly, staying in bed all day long really doesn’t suit me. I shouldn’t be using a computer right now… I cannot help it.

I’ll be in Praha from the second to the fifth of October for the EU MozCamp. It looks like I’ll see many of the people I met in Madrid, those two days can only be fantastic! And I’ll take two extra days to visit this wonderful city with a friend from uni.

Then it will be time to look for a job… Independence, at last!

Unit testing with QUnit

What does reliability means for a computer program?
It means that it has to behave exactly as its designers expect it to.
One way to achieve it is to test every part of the program with a given input, against the expected results. Those tests should be automated and run regularly during the development process to make sure that no regression is introduced (what works today doesn’t break tomorrow). The rule is to break down the program into simple units and test them separately in order to identify precisely the source of a problem.
This technique is called unit testing and most languages are offering a framework to write, run and produce a visual feedback for the test (green for pass and red for fail).

Consider a function myCamelCase( sentence ) designed to turn any given string into my very own kind of camel case word. To make sure that this function is reliable, we need to be able to assert that, for each potential type of input string, its actual output will match the expected result:

input string expected output
“word” “Word”
“WORD” “Word”
“two words” “TwoWords”
“A single letter” “aSingleLetter”
“this is a letter” “ThisIsAletter”
“remove+special_chars!” “RemoveSpecialChars”
“save 123 numbers” “Save123numbers”
“+ trim me _” “TrimMe”
” + _ ! “ false

Using QUnit

The unit test framework of choice for a jQuery related piece of code is QUnit. It offers a reduced number of assertion methods, allows to group them in tests and modules and display verbose feedback in HTML.

The assertions

QUnit offers three basic assertion functions: ok, equals, same

ok( state, message );

Should be used to assert a Boolean output for a function.

test("'ok' is not only meant to be used for Boolean values", function() {
    ok(123, "123 is ok");
    ok("abc", "Any string is ok");
    ok([], "An empty array is ok");
    ok(!"", "An empty string is not ok");
    ok(!0, "0 is not ok");
    ok(!null, "null is not ok");
    ok(!undefined, "undefined is not ok");
});
equals( actual, expected, message );

Should be used for a comparison assertion.

test("Cases where 'equals' passes for two values not strictly identical", function() {
    equals(1, true, "1 equals true");
    equals([1], true, "An array with a single 1 value equals true");
    equals("1", true, "A string with only the number 1 equals true");
    equals(0, false, "0 equals false");
    equals([0], false, "An array with a single 0 value equals false");
    equals("0", false, "A string with only the number 0 equals false");
    equals("012.345", 12.345, "A number and this number turned into a string are equal");
});
same( actual, expected, message );

Should be used for strict comparison assertion, works recursively on arrays and objects.

test("The actual and expected values have to be strictly identical", function() {
    same([{a: 2}, false, null], [{a: 2}, false, null], "Same is recursive");
});

Writing tests and modules

A test is composed of an homogeneous set of assertions for one unit of the program: In our example, we should write one assertion for each potential type of input string. The first line of a test state the number of assertions that are expected to pass, it’s optional.


test("Test that each type of input string produces the expected output", function() {
    expect(9);

    equals( camelCase("word"), "Word", "First letter of a word is capitalized");
    equals( camelCase("WORD"), "Word", "Other letters are lower-case");
    equals( camelCase("two words"), "TwoWords", "Spaces between two words are removed");
    equals( camelCase("A single letter"), "aSingleLetter", "If the first letter is a single letter, it is turned to lower-case");
    equals( camelCase("this is a letter"), "ThisIsAletter", "If there is a single letter in the middle of a sentence, the following letter is turned to lower-case);
    equals( camelCase("remove+special_chars!"), "RemoveSpecialChars", "Special characters are removed");
    equals( camelCase("save 123 numbers"), "Save123numbers", "Numbers are saved and the following letter is turned to lower-case");
    equals( camelCase("+ trim me _"), "TrimMe", "Trailing spaces are always removed");
    ok( !camelCase(" + _ ! "), "Return false instead of an empty camel case word");
});

QUnit offers the module method to help you visually identify related tests:

module("Camel Case");

Every test following this module will be prefixed by the module name.

synchronizing tests

QUnit offers two other methods to pause the tests for example when an Ajax call was made and the client is waiting for the answer. It is possible to make a pause in the sequence using the stop() and the start() methods.

Making your life easier.

If you happen to write a long batch of tests, you’re likely to realise that you often write similar lines. Being, like the vast majority of us, a lazy developer, you may start copy/paste large amount of code and write additional logic in order to factorise your test code. By doing this way, you might end up chasing bug that are not in the code you are testing, but right in your tests!

On the other hand, there are several ways to write DRY tests safely.

In-line function

If your function accept a second parameter that is not supposed to change the output of the function, it’s possible to create an additional set of test with few lines of code by using an in-line function. Imagine that we add a second Boolean parameter to make the camel case algorithm go faster (I’d love to see such parameter for every algorithm): myCamelCase( sentence, faster). The code would be:

var camelCaseTest = function(faster) {
    expect(9);

    equals( camelCase("word", faster), "Word", "First letter of a word is capitalized");
    equals( camelCase("WORD", faster), "Word", "Other letters are lower-case");
    equals( camelCase("two words", faster), "TwoWords", "Spaces between two words are removed");
    ...
});

test("Test that each type of input string produces slowly the expected output", function() {
    camelCaseTest(false);
});

test("Test that each type of input string produces quickly the expected output", function() {
    camelCaseTest(true);
});

We’ve just written two tests for the same price!

Setup and Teardown

module() also offers a second parameter to register a setup and teardown callback: pieces of code that will be called respectively before and after each test of this module. For example if you want to reset a number of variables that have been modified during the test:

module("Test camel case", {
    teardown: function() {
        a = b = c = d = undefined;
    }
});

Breaking the rule

The main rule in unit testing is to test your units of code separately. The smaller the units, the easier it will be to locate the source of an assertion failure. It is actually possible to break this rule and test multiple units at the same time ONCE those units have been tested enough to appear reliable.

Limit of unit tests

The first limit of unit testing is… the writer himself. If you don’t cause any failure just by writing tests, then I’m afraid you haven’t written enough tests. The second limit of unit tests in JavaScript is the number of configuration and what is actually testable.

This has been explained in details by John Resig in JavaScript Testing Does Not Scale. In brief, there is too much browsers and OS and it’s not possible to use unit testing to check the visual appearance of a page, or the correct behaviour of an animation for example.

Going further

During this post I have written a complete test for myCamelCase(), a method that I haven’t actually implemented. The test that I’ve written can actually be seen as a specification of this method, and I will only be able to claim that its implementation is complete once it successfully pass those tests. This software design method is called Test-driven Development and is one of the best way to write code that is reliable from the very beginning throughout the entire life cycle of the project.

You know everything about cookies, don't you ?

Cookies are used on the Web for almost 15 years now, and despite their numerous flows and the emerging alternatives brought by HTML5, developers still resort to them, mainly for their ease of use and consistent implementation across browsers. For example, as you might have noticed, the background colour of this page changes over the time. To keep the colour up to date across the different pages, the colour value is stored in a cookie. When a user is offered controls to increase or decrease the font sit on a site directly from a Web page, the same mechanism is used.

So far, the best technical documentation about cookies available on the Web was the one found on QuirksMode. It still appears as the number 1 result on google for the query: cookie Javascript. The related page on Wikipedia is a good source of information concerning all the drawbacks and the alternative to cookies. Although there are suitable alternatives when it comes identifying a client from a server, there is no widespread solutions working purely on the client-side (but I urge you to use a modern web browser and the gears plugin to get yourself ready for the next generation of Web applications). Anyway this documentation and the proposed algorithms are quite old, and the purpose of this post is to give the small update it deserves, and it will stick to client side use of cookies.

Writing a cookie

Cookies are just little text files managed by the browser that can be used to store key/value pairs. The maximum size of a cookie file for a given website is 4ko, preventing it to be used for anything else than small strings (a colour code, a font-size, a preferred language are perfectly fine). And this is the basic code to write your first cookie:

var cookie = "fontsize=19px";
document.cookie = cookie;

You just append a string composed of a key and a value separated by an “=” sign to the cookie property of the document.

By default, this cookie will be stored only as long as your browser remains opened and it will be available only on the same path of the same domain. If the address of the page is http://en.wikipedia.org/wiki/HTTP_cookie for example, cookies written from this page will be only available on pages with addresses beginning by en.wikipedia.org/wiki/, everything before the first “/” being the domain, and the rest being the path.

By the way, during the celebration of the 20th anniversary of the World Wide Web, Tim Berners-Lee admitted that he regretted the way Web addresses were constructed, and that the domain should start with the more general elements. Applied to the previous example, this would have lead to: /org/wikipedia/en/wiki/… (bringing the folder/sub-folder metaphor to the Web).

It is possible to change those defaults by adding parameters at the end of your cookie String.

Expiry date

It is possible to specify explicitly the date when the cookie will be trashed by the browser, using the expires parameter and a Date object serialized by the Date.toUTCString() method (and not the deprecated toGMTString one):

var day = new Date(),
expiryDate = "expires=" + day.toUTCString();

In modern browsers, it is alternatively possible to set the max-age of a cookie in seconds, but this parameter is unfortunately unavailable in Internet Explorer (just like so many other things that would make the Web developer’s life easier).

Path and domain

It’s a good practice to set the path to the highest level:

var path = "path=/";

I can’t foresee any serious security issue resulting from this practice (considering that cookies should never be used to store critical data). And on the other hands you are likely to be unable to erase a particular cookie written by a script not specifying any path.

If there is no sub-domains for your website, the domain parameter is not useful. But in the Wikipedia example, the domain should be set as follows to be able to use the cookies across sub-domains:

var domain = "domain=wikipedia.org";

All-together

Writing a cookie with those parameters can be achieved with the following code:

var day = new Date();
document.cookie = "fontsize=19px" +";"+
"expires=" + day.toUTCString() +";"+
"domain=wikipedia.org +";"+
"path=/";

As you can notice, parameters should be separated by a “;”. Note that so far the Date used is a new, unmodified date, which represent the current date. Setting a expiry date to the current date is not gonna make your cookie last long. The solution will be provided in the scripts.

It is not possible to write two cookies at a time.

document.cookie = "fontsize=19px; fontcolor=#0000FF";

This code would actually write only the first key/value pair. You have to write the second cookie on a separated line beginning with document.cookie. Considering the parameters string to add to each cookie, the need of code factorisation is easy to understand.

Reading a cookie

Reading a particular cookie value can be achieved by reading the cookie string of the website and extracting a single value pair using a regular expression based on a key.

result = new RegExp("(?:^|; )" +key+ "=([^;]*)”).exec(document.cookie);
var value = result[1];

Special characters

In order to be able to read and write values potentially containing special characters (such as a “;” that would split your value), the value should be encoded before being written and decoded after being read.

document.cookie = encodeURIComponent("special=abc;def");
result = new RegExp("(?:^|; )" +"special"+ "=([^;]*)”).exec(document.cookie);
var value = decodeURIComponent(result[1]);

Overwrite and delete cookies

To overwrite a cookie simply write a new cookie with the same key.

document.cookie = "key=firstValue";
document.cookie = "key=secondValue";
// When reading "key", you'll find "secondValue"

The best way to erase a cookie is to overwrite it with an empty value and a past expiry date

var day = new Date();
// set the date to yesterday
day.setDate(day.getDate() -1);
document.cookie = "key=" +";"+
"expires=" + day.toUTCString();

The scripts

Using the previous pieces of code, it is possible to create a small cookie library that will handle writing/reading/erasing a cookie with a single function.

function cookieLib(key, value, expiryDay) {
  // If there is not just a key, the function is used to write a cookie
  if(arguments.length >1) {
    // But if the value is null, the function is used to erase a cookie
    if (value === null) {
      value = '';
      expiryDay = -1;
    }
    // If the date parameter is a number,
    // create a date from today + this number of days
    if (typeof expiryDay == "number") {
      var day = new Date();
      day.setDate(day.getDate() + expiryDay);
      expiryDay = day;
    }
    // Create the cookie string
    document.cookie = [
      key , '=' , encodeURIComponent(value),
      '; expires=' , expiryDay.toUTCString(),
      '; path=/'
    ].join("");
  // If there was only a key, the function is used to read a value
  } else if(result = new RegExp("(?:^|; )" +key+ "=([^;]*)")
           .exec(document.cookie))
    return decodeURIComponent(result[1]);
  return false;
};

Remember that cookies are only meant to store string values. If you want to write a number, remember to parse it back to a number after reading it.

I’ve also turned this cookie library in a plugin for the famous jQuery library, check the code of jquery.cookie on github.

Changes in VML for IE8, or "what feature can the IE dev team break for you today?"

The final version of Internet Explorer 8 has been released by Microsoft on the 20th of March. It’s packed with a fully CSS2.1 compliant rendering engine, separate process per tab (just like in Google Chrome, good for stability, bad for memory consumption), performances are claimed to have improve, it introduces new features that will benefit the user such as Accelerators and Web Slices (available in Firefox with the WebChunks addon), as well as interesting features that will benefit the developer, such as a debug tool inspired by Firebug, the onhashchange event and some other interesting things. What as not been included in Internet Explorer 8? The list is unfortunately too long:

  • A Javascript Virtual Machine that could compete with the ones shipped by other browser vendors. The performances of Safari (squirrelfish extreme), Chrome (V8), Firefox (Tracemonkey) and Opera (Carakan) are still far better. But why would Microsoft create a Web browser shaped for the next generation of Web Applications? They are actively promoting their Software As A Service platform and Silverlight, their own Rich Internet Application framework because they can make much more money by forcing developers to adopt their technologies than by making any contribution to the Open Web.
  • <canvas>, <svg> or <video> are missing, and again, this isn’t a big surprise. There is everything you need in Silverlight for vector graphics, audio and video , would the salesman say.
  • Only little support of CSS3. Considering the credit crunch this can actually be seen as an advantage. How would the developers justify their pay if they could use border-radius, border-image, text-shadow and such, straight away, without spending countless hours working around Internet Explorer weaknesses?

Now it’s time to get onto what they managed to break without telling anyone: VML. VML stands for Vector Markup Language, an XML based language aimed at describing vector graphics. It has been submitted to the w3c in 1998 and begins with the following status:

This document is a submission to the World Wide Web Consortium.  It is the initial draft of the specification of VML.  It is intended for review and comment by W3C members and is subject to change.

At the same time, Adobe proposed to the w3c a language with similar purposes: PGML. Eventually, the two standards were merged into what is now SVG… But Microsoft made the choice to ignore this standard and to implement only their original proposition into their browser. Considering the market share of Internet Explorer at that time (around 95%), was there any reason to bother about Web standards? The legacy of this regretted market share is that developers now have to deal with two standards when it comes to vector graphics on the Web, since SVG is the one used in every other browser. Fortunately, there are abstraction layers that allow Web developers to use such features with a lowest common denominator approach such as Dojo.gfx or raphaeljs. But sometime you need to get your hands dirty and to use directly VML for performance or technical reason. This is the case in my border-image script where I’m using only one VML element: image. This was before the first Internet Explorer 8 beta came out…

Although the VML specification hasn’t undergone any improvements or modifications since its publication back in 1998, Microsft development team felt like considerably changing the way their own standard should be handled:

  • The namespace declaration has to take a second argument to be functional:
    document.namespaces.add('v', 'urn:schemas-microsoft-com:vml', "#default#VML");
    instead of simply:
    document.namespaces.add('v', 'urn:schemas-microsoft-com:vml');
  • The selector for the behaviour rules needs to be slightly modified (more information follows).
  • For a standard compliance purpose, when setting dimensions or position of an element, the unit no longer defaults to ‘px’. It has to be explicitly specified
  • It is no longer possible to create a VML element outside of the DOM:
    var myFragment = document.createDocumentFragment();
    myFragment.insertAdjacentHTML('beforeEnd',
    '<v:rect id="myRect" fillcolor="blue" style="top:10px;left:15px;width:50px;height:30px;position:absolute;"></biv:rect>'
    );
    document.body.appendChild(myFragment);

    This rect element will not be displayed! And don’t try to modify its CSS properties with the developer tool, you are likely to crash the tab or even the browser, if you’re lucky. Fortunately, there is an easy fix for this bug: you just have to copy the outerHTML of the element into itself:
    var myRect = document.getElementById('myRect');
    myRect.outerHTML = myRect.outerHTML;
  • It’s no more possible to use % unit! This one is my favourite. The advantage of vector graphics is that they can be scaled without quality loss. Now tell me how you scale a graphic from which all components dimensions and position are set in pixels! You have to resort to using the onpropertychange event to recalculate one after the other those values each time you want to resize your graphic. But there is a lot of case where you simply can’t fix this bug. For example, if you had an rectangle in a div: the div has no dimensions and scales with its content, and you want the height of your rectangle to scale with the div. There was a time when it was possible to simply set the height of the rectangle to 100% (providing the position of the div is relative, of course). But it was probably too easy after all.
  • It’s no more possible to use the getAttribute method to access the values of a particular attribute of your element.
    var myRect = document.getElementById('myRect');
    // Not working in IE8
    myRect.setAttribute('fillcolor', 'green');
    // Still working everywhere
    myRect.fillcolor = 'green';

Remarkably, none of this change has been announced by the IE dev team, as far as I know. The only announcement made about VML can be found on a blog post:

Generic CSS prefix selectors are no longer supported in IE8 Standards Mode in order to provide standards-compliant CSS parsing. Most often, this affects pages trying to use CSS to attach behaviors to VML elements. This can cause a script error if set dynamically, or a silent failure when hard-coded into the CSS of a page. Ultimately the rule is not applied, leading to effects such as VML not displaying on a page.

v:* {
behavior: url(#default#VML);
}

SOLUTION: Explicitly specify each tag name you want to match when using CSS prefix selectors.

v: polyline
v: line {
behavior: url(#default#VML);
}

Can you spot the problem? There is actually nothing wrong with the * selector in CSS from a standard compliance point of view, to prevent the script from crashing if you set it dynamically, you simply have to put a space between the “v:” and the * (you need two backslashes in javascript):

v: * {
behavior: url(#default#VML);
}

I don’t really know what to think about that. On one hand it seems that some of this changes are just bugs (elements are still appearing in the developer tool with correct dimensions and position, they are simply not rendered), on the other hand, if they wanted to finish off VML and force developers to work with Silverlight, they couldn’t do it any better…

Just for fun, take a look at the VML documentation on MSDN, here is what you can still read:

  • About the height property:
    Units: A number with an absolute units designator (cm, mm, in, pt, pc, or px) or a relative units designator (em or ex). If no units are given, pixels (px) is assumed.
    Percentage: Value expressed as a percentage of the parent object’s height.
  • About the last parameter of the namespaces.add method:
    sUrl: Optional. String that specifies the URL of the element behavior to import into the namespace. Specifying this parameter is the same as calling the doImport method.

I learned about this namespace change in an obscure bug report in Microsoft connect. I had to figure out the rest by myself. I’m just afraid that I discovered those bugs too late. Now that Internet Explorer 8 has been made publicly available, there is only few chances for any change to be introduced in the rendering engine…

Update: Following the related post on ajaxian, Christian Effenberger pointed out that the setAttribute no longer work. I also want to state that the most obvious and easy way to fix all those bugs at once is to use the IE7 meta-tag:

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" />

IE7 is still the most common Web browser according to its market shares, so you have to make your website compatible with it (still painful, but nothing compared to its predecessor). Once your website is compatible with IE7, add this meta-tag and you will have compatibility with IE8 for the same price, with good ol’ VML implementation. Disclaimer: I do not recommand browser sniffing or browser hacks and should not be held responsible for bad use of the previous advice, period.

Update2: Changed the IE7 meta-tag and the link to the announcement on the IEBlog, thanks to dflock.