Categories
Technology

X-Objects: Clipping

Copy found at Wayback Machine archive.

Clipping in the earlier releases of Mozilla and with Navigator 6.0 followed the CSS 2.0 release specification. Clipping boundaries are set at “0” for all four sides of the rectangle containing the content — to clip, you would offset the values, using positive numbers only.

This differed from the original implementation of IE 4.x and up and Navigator 4.x, and the original CSS-P specification submission. With these, the element’s right and bottom clipping value is set to the width and height of the element by default. To offset the top and left, increment the top, left values. To offset the right and bottom, decrement these same values.

Having a different clipping region framework was a challenge when it came to cross-browser development.

However, beginning with Mozilla build M16 (and Navigator 6.0 PR 2.0 and up), clipping for the new browsers is the same as it was for Navigator 4.x and for IE 4.x and up. Supposedly, the W3C will be issuing errata for CSS 2.0 to change the clipping framework to match this common usage.

On to the X-Objects

The X-Object does have several methods exposed on the interface to handle clipping:

  • objSetClipRect – set the clipping values
  • objGetClipRect – get the current clipping value, if any
  • objGetClipLeft – get the clipping rectangle’s left value
  • objGetClipRight – get the clipping rectangle’s right value
  • objGetClipTop – get the clipping rectangle’s top value
  • objGetClipBottom – get the clipping rectangle’s bottom value

Adding support for Mozilla/Navigator 6.0 didn’t change the implementation for clipping with the original cross-browser objects. The implementation for Navigator 4.x is still:

// clip object
function nsobjSetClipRect (top,left,bottom,right) {
	if (top == null) top = this.objGetClipTop();
	if (left == null) left = this.objGetClipLeft();
	if (bottom == null) bottom = this.objGetClipBottom();
	if (right == null) right = this.objGetClipRight();
	this.css2.clip.left = left;
	this.css2.clip.right = right;
	this.css2.clip.top = top;
	this.css2.clip.bottom = bottom;
}

function nsobjGetClipRect () {
   var strng;
   var left = this.css2.clip.left;
   var right = this.css2.clip.right;
   var bottom = this.css2.clip.bottom;
   var top = this.css2.clip.top;

   strng = "rect(" + top + "px, " + right + "px, " + 
                 bottom + "px, " + left + "px)";
   return strng;
}

// get current clip right 
function nsobjGetClipRight() {
	return this.css2.clip.right;
}

// get current clip left
function nsobjGetClipLeft() {
	return this.css2.clip.left;
}

// get current clip top
function nsobjGetClipTop() {
	return this.css2.clip.top;
}

// get current clip bottom
function nsobjGetClipBottom() {
	return this.css2.clip.bottom;
}

Navigator 4.x implemented the clipping values as separate values, which actually made the use of this CSS attribute a little easier than how it was implemented in IE. However, the IE implementation is one that can be shared by Mozilla/Navigator 6.0 — though it is more complicated:

// return clipping rectangle
function domGetClipRect() {
   return this.css2.style.clip;
}

// clip object
function domSetClipRect(top, left, bottom, right) {
   if (top == null) top = this.objGetClipTop();
   if (left == null) left = this.objGetClipLeft();
   if (bottom == null) bottom = this.objGetClipBottom();
   if (right == null) right = this.objGetClipRight();
   strng = "rect(" + top + "px, " + right + "px, " + 
                 bottom + "px, " + left + "px)";
  this.css2.style.clip = strng;
}

	
// clip object on left
function domGetClipLeft() {
	return get_entry(this,"left");
}

// clip object on right
function domGetClipRight() {
	return get_entry(this, "right");
}

// clip object at top
function domGetClipTop() {
	return get_entry(this,"top");
}

// clip object at bottom
function domGetClipBottom() {
	return get_entry(this,"bottom");
}

The main difference between the Navigator 4.x and IE implementation is that the IE treats the clipping property as one setting rather than four distinct settings. Based on this when accessing the existing the clipping property, it has to be parsed to get each of the clipping rectangle’s values.

Microsoft did provide read only properties to access each value of the clipping rectangle. However, as these aren’t usable with Mozilla/Navigator 6.0, I left the code within the object as is, and used parsing.

The parsing is handled through two helper functions, convert and get_entry, which are not exposed on the X-Object’s interface:

// convert string to value
function convert(strng) {
    var i = parseInt(strng);
    return i;
}

// get clipping value for specific dimension
function get_entry(obj,indx) {
	strng = obj.css2.style.clip;
        if (strng.length > 0) {
	   strng = strng.slice(5,strng.length-1);
	   var entries = strng.split(" ");
           }
        else {
            var entries = new Array(5);
            for (i = 0; i < entries.length; i++)
                entries[i] = "auto";
            }
	if (indx == "top") {
		if (entries[0] == "auto") 
                   return 0;
		else
		   return convert(entries[0]);
            }
	else if (indx == "left") {
		if (entries[3] == "auto") 
		   return 0;
		else
		   return convert(entries[3]);
		}
	else if (indx == "bottom"){
		if (entries[2] == "auto") {
		   return obj.objGetHeight();
                   }
		else
		   return convert(entries[2]);
              }
	else if (indx == "right") {
		if (entries[1] == "auto") 
		   return obj.objGetWidth();
		else
		   return convert(entries[1]);
		}
	
}

Let’s try the X-Object clipping functionality, with an example application created specifically for testing clipping.

Clipping Test Application

The test page has two different blocks in the page, each created in separate positioned DIV blocks. The topmost block contains an HTML form and form buttons, used to clip the second block’s left, top, right, and bottom rectangle’s values:

<BODY onload="create_objects(); setup()">
<DIV style="position:absolute; left:10; 
top:10; background-color: yellow; 
layer-background-color: yellow; padding-top: 20px; 
overflow: hidden;
width: 500; height:150; clip: auto">
<form action="">
<center>
<INPUT type="button" value="Clip to the Left" 
   onclick="clip_left()"> 
<INPUT type="button" value="Clip to the right" 
   onclick="clip_right()"><p>
<INPUT type="button" value="Clip on the top" 
   onclick="clip_top()"> 
<INPUT type="button" value="Clip the bottom" 
   onclick="clip_bottom()"><p>
<input type="button" value="Show the clip property" 
   onclick="show_clip()">
</center>
</FORM>
</DIV>

<DIV id="info" style="position:absolute; 
left: 250px; top: 180px; background-color: red; 
  width: 300px; height: 300px; overflow: hidden; 
layer-background-color: red">
<H1>Block with info</H1>
<p>
This is a block that contains two HTML elements: 
a header and a paragraph
</p>
</DIV>
</BODY>

A function is called when the page is loaded, setup, which sets the clipping region of the target DIV block, using values that differ based on browser type:

function setup() {
  
   theobjs["info"].objSetClipRect(0,0,300,300);
   }
}

These values set the clipping region to fit the existing DIV block parameters (300px wide, 300px tall).

The functions to create the clipping effect are:

// clip element
function clip_left() {
   var lft = theobjs["info"].objGetClipLeft();
   lft+= 10;
   theobjs["info"].objSetClipRect(null,lft,null,null);
}

function clip_right() {
   var rt = theobjs["info"].objGetClipRight();
   rt-= 10;
   theobjs["info"].objSetClipRect(null,null,null,rt);
}

function clip_top() {
   var top = theobjs["info"].objGetClipTop();
   top+= 10;
   theobjs["info"].objSetClipRect(top,null,null,null);
}

function clip_bottom() {
   var bt = theobjs["info"].objGetClipBottom();
   bt-=  10;
   theobjs["info"].objSetClipRect(null,null,bt,null);
}

function show_clip() {
   alert(theobjs["info"].objGetClipRect());
}

Try out the Clipping Example for yourself, using IE 4.x and up, Navigator 4.x, or Mozilla/Navigator 6.0.

For more complicated demonstrations of clipping, check out the associated article Demonstrating PhotoShop functionality using DHTML and X-Objects, which has four DHTML demonstrations using various effects, including clipping.
Categories
Technology

X-Objects: Events

Copy found at Wayback Machine Archive.

Event handling is pretty straight forward for all three impacted browsers (or browser object models). Events can be handled using two different techniques: through event handlers attached to HTML elements, or through event capturing. Navigator 4.x, IE, and Mozilla/Navigator 6.x all support both types of event handling, though the extent of coverage and the methods do differ.

Navigator 4.x only supports certain event handlers embedded within certain HTML tags — most notably this browser supports mouse event handlers such as onMouseOver and OnClick primarily within a link element (designated by <A;>). Additionally, Navigator 4.x also supports event capturing, using functionality similar to:

document.captureEvents(Event.KEYPRESS);

document.onKeyPress=keypress;

function keypress(e) {
   tmp = e.which;
   else if (navigator.appName == "Mozilla")

   // if space bar hit
   if (tmp == 32) {
	window.close();
	return;
	}
}

Both IE and Mozilla/Netscape 6.0 support event handlers on HTML elements as defined within the HTML 4.0 specification, at the least. This means that you can associate an onMouseOver event handler with all HTML elements that support this — which means most if not all of them.

Additionally, IE and Mozilla/Netscape 6.0 also support event capturing though the techniques do differ. In IE, you can use SetCapture or ReleaseCapture with HTML elements, and all mouse movements are then passed to the element. You can also assign event handlers to objects in script:

document.onKeyPress=keypress;

With Mozilla/Netscape 6.0, event handling is provided through the DOM Level 2 Events. For instance, to provide event handling for an event, you can add an Event Listener for the object:

document.addEventListener("keyup",keypress,true);

The addEventListener method takes the event as the first parameter, the event handler (function) as the second, and whether the event is captured or allowed to “bubble up” to other events with the third parameter. In the code just shown, the keyup event is captured and assigned to a function called “keypress”.

Within an event handler, you can access an Event object, though how this is accessed and used also differs between the browsers.

With Navigator 4.x, the Event object is passed, automatically, as a parameter to the event handler. Within IE, the Event object is accessed through the Window object during event handling. Mozilla/Netscape 6.0 supports DOM Level 2 event handling. An Event object is passed to the event handler, and information can be pulled from it.

An example of event handling and pulling event information from an Event object is the following, which pulls information about which key is pressed:

function keypress(e) {

   if (navigator.appName == "Microsoft Internet Explorer")
      tmp = window.event.keyCode;
   else if (navigator.appName == "Navigator")
	tmp = e.which;
   else if (navigator.appName == "Mozilla")
       tmp = e.keyCode;

   // if space bar hit
   if (tmp == 32) {
	window.close();
	return;
	}
}

In this code, if the space bar is hit (an ASCII value of 32), the current window is closed. The event handler itself is activated by the following code:

   // capture events
   if (navigator.appName == "Microsoft Internet Explorer")
	wdth = document.body.clientWidth;
   else if (navigator.appName == "Netscape")
   	document.captureEvents(Event.KEYPRESS);
   else
      document.addEventListener("keyup",keypress,true);


   // assign function to event handler
  if (navigator.appName != "Mozilla")
    document.onkeypress=keypress;

Notice that the Mozilla/Netscape 6.0 code captures the keyup event, not the keypress. The reason for this is that the actual key value is set with a keyup or keydown event, but not with keypress. Capturing keypress will give me the ASCII value for the control keys, such as Enter or Tab, but not the other keys such as “N” or the space bar.

Now that we’ve had a brief introduction to X-Object event handling, we’ll take a look at some examples using different types of event handling.

Embedded Event Handlers

The simplest approach to event handling is to embed event handlers right in the HTML element. However, you also have to take into account that Navigator 4.x doesn’t support embedded events for the most part — if you want to capture events such as mouse events, you need to encapsulate the target element within a link.

To demonstrate, I modified a DHTML example application that uses a transparent GIF to embed “hot spots” in a page. When the mouse moves over the hot spot, the transparent image is replaced by a visible image.

Within the page, the transparent images are created within links, which are themselves encapsulated within positioned DIV blocks:

<DIV id=hotspot1>
<a href="" onclick="return false" onmouseover="hotspot(0)">
<img src="blank.gif" width=80 height=32 border=0></a>
</DIV>

When the mouse moves over the image, the hotspot function is called with the number of the spot. This function then replaces the existing transparent GIF with the visible one:

dfltimg = new Image();
dfltimg.src = "logo.gif";

// access images and changing their source
// is relatively old
function hotspot(num) {

   if (navigator.appName == "Microsoft Internet Explorer" ||
       navigator.appName == "Mozilla")
	document.images[num].src = dfltimg.src;
   else
	document.layers[num].document.images[0].src = dfltimg.src;
}

As you might notice from the code, both IE and Mozilla/Netscape 6.0 can access the images array directly, regardless of what container HTML elements the images are in. With Navigator 4.x, however, you have to access the image object directly from the images collection for each document layer in the page.

Try out the HotSpot example yourself, using Mozilla/Netscape 6.0, IE 4.x and up, and Navigator 4.x. Expose the hotspots by moving your mouse over the page (there are 6 of them).

Finding an ASCII value

I have a utility DHTML application that prints the ASCII value of any key pressed to a form text box. I modified this utility to work with Mozilla as well as with Navigator 4.x and IE 4.x and up.

The application captures the keypress event (the keyup event for Mozilla), and attaches this event to an event handler function. The key capture and handling code is:

// handle keyboard events
if (navigator.appName == "Mozilla")
   document.addEventListener("keyup",keypress,true);
else if (navigator.appName == "Netscape")
   document.captureEvents(Event.KEYPRESS);

if (navigator.appName != "Mozilla")
    document.onkeypress=keypress;

// perform action based on keypress and state info
function keypress(e) {

   if (navigator.appName == "Microsoft Internet Explorer")
      tmp = window.event.keyCode;
   else if (navigator.appName == "Navigator")
	tmp = e.which;
   else if (navigator.appName == "Mozilla")
       tmp = e.keyCode;
   document.forms[0].elements[0].value = tmp;
}

You can try out the ASCII Utility yourself using Mozilla/Netscape 6.0, IE 4.x and up, and Navigator 4.x.

One thing you’ll find if you try this example with Mozilla/Netscape 6.0 is that the ASCII key value reflects the value of the key if it were capitalized. So, you get a value of “65” when you press an “A”, regardless of whether you’re pressing the shift key or not. I’m not sure if this is by design or by accident and am investigating this behavior. (Ahh, I do love working with alpha code).

Keep Away

I modified one more DHTML example that uses a combination of embedded event handlers and event capturing to create a “keep away” effect. The example uses an embedded event handler within a DIV block, which works with Mozilla/Netscape 6.0 and IE 4.x and up, but doesn’t with Navigator 4.x:

<DIV id="block" style="left: 200; top: 200; width: 104"
onMouseover="keep_away()">
<img src="yasd.gif" width=104>
</DIV>

To provide event handling for Navigator 4.x, the onMouseOver event for the DIV block is captured in script, and assigned to the same event handler:

:

function capture_events() {
// handle keyboard events
if (navigator.appName == "Netscape") {
	document.block.captureEvents(Event.MOUSEOVER);
      document.block.onmouseover=keep_away;
	}
}

Within the event handling code itself, the CSS positioned DIV block (containing an image of L’il Flame) is moved either right or left, in effect keeping the image away from the user’s mouse:

MAXHORIZ = 700;
MINHORIZ = 100;
var adjustor = 0;

if (navigator.appName == "Navigator") {
   adjustor = 50;
   }
else
   adjustor = 100;

function keep_away() {
	var newleft = theobjs["block"].objGetLeft();
      if (newleft > MAXHORIZ)
		adjustor=-1 * adjustor;
      else if (newleft < MINHORIZ)
		adjustor= -1 * adjustor;
      newleft= newleft+adjustor;
      theobjs["block"].objSetLeft(newleft);
}

As spacing is a bit different between IE and Navigator, the original code provided a different adjustor value for moving the DIV block. I’ve found that the same settings also work with Mozilla. Try the Keep Away example yourself, using Mozilla/Netscape 6.0, IE 4.x and up, and Navigator 4.x. Move your mouse over L’il Flame to cause it to move away from your mouse.

Categories
Technology

X-Objects: Events

Copy found at Wayback Machine Archive.

Event handling is pretty straight forward for all three impacted browsers (or browser object models). Events can be handled using two different techniques: through event handlers attached to HTML elements, or through event capturing. Navigator 4.x, IE, and Mozilla/Navigator 6.x all support both types of event handling, though the extent of coverage and the methods do differ.

Navigator 4.x only supports certain event handlers embedded within certain HTML tags — most notably this browser supports mouse event handlers such as onMouseOver and OnClick primarily within a link element (designated by <A;>). Additionally, Navigator 4.x also supports event capturing, using functionality similar to:

document.captureEvents(Event.KEYPRESS);

document.onKeyPress=keypress;

function keypress(e) {
   tmp = e.which;
   else if (navigator.appName == "Mozilla")

   // if space bar hit
   if (tmp == 32) {
	window.close();
	return;
	}
}

Both IE and Mozilla/Netscape 6.0 support event handlers on HTML elements as defined within the HTML 4.0 specification, at the least. This means that you can associate an onMouseOver event handler with all HTML elements that support this — which means most if not all of them.

Additionally, IE and Mozilla/Netscape 6.0 also support event capturing though the techniques do differ. In IE, you can use SetCapture or ReleaseCapture with HTML elements, and all mouse movements are then passed to the element. You can also assign event handlers to objects in script:

document.onKeyPress=keypress;

With Mozilla/Netscape 6.0, event handling is provided through the DOM Level 2 Events. For instance, to provide event handling for an event, you can add an Event Listener for the object:

document.addEventListener("keyup",keypress,true);

The addEventListener method takes the event as the first parameter, the event handler (function) as the second, and whether the event is captured or allowed to “bubble up” to other events with the third parameter. In the code just shown, the keyup event is captured and assigned to a function called “keypress”.

Within an event handler, you can access an Event object, though how this is accessed and used also differs between the browsers.

With Navigator 4.x, the Event object is passed, automatically, as a parameter to the event handler. Within IE, the Event object is accessed through the Window object during event handling. Mozilla/Netscape 6.0 supports DOM Level 2 event handling. An Event object is passed to the event handler, and information can be pulled from it.

An example of event handling and pulling event information from an Event object is the following, which pulls information about which key is pressed:

function keypress(e) {

   if (navigator.appName == "Microsoft Internet Explorer")
      tmp = window.event.keyCode;
   else if (navigator.appName == "Navigator")
	tmp = e.which;
   else if (navigator.appName == "Mozilla")
       tmp = e.keyCode;

   // if space bar hit
   if (tmp == 32) {
	window.close();
	return;
	}
}

In this code, if the space bar is hit (an ASCII value of 32), the current window is closed. The event handler itself is activated by the following code:

   // capture events
   if (navigator.appName == "Microsoft Internet Explorer")
	wdth = document.body.clientWidth;
   else if (navigator.appName == "Netscape")
   	document.captureEvents(Event.KEYPRESS);
   else
      document.addEventListener("keyup",keypress,true);


   // assign function to event handler
  if (navigator.appName != "Mozilla")
    document.onkeypress=keypress;

Notice that the Mozilla/Netscape 6.0 code captures the keyup event, not the keypress. The reason for this is that the actual key value is set with a keyup or keydown event, but not with keypress. Capturing keypress will give me the ASCII value for the control keys, such as Enter or Tab, but not the other keys such as “N” or the space bar.

Now that we’ve had a brief introduction to X-Object event handling, we’ll take a look at some examples using different types of event handling.

Embedded Event Handlers

The simplest approach to event handling is to embed event handlers right in the HTML element. However, you also have to take into account that Navigator 4.x doesn’t support embedded events for the most part — if you want to capture events such as mouse events, you need to encapsulate the target element within a link.

To demonstrate, I modified a DHTML example application that uses a transparent GIF to embed “hot spots” in a page. When the mouse moves over the hot spot, the transparent image is replaced by a visible image.

Within the page, the transparent images are created within links, which are themselves encapsulated within positioned DIV blocks:

<DIV id=hotspot1>
<a href="" onclick="return false" onmouseover="hotspot(0)">
<img src="blank.gif" width=80 height=32 border=0></a>
</DIV>

When the mouse moves over the image, the hotspot function is called with the number of the spot. This function then replaces the existing transparent GIF with the visible one:

dfltimg = new Image();
dfltimg.src = "logo.gif";

// access images and changing their source
// is relatively old
function hotspot(num) {

   if (navigator.appName == "Microsoft Internet Explorer" ||
       navigator.appName == "Mozilla")
	document.images[num].src = dfltimg.src;
   else
	document.layers[num].document.images[0].src = dfltimg.src;
}

As you might notice from the code, both IE and Mozilla/Netscape 6.0 can access the images array directly, regardless of what container HTML elements the images are in. With Navigator 4.x, however, you have to access the image object directly from the images collection for each document layer in the page.

Try out the HotSpot example yourself, using Mozilla/Netscape 6.0, IE 4.x and up, and Navigator 4.x. Expose the hotspots by moving your mouse over the page (there are 6 of them).

Finding an ASCII value

I have a utility DHTML application that prints the ASCII value of any key pressed to a form text box. I modified this utility to work with Mozilla as well as with Navigator 4.x and IE 4.x and up.

The application captures the keypress event (the keyup event for Mozilla), and attaches this event to an event handler function. The key capture and handling code is:

// handle keyboard events
if (navigator.appName == "Mozilla")
   document.addEventListener("keyup",keypress,true);
else if (navigator.appName == "Netscape")
   document.captureEvents(Event.KEYPRESS);

if (navigator.appName != "Mozilla")
    document.onkeypress=keypress;

// perform action based on keypress and state info
function keypress(e) {

   if (navigator.appName == "Microsoft Internet Explorer")
      tmp = window.event.keyCode;
   else if (navigator.appName == "Navigator")
	tmp = e.which;
   else if (navigator.appName == "Mozilla")
       tmp = e.keyCode;
   document.forms[0].elements[0].value = tmp;
}

You can try out the ASCII Utility yourself using Mozilla/Netscape 6.0, IE 4.x and up, and Navigator 4.x.

One thing you’ll find if you try this example with Mozilla/Netscape 6.0 is that the ASCII key value reflects the value of the key if it were capitalized. So, you get a value of “65” when you press an “A”, regardless of whether you’re pressing the shift key or not. I’m not sure if this is by design or by accident and am investigating this behavior. (Ahh, I do love working with alpha code).

Keep Away

I modified one more DHTML example that uses a combination of embedded event handlers and event capturing to create a “keep away” effect. The example uses an embedded event handler within a DIV block, which works with Mozilla/Netscape 6.0 and IE 4.x and up, but doesn’t with Navigator 4.x:

<DIV id="block" style="left: 200; top: 200; width: 104"
onMouseover="keep_away()">
<img src="yasd.gif" width=104>
</DIV>

To provide event handling for Navigator 4.x, the onMouseOver event for the DIV block is captured in script, and assigned to the same event handler:

:

function capture_events() {
// handle keyboard events
if (navigator.appName == "Netscape") {
	document.block.captureEvents(Event.MOUSEOVER);
      document.block.onmouseover=keep_away;
	}
}

Within the event handling code itself, the CSS positioned DIV block (containing an image of L’il Flame) is moved either right or left, in effect keeping the image away from the user’s mouse:

MAXHORIZ = 700;
MINHORIZ = 100;
var adjustor = 0;

if (navigator.appName == "Navigator") {
   adjustor = 50;
   }
else
   adjustor = 100;

function keep_away() {
	var newleft = theobjs["block"].objGetLeft();
      if (newleft > MAXHORIZ)
		adjustor=-1 * adjustor;
      else if (newleft < MINHORIZ)
		adjustor= -1 * adjustor;
      newleft= newleft+adjustor;
      theobjs["block"].objSetLeft(newleft);
}

As spacing is a bit different between IE and Navigator, the original code provided a different adjustor value for moving the DIV block. I’ve found that the same settings also work with Mozilla. Try the Keep Away example yourself, using Mozilla/Netscape 6.0, IE 4.x and up, and Navigator 4.x. Move your mouse over L’il Flame to cause it to move away from your mouse.

Categories
Technology

X-Objects: HTML Replacement

Copy found at Wayback Machine Archive.

HTML Replacement is functionality to replace the contents of an HTML tag. The contents could be the element’s data or could consist of one or more embedded HTML elements.

Internet Explorer has four proprietary methods to replace the contents of an HTML tag: innerHTML to replace the contents with new HTML and innerText to replace the HTML tag’s data, and outer versions of each (outerHTML and outerText). Netscape Navigator 4.x also has its own form of HTML replacement — CSS positioned DIV blocks actually come with their own document object, you can use document.write with the element to replace its contents.

The IE 4.x and up and Navigator 4.x cross-browser objects have a method, replace_html, that uses each of the browsers specific HTML replacement technique to implement the exposed functionality:

// replace html (IE)
function ieReplaceHTML(html_string) {
	this.css2.innerHTML = html_string;
}

// replace html (navigator)
function nsreplace_html(html_string) {
	this.css2.document.write(html_string);
	this.css2.document.close();
}

These methods have been left, as is, with the new X-Objects.

DOM compatible browsers have several techniques that can be used to clear or replace the contents of an element. For instance, the method removeNode was exposed in DOM Level 1 to remove a specific element, and DOM Level 2 has a new Range object, which can be used to select (and delete) a range of HTML elements. However, I haven’t yet found a W3C standard technique that can be used as innerHTML is used with IE: replacing existing contents with new contents, passed as a string to the method and that can consist of more than one HTML element. The DOM (Level 1 and 2) provides techniques to create specific HTML elements, add CSS classes to the elements, and then append or insert these elements into the existing document, but nothing on creating document elements based on element definitions within a string.

Starting with Mozilla build M16 (released June, 2000) and for Navigator 6.0 PR 2.0, innerHTML has been implemented for both of these browsers, with functionality identical to that of IE 4.x and up as far as I can see. (The innerText, outerHTML, and outerText may also be implemented, but the X-Objects only use innerHTML).

Based on this implementation, the domReplaceHTML method now reads:

// replace HTML (equivalent to innerHTML)
function domReplaceHTML(html_string) {

	this.css2.innerHTML = html_string;

}

Both IE and Mozilla M16 and up (and Navigator 6.0 PR 2.0 and up) use this new object method.

DOM compatible replacement

I did create a new method in X-Objects to provide a second way to handle HTML replacement, the objReplaceHTML method. This method has four parameters: an HTML tag name, a CSS class name, an element identifier, and element contents. Within the method, the contents of the existing element are deleted and a new HTML element is created with the provided element tag type, id, class, and contents. This new element is then appended to the current element.

The implementation for the new method for each browser type — IE, Mozilla/Navigator 6.0, and Navigator 4.x — reflects the functionality supported by each:

// replace HTML -- DOM
function domParamReplaceHTML(tag,clss,id,contents) {

    this.objHide();
    var r = this.css2.ownerDocument.createRange();
    r.selectNodeContents(this.css2);
    r.deleteContents();

    var elem = document.createElement(tag);
    elem.setAttribute("id",id);
    elem.setAttribute("className",clss);
    var txt = document.createTextNode(contents);
    elem.appendChild(txt);
    this.css2.appendChild(elem);
    this.objShow();
  }


// replace html -- IE
function ieParamReplaceHTML(tag, clss, id, contents) {
    var strng = "<" + tag + " class='" +
             clss + "' id='" + id + "'>";
    strng = strng + contents;
    strng = strng + "</" + tag +  ">";
    this.css2.innerHTML = strng;
     }


// replace html -- Navigator 4.x
function nsParamReplaceHTML(tag, clss, id, contents) {
    var strng = "<" + tag + " class='" +
                         clss + "' id='" + id + "'>";
    strng = strng + contents;
    strng = strng + "</" + tag + ">";
    this.css2.document.write(strng);
    this.css2.document.close();
    }

Notice in the code for the DOM implementation of objReplaceHTML that I hide the element contents before making the change, and then show the contents at the end of the HTML replacement. The reason for this is that I have found with Mozilla build M14, which I used for testing the new X-Objects, would not re-paint the DIV element after I added new content. The element would be added — I could see it when I minimized the browser and then maximized it, forcing a repaint — but it wouldn’t show without a forced repaint. By hiding and showing the content I’m programatically forcing a repaint of the contents.

Additionally, I also created another new interface method, this one to replace the data contents of an element. This new method, objReplaceText, is implemented only for IE and for Mozilla/Navigator 6.0 — the method provided for the Navigator 4.x implementation is a placeholder only. The code for the three implementations of objReplaceText is:

// replace text -- DOM and IE
function domReplaceText(txt_string) {

   var nodes = this.css2.childNodes;
   var node = nodes.item(0);
   node.replaceData(0,node.length,txt_string);
}

// Navigator 4.x implementation
function nsReplaceText(text_string) {
  // this function not implemented
}

This method grabs the first child node for the element, and replaces its data with the new data string.

Testing HTML Replacement

To test the new HTML replacement method, I modified an existing DHTML example to use the new X-Objects, including using objReplaceHTML. The example has two HTML forms with only one form showing at a time. Each form is contained within a CSS positioned DIV block. Pressing a button switches which form shows at a time. You can try out the Forms Example before viewing the code.

Pushing another button starts an animated effect that shows which HTML input element is mandatory for the page. An arrow moves through the form showing the mandatory items, and the name of the block is shown above the form. The form element name is the content that changes with the HTML replacement.

To make things interesting, in this example I’m using my higher-level animation objects, the Animators, in order to provide the form animation. The Animator objects do such things as control a movement from point A to point B in timed increments, creating an animated movement effect. By packaging whole animation effects (such as movement, clipping, and sliding) into objects, I can simplify the amount of code I need to create for my individual pages.

One of the Animators replaces HTML using the old replace_html method. As I’m using a new interface method for HTML replacement, objReplaceHTML, I create a new Animator that uses this method rather than the existing one — The parameterized HTML replacement Animator.

View the Animator Objects source code directly. You can also read a more detailed discussion of the Animator objects in the InterActZone article Demonstrating PhotoShop functionality using DHTML and X-Objects. This article looks at four detailed examples of using the new X-Objects to create demonstrations of PhotoShop capabilities.

Once I modified the Animator objects, I’m ready to modify my form switching example to using X-Objects.

The BODY of the example contains four separate content areas. First, the top of the document has a form with buttons used to control form page switching, showing mandatory values, and submitting the form. Each of the two forms is in its own content area, and the fourth and final content is a message stating that the form has been successfully submitted:

<BODY onload="create_objects();setup();setup_animation()">
<H2>Press the button to display the second part of the form</H2>
<form>
<input type=button value="Switch Form Pages"
                    onclick="switch_forms()">
<input type=button value="Submit Information"
	onclick="submit_form()">
<input type=button value="Show Mandatory Fields"
        onclick="play_animation()">
</form>
<DIV id="error" style="position:absolute; z-index: 3;
        top: 120; left: 150; visibility:hidden">
<p>
Missing Information
</p>
</DIV>

<DIV id="mandatory"
  style="position:absolute; z-index: 3; top: 100; left: 150;
  visibility:hidden">
<p class="mandatory" >
Mandatory Fields are:
</p>
</DIV>

<DIV id="arrow" style="position:absolute; top: -50; left: 600;
     visibility: hidden">
<img src="arrow.jpg" width=51>
</DIV>

<DIV id="one" style="position:absolute; left: 100; top: 150;
         clip: rect(0,500,400,0);
	width: 500; height: 400; background-color: lime;
        layer-background-color: lime">
<h2>Please enter information about yourself:</h2>
<form name="firstform">
<table cols=2 width=200>
<tr><td width=150>Enter Name:</td><td>
<input type="text" name="readername"
        onchange="values[0] = 1"></td></tr>
<tr><td >Street Address: </td><td>
<input type="text" name="address"
        onchange="values[1] = 1"></td></tr>
<tr><td >City:</td><td>
<input type="text" onchange="values[2] = 1"></td></tr>
<tr><td>State:</td><td>
<input type="text" onchange="values[3] = 1"></td></tr>
<tr><td >Phone: </td><td>
<input type="text" name="phone"></td></tr></table>
</form>
</DIV>

<DIV id="two" style="position:absolute; left: 100; top: 150;
          clip: rect(0,500,400,0); visibility: hidden;
	width: 500; height: 400; background-color: yellow;
        layer-background-color: yellow">
<h2>Please enter information about your Company:</h2>
<form name="secondform">
<table cols=2 width=200>
<tr><td width=150>Enter Company Name:</td><td>
<input type="text" name="readername"
    onchange="values[4] = 1"></td></tr>
<tr><td >Company Address: </td><td>
<input type="text" name="address"></td></tr>
<tr><td >City:</td><td>
<input type="text"></td></tr>
<tr><td>State:</td><td>
<input type="text"></td></tr>
<tr><td >Voice Phone: </td><td>
<input type="text" name="phone"
      onchange="values[5] = 1"></td></tr>
<tr><td >FAX: </td><td>
<input type="text" name="phone"
      onchange="values[6] = 1"></td></tr></table>
</form>
</DIV>

<DIV id="message" style="position:absolute; left: 100;
            top: 150; clip: rect(0,500,200,0);
	width: 400; height: 200; background-color: aqua;
        layer-background-color: aqua; visibility: hidden;">
<H2>Your form information has been submitted successfully</H2>
</DIV>
</BODY>

The X-Objects create_objects method is called to create the object wrappers for the page’s DIV blocks. Additionally, the animation sequences are setup through a call to the setup_animation function:

function setup_animation() {

	seq[0] = new animatorSequence();
	var anim = seq[0].newAnimator(1,50);
	anim.addAnimator(theobjs["one"],"S");
	anim.addAnimator(theobjs["arrow"],"S");
	anim.addAnimator(theobjs["mandatory"],"S");

      seq[1] = new animatorSequence();
	anim = seq[1].newAnimator(5, 20);
	anim.addAnimator(theobjs["arrow"], "M",210, 600);

	anim = seq[1].newAnimator(1, 20);
	anim.addAnimator(theobjs["mandatory"],"R",
        "<p style='color: red; font-weight: bold'>
            Personal Name</p>");

      seq[2] = new animatorSequence();
	anim = seq[2].newAnimator(5, 20);
	anim.addAnimator(theobjs["arrow"], "M",240, 600);

	anim = seq[2].newAnimator(1, 20);
	anim.addAnimator(theobjs["mandatory"],"R",
        "<p style='color: red; font-weight: bold'>
            Address</p>");

      seq[3] = new animatorSequence();
	anim = seq[3].newAnimator(5, 20);
	anim.addAnimator(theobjs["arrow"], "M",270, 600);

	anim = seq[3].newAnimator(1, 20);
	anim.addAnimator(theobjs["mandatory"],"R",
       "<p style='color: red; font-weight: bold'>
           City</p>");

      seq[4] = new animatorSequence();
	anim = seq[4].newAnimator(5, 20);
	anim.addAnimator(theobjs["arrow"], "M",300, 600);

	anim = seq[4].newAnimator(1, 20);
	anim.addAnimator(theobjs["mandatory"],"R",
       "<p style='color: red; font-weight: bold'>
          State</p>");

	seq[5] = new animatorSequence();
	var anim = seq[5].newAnimator(1,50);
	anim.addAnimator(theobjs["one"],"H");
	anim.addAnimator(theobjs["two"],"S");

      seq[6] = new animatorSequence();
	anim = seq[6].newAnimator(5, 20);
	anim.addAnimator(theobjs["arrow"], "M",225, 600);

	anim = seq[6].newAnimator(1, 20);
	anim.addAnimator(theobjs["mandatory"],"R",
        "<p style='color: red; font-weight: bold'>
         Company Name</p>");

      seq[7] = new animatorSequence();
	anim = seq[7].newAnimator(5, 20);
	anim.addAnimator(theobjs["arrow"], "M",350, 600);

	anim = seq[7].newAnimator(1, 20);
	anim.addAnimator(theobjs["mandatory"],"R",
       "<p style='color: red; font-weight: bold'>
         Company Phone</p>");

      seq[8] = new animatorSequence();
	anim = seq[8].newAnimator(5, 20);
	anim.addAnimator(theobjs["arrow"], "M",380, 600);

	anim = seq[8].newAnimator(1, 20);
	anim.addAnimator(theobjs["mandatory"],"R",
        "<p style='color: red; font-weight: bold'>
          Company FAX</p>");
}

Several Animation sequences are created to control the mandatory field display, consisting of several different animation steps. The steps control which form shows, the position of the arrow pointing out the mandatory field, and the replacement of the HTML containing the mandatory form field name. When the form button labeled “Show Mandatory Fields” is pressed, the function play_animation is called to play the animation:

var slide = 0;

function play_animation() {
      if (slide == 0)
         reset();
      seq[slide].play();
	if (slide < 8) {
		setTimeout("play_animation()",2000);
		slide++;
		}
	else {
		setTimeout("reset()", 2000);
		}
}

Two other functions are created: one to reset the display after showing the Mandatory fields; one to switch between the forms:

function reset() {
      theobjs["message"].objHide();
      theobjs["two"].objHide();
      theobjs["one"].objShow();
      theobjs["mandatory"].objHide();
	theobjs["mandatory"].
          replace_html("<p style='color:red; font-width: bold'>
                           Mandatory Fields Are:</p>");
	theobjs["arrow"].objHide();
      theobjs["arrow"].objMoveAbsolute(-50,600);
	slide = 0;
}

function switch_forms() {
	// just in case, hide the message layer
	theobjs["message"].objHide();
	if (side == 1) {
		theobjs["one"].objHide();
		theobjs["two"].objShow();
		side = 2;
		}
	else {
		theobjs["two"].objHide();
		theobjs["one"].objShow();
		side = 1;
		}
}

The last function for the page is submit_form. This function checks to see if the mandatory fields have values, and if not to provide an error message and field indicator. If all the mandatory fields have data, then a message is provided to that effect:

// form values
var values = new Array(7);
for (var i = 0; i < values.length; i++)
	values[i] = 0;

function submit_form() {
	// hide error and arrow
	theobjs["arrow"].objHide();
	theobjs["error"].objHide();
	theobjs["one"].objHide();
	theobjs["two"].objShow();
	// check out page one for mandatory fields
	if (values[0] != 1) {
		missing("one", 1);
		return;
		}
	if (values[1] != 1) {
		missing("one", 2);
		return;
		}
	if (values[2] != 1) {
		missing("one", 3);
		return;
		}
	if (values[3] != 1) {
		missing("one", 4);
		return;
		}
	if (values[4] != 1) {
		missing("two", 1);
		return;
		}
	if (values[5] != 1) {
		missing("two", 5);
		return;
		}
	if (values[6] != 1) {
		missing("two", 6);
		return;
		}

	theobjs["message"].objShow();
}

The values array elements used in the function is set when the mandatory fields are changed.

Try the example for yourself, using Internet Explorer 4.x and up, Mozilla/Navigator 6.0, or Navigator 4.x. Specifically, try out the HTML replacement by clicking on the “Show Mandatory Fields” button.

Before I leave this page, I wanted to point out something in the example just shown. Using Mozilla/Navigator 6.0, try clicking in any of the text elements in the forms that are switched — you’ll find that you can’t put the mouse cursor in these elements. However, if you try the example with IE or Navigator 4.x, you don’t have any problem with the forms.

The example is performing incorrectly not because Mozilla/Navigator 6.0 did something wrong. On the contrary, Mozilla/ Navigator 6.0 did something right, and that’s the challenge as we’ll see in the next section.

Visibility vs. Display

In the example, the forms and the success message are layered on each other. To control which form is shown at a time, the CSS visibility property is set to either “visible”, for showing the form or message, or “hidden” to hide the form or message.

This works without a hitch in IE and Navigator 4.x, primarily because these two browsers have implemented visibility as it was defined with the original CSS-P specification, submitted to the W3C a couple of years ago.

However, the visibility property with CSS2 can hide page content, but it doesn’t remove the content from the page layout — and this includes the layout even if the content is layered. So, when I “hid” any of the forms, all I’m doing is effecting their appearance not their impact on the page.

Another thing about the example is that Web page contents that are not given specific Z-order values, or are given the same Z-order value, are given implicit z-orders by the browser, and those that occur later in the page are given an implicitly higher z-order than those that occur earlier. As the example’s message block occurs last in the page, it is given a higher z-order than the two forms, even if the message block is hidden.

So, with Mozilla/Navigator 6.0 when I hide one of the forms and show the other, I still can’t access the form contents of the shown layer as this higher z-order content is “blocking” my access, though not blocking my view.

To actually hide a Web page element AND remove it from the page layout, I need to use the display CSS property instead.

I could have also changed the z-order of whichever DIV block was showing at the time — making it a higher value than the others. Then, when the block was hidden, I would reset the z-order back to a lower value.

To correct the forms example I need to add a new method to the X-Objects, objDisplay. This property sets the value of the Display to whatever is passed into the method. For my DHTML X-Browser effects, I’m using only two values for Display at this time: “block”, to show the content, and “none” to remove the content from display.

The implementation of objDisplay is the same for both IE and Mozilla/Navigator 6.0:

// display element
function domDisplay(type) {
   this.css2.style.display = type;
}

The implementation of objDisplay for Navigator 4.x checks the value of type passed to the method, and either shows or hides the element using the visibility methods:

// element display
function nsobjDisplay(type) {
   if (type == "none")
       this.objHide();
   else
       this.objShow();
}

As the CSS Display property is not implemented with Navigator 4.x, we have to use visibility with this browser. However, for layered content, the behavior is the same.

Since the underlying X-Objects implementation has changed, I also need to change my Animator objects to handle setting the display value as well as setting visibility, so another Animator type is added, the display_animator (again see the implementation in the text version of the Animator objects, above).

The example is modified to remove the visibility setting from the two forms and the message that are layered on each other. Additionally, a setup function is created to “hide” the second form and the message when the page is loaded.

function setup() {
   theobjs["two"].objDisplay("none");
   theobjs["message"].objDisplay("none");
}

By setting the display programmatically rather than using CSS style settings, the correct implementation of objDisplay is used for the specific browser. Other changes are made to the code to use objDisplay rather than objHide or objShow, as shown in the function to switch the forms:

function switch_forms() {
	// just in case, hide the message layer
	theobjs["message"].objDisplay("none");
	if (side == 1) {
	   theobjs["one"].objDisplay("none");
	   theobjs["two"].objDisplay("block");
	   side = 2;
	   }
	else {
	   theobjs["two"].objDisplay("none");
	   theobjs["one"].objDisplay("block");
	   side = 1;
	   }
}

Try out the new version of the forms example, and you’ll find that you can now access the form elements with Mozilla/Navigator 6.0, as with IE 4.x and up and Navigator 4.x.

Categories
Technology

X-Objects: Layering and Z-Order

Copy found at Wayback Machine archive.

Internet Explorer 6.x differs from IE 5.x in one very important area: In IE 6.0 and up, units must be specified with all CSS styles that take units, if you use some combination of CSS positional attributes — what combination, exactly, I haven’t been able to figure out yet. This includes width, height, as well as the positional attributes of top, left, bottom, and right. In IE 5.x and lower, as well as Mozilla and Netscape, positional attributes were given a default unit of pixel.

The index card example in this chapter didn’t specify pixels with the positional attributes, and the results were completely wrong. However, after a quick fix, the example’s just fine now. That’ll teach me to be sloppy. Units should be specified in all uses.

DHTML developers can layer HTML elements one on top of each other, using a combination of visibility to hide and show elements located in the same location, or by using the CSS z-order style attribute, or both. You’ve had a chance to see visibility, but we’ll take a look at z-order layering in this section, as well as looking at layering using both techniques.

The z-order methods exposed on the X-Objects interface are:

  • objGetZIndex – to get the element’s current z-order, if set
  • objSetZIndex – to set the element’s current z-order

Again, Navigator 4.x has unique implementations for setting and getting the z-order:

// set element's zindex order
function nsobjSetZIndex(zindex) {
	this.css2.zIndex = zindex;
}

// get element's current zindex order
function nsobjGetZIndex() {
	return this.css2.zIndex;
}

The DOM compliant versions of objGetZIndex and objSetZIndex again use the Style object to change or access the current settings:

// set element's zindex order
function domSetZIndex(zindex) {
   this.css2.style.zIndex = zindex;
}

// get element's current zindex order
function domGetZIndex(zindex) {
   return this.css2.style.zIndex;
}

Testing z-order and layering

Instead of creating new examples to test layering, I modified existing DHTML examples to use the new X-Objects.

The first example is an emulation of a graphical button that doesn’t require an HTML form, or the use of form elements. Intead, the example uses two DIV blocks, both containing the same image, but each with a different image border. One border is set using the CSS outset setting, one using the inset setting.

The two images with the different CSS borders are layered on one another. Clicking (mouse down) on the image “hides” the top image by changing its z-order to a lower order than the image in the bottom layer. The mouse up event changes the z-order of this image layer to a higher number again, returning the “button” to the unclicked state.

Try this button effect created using X-Objects by accessing the Button page. View the source to see the code for the example.

Stacked Index Card Example

A second example I modified to use the new X-Objects is a stacked index card application. A group of names are created as separate layers each stacked on the others, with only the topmost layer being visible. Clicking on the Next and Prev command texts shows the next or previous card in the “stack”. When you access the last card in the stack, a surprise occurs: the text-based card navigation icons are replaced by labeled tabs above the cards. Clicking on the tabs displays the address associated with the tab. View the Index Card Example View the source to see the code for the example.