Bookmark and Share

Bride of Windows

See the demo page for the finished version of the code.

Setting Up Event Handling

In order for the windows to respond to user actions, several of the components need to capture and process the appropriate events. First the window frame (the outermost window DIV) is set to capture various mouse events so the window can be resized by dragging it.

  // Set up events.

  this.frame.parentWindow = this;
  this.frame.onmouseover  = winResizeCursorSet;
  this.frame.onmousemove  = winResizeCursorSet;
  this.frame.onmouseout   = winResizeCursorRestore;
  this.frame.onmousedown  = winResizeDragStart;

Then the title bar element is set up for mouse events so the window can be moved by dragging it.

  this.titleBar.parentWindow = this;
  this.titleBar.onmousedown  = winMoveDragStart;
  this.titleBar.onmouseup    = winMoveDragStop;

The client area is set up for the onclick event so that the user can make the window active by clicking on it.

  this.clientArea.parentWindow = this;
  this.clientArea.onclick      = winClientAreaClick;

Note that a user-defined property called parentWindow is assigned to each of these elements. This is done so the event handler functions can easily reference the actual window object itself rather than just the object that fired the event.

This is also seen in the HTML code for the window buttons image maps. Recall that each AREA has an onclick event handler set which refers to the area's parentWindow.

  <area shape="rect" coords="0,0,13,13" href=""  alt="" title="Minimize"
      onclick="this.parentWindow.minimize();return false;">
  <area shape="rect" coords="14,0,27,13" href="" alt="" title="Restore"
      onclick="this.parentWindow.restore();return false;">
  <area shape="rect" coords="28,0,41,13" href="" alt="" title="Close"
      onclick="this.parentWindow.close();return false;">

This reference is actually set in the Window() function using the following code.

  for (i = 0; i < this.titleBarMap.childNodes.length; i++)
    if (this.titleBarMap.childNodes[i].tagName == "AREA")
      this.titleBarMap.childNodes[i].parentWindow = this;

Again, adding this property to each AREA element object makes it easy for the corresponding event handler to identify the window it belongs to.

Window Sizing and Display

The next section of the initialization code sets up some values to be used when resizing the window and also fixes some potential display problems.

Initially, each window's size is defined by the width style set on its frame and the height style set on its client area. To change the window dimensions, the code will simply changes these two values. All the other window elements should adjust accordingly.

However, there are a couple of situations where the window may not display as expected. The actual appearance depends on the browser but the situations have the same root cause.

First, if the title bar text is too long to fit within the width set by the frame, it will either spill over (Netscape) or cause the frame to appear wider than it should be (Internet Explorer). To avoid this, we may need to set an explicit width on the title bar text's element, forcing the browser to clip the text.

The second situation affects only Internet Explorer. Recall that the client area element has a style setting of overflow:auto. So if its content is too wide or too long to fit the available space, it should clip it to fit that space and add scroll bars as necessary.

The client area has an explicit height set for it, so vertical clipping and scrolling works as expected. But it does not have an explicit width set. With IE, if the content of the client area is too wide to fit in the space provided by the window frame, it will simply widen the client area (and, consequently, the window frame) to fit the content without clipping. Again, this can be avoided by setting an explicit width on the client area element.

So, when setting the window frame's width, the script may also need to set a width on the title bar text and, for IE, the client area. The next part of the code calculates some values that will be used to determine the proper widths for these given a specific frame width.

Element Measurements

To do this, it will have to measure the dimensions of various window elements and calculate the corresponding values relative to the style width setting. It also sets minimums for the window width and height.

In order to get the proper measurements, the script will have to temporarily alter some style settings. Since the window style class has visibility:hidden defined, this can all be done without the user seeing the changes. Once the necessary values are found, the code will restored the window to its original state.

According to "box model" of the CSS standard, the style width parameter applies to the contents of an element, which does not include any padding, borders or margins for the element.

For example, an element with a style of width:200px; padding:8px; border-width:2px; should really take up 220 pixels horizontally on the screen. This is reflected in the element's offsetWidth property which would be 220. Internet Explorer 5.5 (or version 6.0 in compatibility mode) does not follow the standard. Instead, it includes any padding, borders and margins within the width style value when rending an element. So in this example, the offsetWidth would be 200 pixels.

When measuring element dimensions to calculate style values, the code must account for this difference, if any.

First is saves the initial left and width values set on the frame. Then it moves the window to the far left, actually off the page.

  // Calculate the minimum width and height values for resizing
  // and fix any initial display problems.

  var initLt, initWd, w, dw;

  // Save the inital frame width and position, then reposition
  // the window.

  initLt = this.frame.style.left;
  initWd = parseInt(this.frame.style.width);
  this.frame.style.left = -this.titleBarText.offsetWidth + "px";

The reason for moving the window is that, later on, the code will need to temporarily remove the width setting on the frame. If the window happened to be near the right-hand edge of the browser, the title bar text and buttons might wrap and throwing off the calculations. Moving the window off the left side of the page by a distance equal to the text width ensures that this will not happen.

Next, for IE, it defines a property called widthDiff for use in setting the client area width. It starts calculating the value by comparing the frame element's actual width to the client area's.

  // For IE, start calculating the value to use when setting
  // the client area width based on the frame width.

  if (browser.isIE) {
    this.titleBarText.style.display = "none";
    w = this.clientArea.offsetWidth;
    this.widthDiff = this.frame.offsetWidth - w;
    this.clientArea.style.width = w + "px";
    dw = this.clientArea.offsetWidth - w;
    w -= dw;     
    this.widthDiff += dw;
    this.titleBarText.style.display = "";

It first removes the title bar text from the display by setting its display style to none. This ensures only the client area can affect the frame's actual display width, not a long text title.

Note that the code accounts for the difference between the client area's style width and it's actual width. It does this by setting a value for the width style of the element then comparing it to the element's resulting offsetWidth.