Michael Wallent
Lead Program Manager
Microsoft Corporation
June 11, 1998
The following article was originally published in the Site Builder Magazine (now known as MSDN Online Voices) DHTML Dude column.
Before starting, I owe you an apology. The DHTML Dude has been absent lately. What was I doing? Well, we on the Internet Explorer team were busily working on the next release -- version 5.0. This week, we released the Developer Preview of Internet Explorer 5. Whew. This month I'm going to make up for my absence with a discussion of one of the most exciting new features in Internet Explorer 5 -- dynamic properties.
What are dynamic properties? If you look at any property on HTML elements, they all take "Scalar" values. The values are absolute. Examples: Color = Red, Font = Arial, Source=MyImage.GIF. You can vary any of these properties with script. This is the essence of Dynamic HTML -- changing properties in response to some sort of event. Dynamic properties allow any property to be set to an expression. Here's the trick: We calculate the dependency graph, so that if any value on the document changes and there is a Dynamic Property dependent on that value, the Dynamic Property is re-evaluated, and the new value is applied.
Note that circular references are not allowed. If there is a circular dependency, all properties in the circularity will not be calculated. On the bright side, circularity detection is done at the property level, so it's possible for element A's width to be dependent on element B's height, and B's top to be dependent on A's left. That's not circular; it's okay.
Dynamic properties can be applied in two ways. There is a new method on every element, and on the style object that can be set in script.
MyElement.setExpression("innerHTML", "document.body.clientWidth"); MyElement.style.setExpression("fontSize", "document.body.clientWidth / 20");
The first parameter is the name of the property, and the second parameter is the expression. An optional third parameter lets you set the language of the expression. Note that you call the setExpression() method directly on the object that has the property. This is very important when setting Cascading Style Sheets (CSS) properties, because if you call the setExpression() function on the element itself and set the CSS property name, it will not work.
Every function and feature available in the object model can be used in the expression. However, the dependency mechanism picks up only element and property names referenced in the expression itself. For example:
MyElement.setExpression("fontSize", "someexpression(document.body.clientWidth)");
This will only track dependency on the clientWidth property. Other properties can be referenced in the function, but, if they change, the expression will not be automatically recalculated. If for some reason you require the expressions to recalculate, you can call the document.recalc() method. This method takes a single parameter, which is false by default. If you call document.recalc(), any expressions that are currently dirty will be re-evaluated. (Actually, this should never be the case; those properties should get called automatically, so if you find yourself having to call document.recalc() with the parameter missing or set to false, please file a bug report.) If document.recalc(true) is called, then that forces all expressions in the document to be re-evaluated.
For CSS properties, there is an even easier way. Each CSS property can take as a value a function object. The function object contains the expression. For example:
<div style="font-size: expression(document.body.clientWidth / 20);">One of the benefits of using the function notation over the setExpression() method is that the function notation degrades to other browsers much more gracefully -- they just ignore it. With setExpression(), you must write conditional code, sniffing the browser string, to make sure that you don't call a method that will return an error.
All well and good, but what on earth can you do with dynamic properties?
One of the primary reasons we implemented this feature (no, it wasn't just because it's cool) was to make CSS Positioning easier to use. One of the first questions I'm always asked when I discuss CSS Positioning is, "How do I handle different size screens, or resizing the browser?" The answer in Internet Explorer 4.0 is to trap the onload and onresize events, and write script to reposition or resize the elements. Possible, but not trivial.
Believe it or not, one of the hardest things to do was to keep an element aligned to the right-hand side of a document, or perfectly centered. It was painful.
Here's a double-demo that does both. To view properly, you must be using Internet Explorer 5.
The first version uses the setExpression(), while the second uses the function notation in CSS. Resize the document after it loads, and notice how the colored squares stay in their places.
Let's examine the first example:
<html> <head> <title>Sticky Positioning With Script</title> <style> BODY {font-family: arial; font-size: 18pt; font-weight: bold;} DIV { width: 80px; height: 80px; text-align: center; position: absolute; } </style> </head> <body> <div id=Blue style="background: blue;"> Top Left</div> <div id=Red style="background: red;"> Bottom Left</div> <div id=Green style="background: green;"> Top Right</div> <div id=Yellow style="background: yellow;"> Bottom Right</div> <div id=Purple style="background: purple"> Center</div> <script> Blue.style.setExpression("posLeft", "document.body.clientLeft + 10"); Blue.style.setExpression("posTop", "document.body.clientTop + 10"); Yellow.style.setExpression("posLeft", "document.body.clientWidth - 90"); Yellow.style.setExpression("posTop", "document.body.clientHeight - document.body.style.border - 90"); Red.style.setExpression("posLeft", "document.body.clientLeft + 10"); Red.style.setExpression("posTop", "document.body.clientHeight - document.body.style.border - 90"); Green.style.setExpression("posLeft", "document.body.clientWidth - 90"); Green.style.setExpression("posTop", "document.body.clientTop + 10"); Purple.style.setExpression("posLeft", "(document.body.clientWidth - 80)/2"); Purple.style.setExpression("posTop", "(document.body.clientHeight - 80)/2"); </script> </body> </html>
Initially, each element has no position, but when the expressions are evaluated, the element's top and left are set based on the input expression.
Let's take a closer look at how the center element, "Purple," gets positioned.
<div id=Purple style="background: purple"> Center</div> ... Purple.style.setExpression("posLeft", "(document.body.clientWidth - 80)/2"); Purple.style.setExpression("posTop", "(document.body.clientHeight - 80)/2");
Purple is just an absolutely positioned <DIV>. Note how the setExpression() method is called on the Purple's style object, as the target property posLeft is a property of that style object (not Purple itself).
First, let's look at how the left position of Purple is set. We'd like it to always appear in the dead center of the page. The page width is exposed on the clientWidth property of the body. Using the page width, and the width of Purple (80 pixels), the math is straightforward.
left = (body width - Purple width) / 2
Changing that into a JScript expression, we get
left = (document.body.clientWidth - 80 ) /2
More elegantly, instead of using a fixed value (80) for the width, we could actually look that up as well, and get
left = (document.body.clientWidth - Purple.clientWidth) / 2
Now that we have the right function, it can be applied with the setExpression() method as shown above.
An aside: The reason we can't look at Purple.style.posWidth is that the width of all the <DIV>s on the page is set with a global style sheet. Purple.style.posWidth would return null. In Internet Explorer 4.0, only values set with inline styles or with script are available on the style object. To remedy this, Internet Explorer 5 introduces the currentStyle object on each element. The currentStyle object is a read-only representation of all the current CSS values of an object. All CSS properties are available, no matter if they are applied to the element because of style sheet settings or inheritance. (Ooooh, version 5.0!)
Here is the source for the second demonstration that doesn't use script.
<html> <head> <title>Sticky Positioning With No Script</title> <style> BODY {font-family: arial; font-size: 18pt; font-weight: bold;} DIV { width: 80px; height: 80px; text-align: center; position: absolute; } </style> </head> <body> <div id=Blue style="left: expression(document.body.clientLeft + 10); top: expression(document.body.clientTop + 10); background: blue;"> Top Left</div> <div id=Red style="left: expression(document.body.clientLeft + 10); top: expression(document.body.clientHeight - document.body.style.border - 90); background: red;"> Bottom Left</div> <div id=Green style="left: expression(document.body.clientWidth - 90); top: expression(document.body.clientTop + 10); background: green;"> Top Right</div> <div id=Yellow style="left: expression(document.body.clientWidth - 90); top: expression(document.body.clientHeight - document.body.style.border - 90); background: yellow;"> Bottom Right</div> <div id=Purple style="left: expression((document.body.clientWidth - 80)/2); top: expression((document.body.clientHeight - 80)/2); background: purple;"> Center</div> </body> </html>Notice how the same expressions are used, but now they are set directly on each element, using the proposed CSS function notation.
One more demo for this month -- the resizing text demonstration. This demo ensures that the text will fit without wrapping, no matter what the browser's width.
View third sample. (Requires Internet Explorer 5.)
Here's the source:
<html> <body> <h1 style="font-family: verdana; text-align: center; font-size: expression(document.body.clientWidth / 13)"> Internet Explorer 5 </h1> </body> </html>That's all there is to it. Of course, if the window were to be sized really, really small, you might see some rounding and font-select errors that cause the text to display on two lines. You can write a more sophisticated function to handle such obscure cases, but this example illustrates the basic point, and will work well for most practical applications.
Just a couple of issues of which you should be aware in the Developer Preview of Internet Explorer 5:
Even with those issues, dynamic properties is an exciting and easy new way to take advantage of the power of Dynamic HTML. Effects that were previously difficult, if not impossible, can now be done without any script blocks on your page.
In the upcoming months, I'll be exploring more of the cool new features available in Internet Explorer 5. It's going to be fun.
Michael Wallent is Microsoft's lead program manager for Dynamic HTML.
As you might imagine, naming new features at Microsoft is quite a challenge. We have many, many, many products, and sometimes the engineering teams aren't the best at coming up with names.
In the case of dynamic properties, the first name we came up with was "Dependent Expression Based Attributes," a name only its mother could love. Even the acronym -- DEBA -- made no sense at all.
We on the product team went back and forth with the marketing team. Pick one from column A: "Expression." "Dependent." "Automatic." Now pick one from column B: "Properties." "Attributes." "Expressions." Personally, I liked "Expression Expressions," but that's just me. The deadline for documentation and public release was coming close. We had no name. We had to do something.
So we chose a name that was so hideous that it would force the stony arm of marketing into action. "IntelliProperties." What a hoot. People liked it. We said, "It's a joke name. We thought it was funny." Cooler heads prevailed, and we now have a real name: "Dynamic properties."
Not much better, is it?
-- M.W.For a comprehensive guide to information on DHTML, from introductory overviews to advanced how-to's, consult the Dynamic HTML pages in the MSDN Online Web Workshop.