dean.edwards.name/IE7/notes/version%200.3.html


sourceforge.net logo

notes

to accompany version 0.3 (alpha)

OK. How does it work?

In a nutshell, a DHTML Behavior loads and parses all style sheets (at page load) and re-writes them. Simulating CSS2 in Explorer is achieved by converting CSS selectors that Explorer does not understand into selectors that it does understand. Namely, classes.

This means that most of the CSS fixes are performed using CSS!

For example: p > a {
    /* blah blah blah */
}
becomes: .ie7_22 {
    /* blah blah blah */
}
and: [this="that"] {
    /* bling bling bling */
}
becomes: .ie7_23 {
    /* bling bling bling */
}
etc.

Explorer supports multiple class names. It has a buggy implementation (we fix this later) but it is sufficient for this solution.

Now it’s all well and good converting these unknown selectors into classes but just how do we assign those classes?

I considered writing some kind of query function to assign them but realised I already had one. CSS!

By using expressions (a Microsoft technology that allows the creation of Dynamic Properties) I can dynamically allocate classes according to CSS rules. Exactly what I want!

This means that a CSS rule that Explorer does not understand is split into two rules. The first is the original rule with the selector replaced by a class (as shown above). The second rule contains an expression that assigns the class of the first rule. It is best explained by example.

Consider this rule: .menu > .popup {
    background: yellow;
}
This is split in to two rules. The first is just a simple conversion of the .menu > .popup selector to a class: .ie7_99 {
    background: yellow;
}
Then we add another rule which contains an expression that allocates the above class: .menu .popup {
    -ie7-class:
    expression((parentElement.className.test(/\smenu\b/))?99:0);
}

If you are not familiar with JavaScript regular expressions then the above code will look like gobbledygook. Translated, it tests that the selected element’s parent is in the class "menu". If it is, then it assigns itself to the class "ie7_99". This is achieved by watching the style.ie-class property (using an onpropertychange event handler). When this property changes it allocates the "ie7_99" class.

function watch() {
 switch (event.propertyName) {
  case "style.ie7-class":
   // trigger for allocation of an ie7 class
   // the class name will now change
   addClass(event.srcElement);
   break;
  case "style.ie7-dynamic":
   // trigger for allocation of a dynamic ie7 class
   //  event handlers will be attached
   addDynamicClass(event.srcElement);
   break;

  ...

This technique is used throughout, with different expressions for each situation. It’s even used to fix the multiple class names bug that Explorer exhibits! Using this approach also preserves the original cascade of the style sheet.

Complex selectors may generate several rules which in turn allocate lots of classes.

For example, this selector: .visible my|menu > my|popup:hover {
    background: yellow;
}
Simply becomes: .ie7_6 {
    background: yellow;
}
But generates the following rules: .visible my\:menu {
    -ie7-class: expression(4);
}
.ie7_4 my\:popup {
    -ie7-class:
   expression((parentElement.className.test(/\sie7_4\b/))?5:0);
}
.ie7_5 {
    -ie7-dynamic: expression(6);
}

When the property style.ie7-dynamic changes, event handlers are attached to the target element. This enables the dynamic pseudo-classes :active, :focus and :hover. In the above example, when the appropriate event is fired, the element is dynamically allocated the class "ie7_6" which in turn loads the style background: yellow.

Attribute selectors ([attr="value"]) are handled slightly differently. They are also converted into classes but these classes are allocated programmatically (not using CSS selectors).

To improve performance, a separate style sheet is built containing all rules that allocate classes. This style sheet once applied, is then unloaded. If you change the structure of your document (by using the DOM to add nodes or just by inserting HTML) then you may wish to reload these rules. You can do this by calling document.recalc(). This method has been overriden to provide support for these additional classes/expressions.

I’ll document this solution in more detail at a later date. For the time being this should give you some insight if you want to read the source-code.

The source is a bit jumbled (I’ve had difficulty imposing order on it). The mechanism for loading the generated style rules is pretty complex. Hopefully I’ll think of a better way of doing it. But for the moment it seems to work...

Dean Edwards