7.8 Resizing Frames
NN 6, IE 4
7.8.1 Problem
You
want a script to adjust the size of one or more frames in a frameset,
including resizing a frame to zero width or height to hide a frame.
7.8.2 Solution
Apply a variation of the script shown in Example 7-1
in the Discussion to your frameset (customized with your
frameset element's ID). The
script goes in the framesetting document and is invoked by a user
interface element in one of the frames. For example, a button
specification in one of the frames invokes the toggleFrame(
) function as follows:
<button onclick="parent.toggleFrame( )">Hide/Show Navbar</button>
You could also use hyperlinks or linked images to act as clickable
triggers for the action.
7.8.3 Discussion
In browsers whose object models support access to all element types
(IE 4 and later or NN 6 and later), you can control the values of the
cols and rows attributes of the
frameset element via properties of the same names.
The
frameResize.js code library shown in Example 7-1 adjusts only the cols
property of a typical two-column frameset. In this case, the width of
the lefthand frame is set to zero, effectively hiding the frame from
view.
Example 7-1. Frame-resizing functions in frameResize.js
// global to save previous setting
var origCols;
// resize lefthand franem
function resizeLeftFrame(left) {
var frameset = document.getElementById("masterFrameset");
origCols = frameset.cols;
frameset.cols = left + ", *";
}
function restoreFrame( ) {
document.getElementById("masterFrameset").cols = origCols;
origCols = null;
}
function toggleFrame( ) {
if (origCols) {
restoreFrame( );
} else {
resizeLeftFrame(0);
}
}
The code begins by declaring a global variable,
origCols, that preserves the original
cols attribute setting so it can be used later to
restore the previous setting. Next, the resizeLeft(
) function is wired to apply a numeric
value (arriving as an argument) to the first of two values of the
frameset's cols property. Before
doing so, the function stores a copy of the property value in the
origCols variable. To return the frameset to its
previous state, invoke the restoreFrame(
) function. These two functions are
controlled through a master function, toggleFrame(
), invoked from user interface elements
located in one or more of the visible frames.
A logical user interface idea behind the frame toggle is to change
the text or image signifying the purpose of the click action. For
example, when the frame is showing, the label says something like
"Hide Navbar"; when the bar is
invisible, the label says "Show
Navbar." You can do this quite easily on IE
browsers, but not Netscape 6 or 7. Here's how to
implement the toggleFrame( ) function to work this
IE magic:
function toggleFrame(elem) {
if (origCols) {
restoreFrame( );
elem.innerHTML = "<<Hide Navbar";
} else {
resizeLeftFrame(0);
elem.innerHTML = "Show Navbar>>";
}
}
The button in the frame is:
<button onclick="parent.toggleFrame(this)"><<Hide Navbar</button>
Figure 7-1 shows the two states of the button with
respect to the frameset.
The reason Netscape 6 and 7 won't let you handle the
UI portion so elegantly is that when the frameset resizes itself to
the new specifications, the browser automatically reloads the pages
(needlessly, in my opinion). Thus, the default value for the UI
element reverts to the value as delivered from the server.
To counteract the problem—and provide a cross-browser service
to all users—you can preserve the state of the
frame's visibility in a cookie, and use the cookie
to determine the text of the button (or image URL if you prefer) each
time the frame loads. This added work lets the
user's preference persist from session to session,
thus enhancing the visitor's enjoyment of the site.
Coding for this enhancement has to work around an unfriendly bug in
some versions of IE, where an event handler invoking a function in
the parent provides incorrect information about the actual target of
the event. The solution requires a little bit of indirection, but the
result works in all browsers that support W3C DOM syntax.
The page in the frame that always stays visible defines a placeholder
span element whose content is filled only if
the browser is of a required minimum scriptability:
<span id="togglePlaceholder"></span>
Scripts include a linked cookies.js library (see
Recipe 1.9):
<script type="text/javascript" src="cookies.js"></script>
An onload
event handler in that frame's page initiates the
creation of the UI element for the frame state toggle:
function setToggleUI( ) {
var label = "<<Hide Navbar";
if (document.getElementById) {
if (getCookie("frameHidden") = = "true") {
label = "Show Navbar>>";
}
var newElem = document.createElement("button");
newElem.onclick = initiateToggle;
var newText = document.createTextNode(label);
newElem.appendChild(newText);
document.getElementById("togglePlaceholder").appendChild(newElem);
}
}
Notice that the button element's
onclick event handler invokes the local function
initiateToggle( ) in the same frame. This
indirection allows the accurate event target to be reported in IE.
That function reads the target and sends it along to the revised
toggleFrame( ) function in the parent frame:
function initiateToggle(evt) {
evt = (evt) ? evt : event;
var elem = (evt.target) ? evt.target : evt.srcElement;
if (elem.nodeType = = 3) {
elem = elem.parentNode;
}
parent.toggleFrame(elem);
}
In the parent frame (which must also link in the
cookies.js library from Recipe 1.9), the
toggleFrame( ) function now not only adjusts the
cols setting of the frameset, but it also saves
the current state value to a cookie and sets the
button's label to the new version (since IE
doesn't reload the frameset, it needs the new values
directly):
function toggleFrame(elem) {
if (origCols) {
elem.firstChild.nodeValue = "<<Hide Navbar";
setCookie("frameHidden", "false", getExpDate(180, 0, 0));
restoreFrame( );
} else {
elem.firstChild.nodeValue = "Show Navbar>>";
setCookie("frameHidden", "true", getExpDate(180, 0, 0));
resizeLeftFrame(0);
}
}
All other functions in the parent stay the same as in the original
solution.
That Netscape 6 and 7 (and perhaps later versions, as well) reload
the frameset each time you adjust a frame's
dimensions argues against implementing gradually expanding or
contracting frames. Flicker from each reload may drive users crazy.
7.8.4 See Also
Recipe 7.7 for reading the current pixel dimensions of a frame;
Recipe 1.9 for details on the cookies.js
library.
|