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.
</map> <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;"> </map>
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.
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.