(2001-12-04) Updated the behaviors to work inside frames and framesets.
The other day I got an email with the question how to drag a window using a div element. And in my eternal struggle to create new useful scripts I accepted the challenge.
After getting the basic scripts to both move and resize a window by dragging any element on the page I converted the scripts to two behaviors.
To use these behaviors you need to attach the behavior to an element in the normal way. For the resize behavior
you also need to add a custom attribute called resizedirection
. This can can be set to any of the following values;
e, w, s, n, se, sw, ne or nw, representing the direction to resize the window.
<style type="text/css"> .winmove { behavior: url("winmove.htc"); } .winresize { behavior: url("winresize.htc"); } </style> ... <span class="winmove">Move</span> <span class="winresize" resizedirection="se">Resize</span>
Even though drag and drop scripts are fairly straight forward, there are some issues when an actual window is supposed to be moved.
If you want to drag a window it is very likely that the mouse leaves the actual
document container and therefore you would not receive any mouse move events.
The solution to this is to use capturing. This is done by calling the method
setCapture()
on an actual element. Once this is called all mouse events
are captured by this element and therefore you will be able to receive mouse move (and mouse up)
events even when the mouse pointer is outside the actual element.
function startMove() { _dx = event.screenX - getLeft(); _dy = event.screenY - getTop(); element.attachEvent("onmousemove", doMove); element.attachEvent("onmouseup", endMove); element.attachEvent("onlosecapture", endMove); element.setCapture(); event.cancelBubble = true; }
The function startMove
is called when the mouse is pressed down on the element
and as you can see we attach event listeners to onmousemove
, onmouseup
and onlosecapture
before we actually set the capture. The event
onlosecapture
is called if something interferes with the capture forcing it to lose the
capture (for example a call to window.alert()
).
IE does not provide any DOM interface to actually read the position of a window. It
only provides a method to set it (window.moveTo(x, y)
.However all is not lost because there
are two properties called window.screenLeft
and window.screenTop
.
These returns the position of the upper left corner of the document container so what we need to
do is to figure out the distance between the upper left corner of the window and the upper left corner
of the document (this is usually known as the insets). This can be done by moving the window to a
known position and calculate the difference of the window position and the document position. After
this we can move back the window to the original place because we now know the insets and the
previous location of the document.
function getInsets() { // Store the old document position var oldScreenLeft = window.top.screenLeft; var oldScreenTop = window.top.screenTop; // if no previous inset calculated assume one if (window.top._insets == null) window.top._insets = {left: 5, top: 80}; // move to a known position window.top.moveTo(oldScreenLeft - window.top._insets.left, oldScreenTop - window.top._insets.top); // Measure the new document position var newScreenLeft = window.top.screenLeft; var newScreenTop = window.top.screenTop; // ... and store the insets result var res = { left: newScreenLeft - oldScreenLeft + window.top._insets.left, top: newScreenTop - oldScreenTop + window.top._insets.top }; // move back the window to its original place window.top.moveTo(oldScreenLeft - res.left, oldScreenTop - res.top); // and backup the insets for next time window.top._insets = res; return res; }
Now that we can get the insets we can easily define the functions
getLeft()
and getTop()
.
function getLeft() { return window.top.screenLeft - getInsets().left; } function getTop() { return window.top.screenTop - getInsets().top; }
The same problem as with the position applies to the size of the window.
One can only set the size using window.resizeTo(w, h)
. The solution
to find the window size is the same as for getting the position but we use the
document size and resize the window instead. To get the size we return the
clientWidth
and clientHeight
of the topmost (visual) element in the
document. Notice that this is different in IE6 if it is put in CSS1 compatible mode.
function getInnerSize() { var el = window.top.document.compatMode == "CSS1Compat" ? window.top.document.documentElement : window.top.document.body; return { width: el.clientWidth, height: el.clientHeight }; }
Now we can create the getSize()
function. The algorithm for this is
very similar to the earlier one for the position.
function getSize() { // Store old size var oldInnerSize = getInnerSize(); // if no previous diff assume one if (window.top._diff == null) window.top._diff = {width: 10, height: 90}; // resize to known size window.top.resizeTo(oldInnerSize.width + window.top._diff.width, oldInnerSize.height + window.top._diff.height); // calculate inner size again var newInnerSize = getInnerSize(); // store diff result var diff = { width: oldInnerSize.width - newInnerSize.width + window.top._diff.width, height: oldInnerSize.height - newInnerSize.height + window.top._diff.height }; // restore size to old size window.top.resizeTo(oldInnerSize.width + diff.width, oldInnerSize.height + diff.height); // backup diff for future calculations window.top._diff = diff; return { width: oldInnerSize.width + diff.width, height: oldInnerSize.height + diff.height }; }
For the complete code download the archive.