reptile7's JavaScript blog
Friday, October 19, 2012
 
What an oldX It Is Getting drag( )ged
Blog Entry #267

The Dynamic HTML in Netscape Communicator examples conclude with the Changing Wrapping Width Example, which we take up in today's post. The Changing Wrapping Width Example presents code via which the user can increase or decrease the widths of a layer and the elements it contains by respectively dragging the layer rightward or leftward.

In the div below, move your mouse cursor into the bluish content area, hold down the primary mouse button, and move the mouse cursor to the right or left.

Resizing a layer/div and its contents


Netscape Communicator

Netscape Communicator is a suite of software components for sharing, accessing, and communicating information via intranets and the Internet.

Communicator includes components for navigation, email, discussion groups, HTML authoring, dynamic information delivery, real-time collaboration, calendaring and scheduling, IBM host communications, and Communicator management.

Communicator runs on 16 platforms and is available in Standard and Professional Editions.

Netscape offered two versions of the Changing Wrapping Width Example:

(1) A wrapping.htm version puts the Netscape Communicator content in a layer element.

<layer name="layer1" src="mytext.htm" left="100" width="300" bgcolor="#99bbff"></layer>

(2) A wrapcss.htm version puts the Netscape Communicator content in a div element.

#layer1 { position: absolute; include-source: url("mytext.htm"); left: 100px; width: 300px; background-color: #99bbff; border: 1px solid white; }
...
<div id="layer1"></div>


The Netscape Communicator content comes from an external mytext.htm file that is imported into the layer1 layer element via its src attribute and into the layer1 div element via its include-source CSS property. The mytext.htm document body comprises a horizontal rule, an h1 heading, and three paragraphs.

The two versions' JavaScript parts are almost identical - I'll note where they differ in the following section.

Script overview

The example JavaScript relies on several functions to do its work - we'll take 'em in chronological order. Holding down the primary mouse button in the layer1 content area calls a begindrag( ) function.

var layer1 = document.layers["layer1"], oldX;
layer1.document.onMouseDown = begindrag;
function begindrag(e) { ...
    layer1.document.onmousemove = drag;
    oldX = e.pageX; ... }


The begindrag( ) function coassociates the layer1 layer's document, mousemove events, and a drag( ) function, and it also gets the mouse cursor's starting x-coordinate and assigns it to an oldX variable.

• The onMouseDown event handler appears as onMouseDown in the wrapping.htm source and as onmousedown in the wrapcss.htm source; modern browsers require an all-lowercase formulation for this type of statement but the lower camel case formulation is OK with Netscape 4.x.

• It might not be intuitively obvious to you that layer1.document provides access to the imported mytext.htm document - recall that an iframe uses a specialized contentDocument property to get at its src document - but it does do that.

e.pageX returns the cursor's x-coordinate relative to the left edge of the page (as opposed to the left edge of the layer1 layer).

With the primary mouse button held down, moving the mouse cursor in any direction calls the aforementioned drag( ) function.

function drag(e) {
    changeWidth(layer1, e.pageX - oldX);
    oldX = e.pageX; ... }


The drag( ) function's first order of business is to call a changeWidth( ) function.

var layerWidth = 300;
function changeWidth(layer, delta) {
    layerWidth = layerWidth + delta;
    if (delta != 0) layer.load("mytext.htm", layerWidth); }


The drag( ) function passes two arguments to the changeWidth( ) function:
(1) the layer1 layer object, which is rechristened layer;
(2) e.pageX - oldX, the horizontal distance traversed by our mousemove, which is given a delta identifier - for a mousemove event, e.pageX returns the cursor's ending x-coordinate.

In the top-level (unfunctionized) part of the example JavaScript, the layer1 layer's width in pixels, 300, is assigned to a layerWidth variable. The first changeWidth( ) function body statement adds the delta distance to the layerWidth width. Subsequently an if statement checks if delta is not equal to 0, i.e., if there was a horizontal component to our mousemove: if true (the condition would return false for a purely vertical mousemove), then the mytext.htm file is reloaded into the layer layer and the layer layer's width is set to the new layerWidth via the load( ) method of the layer object - this is the line that actually resizes the layer1 layer and its contents when the layer is dragged.

In the wrapcss.htm source, the layerWidth += delta assignment is preceded by a layer.bgColor = "#99bbff"; statement; the load( ) command dispatches a load event that would discharge the #99bbff background color of the layer1 div element in the statement's absence. (Relatedly, the load event removes the div's solid white border.)

Moving back to the drag( ) function, oldX is set to e.pageX after the changeWidth( ) function has finished executing, setting the stage for our next mousemove.

The load( ) load event triggers a resetcapture( ) function when the drag( ) function has finished executing - we'll discuss the resetcapture( ) function in the following section.

Finally, releasing the primary mouse button calls an enddrag( ) function that uncouples mousemoves in the layer1 content area from the drag( )/changeWidth( ) action by setting the layer1 document's onmousemove event handler to 0.

layer1.document.onMouseUp = enddrag; /* It's onmouseup in the wrapcss.htm source. */
function enddrag(e) {
    layer1.document.onmousemove = 0; ... }


We are now free to move the cursor anywhere within the widened or narrowed layer1 area and the area will stay put. Or we can mousedown on and drag the area again as we see fit.

Capture voodoo

Omitted from the previous section's code are three document.captureEvents( ) commands that play important roles in the example's operation. Unfortunately, neither the example text nor the Event Capturing section of the JavaScript 1.3 Client-Side Guide sheds any real light on why these commands are required: I'll explain them as best I can.

The top-level part of the example JavaScript includes the following command:

layer1.document.captureEvents(Event.MOUSEUP|Event.MOUSEDOWN|Event.MOUSEDRAG);

JavaScript 1.2 equipped the document object with onMouseDown and onMouseUp event handlers so you might not think that we would need to capture mousedown and mouseup events for the layer1 document, and mousedown/mouseup capture is indeed unnecessary if the mouse cursor is maneuvered outside the layer1 h1 and paragraph text.

But suppose we mousedown right on the Netscape Communicator heading or one of the paragraphs. In the absence of capture, our mousedown will be dispatched to the underlying h1 or p element - the browser will see the mousedown as the beginning of a text selection process - but it will not bubble up to the element's layer1.document ancestor (recall that Netscape 4.x does not support event bubbling), so if we want to be able to mousedown anywhere within the layer1 area and call the begindrag( ) function so we can get the layer1 dragging under way, then we're going to have to intercept the mousedown at the layer1.document level.

It is actually not necessary to capture mouseup events at this stage, but we'll definitely need to capture them after we reload mytext.htm into the layer1 layer (vide infra).

The Event.ONMOUSEDRAG subargument initially threw me for a loop as there is no onMouseDrag in classical JavaScript's list of event handlers. Interestingly, however, the mysterious Event object/function (the generic/default Event object according to Danny Goodman although Netscape doesn't say anything about it in the JavaScript 1.2/1.3 specifications) can with modern browsers be probed via a for...in loop

var result = "";
for (var i in Event) result += "Event." + i + " = " + Event[i] + "<br>";
document.write(result);


and the result, which varies from browser to browser, does include an Event.MOUSEDRAG = 32 line when using Firefox, Google Chrome, or Safari. Still, I wondered, "Will anything bad happen if I subtract the Event.ONMOUSEDRAG subargument?" I took it out and there were no ill effects as far as I could tell.

Moving on, the begindrag( ) function body begins with:

layer1.document.captureEvents(Event.MOUSEMOVE);

The onMouseMove event handler was also introduced in JavaScript 1.2 but Netscape didn't associate it with any objects at all (onMouseMove is unique in this respect), so we'll need to capture mousemoves at the layer1.document level in order to register the drag( ) listener on the layer1 document. Indeed, Netscape states that mousemove events are not even dispatched by Netscape 4.x in the absence of capture.

As it happens, the preceding captureEvents( ) commands are only good for our first mousedown and mousemove; these commands are cleared by the load( ) operation of the changeWidth( ) function and must be repeated after the mytext.htm content has reloaded.

layer1.onload = resetcapture;
function resetcapture( ) {
    layer1.document.captureEvents(Event.MOUSEUP|Event.MOUSEDOWN|Event.MOUSEDRAG|Event.MOUSEMOVE); }


We are still mousedowning at the time of the first load( ) operation. If we want to mouseup over the h1 heading or a paragraph and call the enddrag( ) function, and not have the mouseup interpreted as the end of a text selection process, then the mouseup must be intercepted at the layer1.document level, and thus the Event.ONMOUSEUP subargument is needed here. The Event.ONMOUSEDRAG subargument can again be thrown out.

Release the mousemove

The enddrag( ) function sports a

layer1.document.releaseEvents(Event.MOUSEMOVE);

command that stops the dispatch of mousemove events when we move the mouse cursor. The releaseEvents( ) command makes the layer1.document.onmousemove = 0; statement unnecessary: if we're not dispatching mousemove events in the first place then there's no need to 'turn them off'.

In the following entry, I'll briefly comment on how the original example works in practice and then I'll tell you what I did to modernize the code.

Comments:
Thanks for the informative article, it was a good read and I hope its ok that I share this with some facebook friends. Thanks.
 
It's OK.
 
Post a Comment

<< Home

Powered by Blogger

Actually, reptile7's JavaScript blog is powered by Café La Llave. ;-)