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.

Categories
Technology

X-Objects: Element Width and Height

Copy found at Wayback Machine archive.

In addition to being able to change an HTML element’s position within a page, or hide or show the element, you can also use the X-Objects to change the HTML element’s width and height. The X-Objects interface methods to control width and height are:

  • objSetWidth: set an element’s width
  • objGetWidth: get an element’s width, if set
  • objSetHeight: set an element’s height
  • objGetHeight: get an element’s height, if set
  • objResizeBy: resize an element with a given height and width

As with the movement interface methods, Navigator 4.x has unique method implementations for all but the objResizeBy method. Unlike those for the DOM compatible browsers (IE and Mozilla/Navigator 6.0), the Navigator 4.x object methods set width properties directly on the objects rather than through the use of a Style object:

// get element's width
function nsobjGetWidth() {
	return this.css2.clip.width;
}

// get element's height
function nsobjGetHeight() {
	return this.css2.clip.height;
}

// set element's width
function nsobjSetWidth(width) {
	this.css2.clip.width = width;
}

// set element's height
function nsobjSetHeight(height) {
	this.css2.clip.height = height;
}

IE and Mozilla/Navigator 6.0 apply width and height changes directly to the Style object associated with the object:

// get element's width
function domGetWidth() {
        var wd = parseInt(this.css2.style.width);
	return wd;
}

// get element's height
function domGetHeight() {
        var ht = parseInt(this.css2.style.height);
	return ht;
}

// set element's height
function domSetHeight(height) {
	this.css2.style.height = height + "px";
}

// set element's width
function domSetWidth(width) {
	this.css2.style.width = width + "px";
}

All of the X-Objects use the same implementation for the method objResizeBy:

function domResizeBy(wincr,hincr) {
   var wdth = this.objGetWidth();
   wdth += wincr;
   this.objSetWidth(wdth);

   var ht = this.objGetHeight();
   ht += hincr;
   this.objSetHeight(ht);
}

 

Testing Element Sizing

I created a Web page that uses the X-Object width and height methods. The page has a Web form and a test block created within a CSS Positioned DIV block. In the form, buttons are used by the client to shrink or grow the test block horizontally (through the width methods), or vertically (through the height methods).

The BODY contents of the test page has the following:

<DIV style="position:absolute; left:10;
        top:10; background-color: yellow;
layer-background-color: yellow; padding-top: 20px;
width: 500; height:150; clip: rect(0,500,150,0)">
<form action="">
<center>
<INPUT type="button" value="Shrink Block Horizontally"
    onclick="decr_width()"> 
<INPUT type="button" value="Grow Block Horizontally"
    onclick="incr_width()"><p>
<INPUT type="button" value="Shrink Block Vertically"
    onclick="decr_height()"> 
<INPUT type="button" value="Grow Block Vertically"
    onclick="incr_height()"><p>
</center>
</FORM>
</DIV>

<DIV id="info" style="position:absolute;
left: 250; top: 180; width: 300;
height: 300;  background-color:red;
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>

The JS to perform the DHTML effects based on the choices the user makes from the form elements is:

<SCRIPT language="javascript"
type="text/javascript">
<!--
// increase width
function incr_width() {
   var wdth = theobjs["info"].objGetWidth();
   wdth+= 10;
   theobjs["info"].objSetWidth(wdth);
}

function decr_width() {
   theobjs["info"].objResizeBy(-10,0);

}
// move element up and down
function incr_height() {
   var top = theobjs["info"].objGetHeight();
   top+= 10;
   theobjs["info"].objSetHeight(top);
}

function decr_height() {
   theobjs["info"].objResizeBy(0,-10);
}

//-->
</script>

As you can see from the code, the DHTML effects based in this example are created using a combination of objGetWidth and objSetWidth, objGetHeight and ObjSetHeight, and objResizeBy. Access the Sizing example for yourself.

Categories
Technology

X-Objects: Movement and Visibility

Copy found at Wayback Machine.

All of the X-Objects have several methods for handling visibility and movement:

  • objSetLeft: set the left position of the element
  • objSetTop: set the top position of the element
  • objGetLeft: get the current left position of the element, if set
  • objGetTop: get the top position of the element, if set
  • objMoveRelative: move the object by a given horizontal and vertical change, relative to current position
  • objMoveAbsolute: move the object, absolutely, by given horizontal and vertical values
  • objHide: hide the object
  • objShow: show the object
  • objGetVisibility: show the object’s current visibility

The Navigator 4.x implementations of all of these interface methods is unique to this browser and version except for objMoveRelative and objMoveAbsolute, which are shared with the DOM and IE implementations.

The Navigator 4.x implementations of the movement and visibility methods are:

// hide element
function nsobjHide() {
	this.css2.visibility = "hidden";
}

// show element
function nsobjShow() {
	this.css2.visibility = "inherit";
}

// element's left position
function nsobjGetLeft() {
	return this.css2.left;
}

// element's top position
function nsobjGetTop () {
	return this.css2.top;
}

// set element's top position
function nsobjSetTop(top) {
	this.css2.top = top;
}

// set element's left position
function nsobjSetLeft(left) {
	this.css2.left = left;
}

 

As stated in the introduction, the IE-specific and the DOM objects (which Mozilla/Navigator 6.0 use) share much of the same interface implementations. The reason this can occur is because of a key shared concept between both browsers — all CSS properties are set through one specific object, the style object. Each HTML element has a property “style”, a reference to the Style object associated with that element and which contains all CSS settings for the element. Based on this, the implementations for movement and visibility for DOM-compatible browsers are:

// element's left position
function domGetLeft() {
      var lt = parseInt(this.css2.style.left);
	return lt;
}

// element's top position
function domGetTop () {
      var tp = parseInt(this.css2.style.top);
	return tp;
}

// set element's top position
function domSetTop (top) {
	this.css2.style.top = top + "px";
}

// set element's left position
function domSetLeft(left) {
	this.css2.style.left = left + "px";
}

// hide element
function domHide() {
   this.css2.style.visibility = "hidden";
}

// show element
function domShow() {
   this.css2.style.visibility = "visible";
}

The DOM compliant methods need to specify a unit of measurement as well as the numeric value for the new values. The X-Objects use pixels (px) for all of the DHTML effects.

The Navigator 4.x and the DOM-compatible browsers (IE and Mozilla/Navigator 6.0) share two method implementations, domMoveRelative and domMoveAbsolute, as these methods are higher-level implementations that call existing lower-level object methods to perform the actual movements:

// make absolute move
function domMoveAbsolute(newleft, newtop) {
   this.objSetLeft(newleft);
   this.objSetTop(newtop);
}

// move relative to current location
function domMoveRelative(left, top) {
   this.objSetLeft(left + this.objGetLeft());
   this.objSetTop(top + this.objGetTop());
}

By calling the exposed interface methods within the X-Object implementation, the correct version of the interface implementation is used for the movement. So, from Navigator 4.x, the left and top methods invoked when using objSetLeft and objSetTop are nsobjSetLeft and nsobjSetTop; from DOM-compatible browsers, the methods invoked are domSetLeft and domSetTop.

Now that we’ve provided the interface implementations for movement and visibility, let’s try them out.

Testing the implementations

I created an HTML page that contains a form with several buttons and a test block that is moved or hidden based on the user pushing the form buttons. The Body of the page contains the form and the test block:

<BODY onload="create_objects()">
<DIV style="position:absolute; left:10;
    top:10; background-color: yellow;
layer-background-color: yellow; padding-top: 20px;
width: 500; height:150; clip: rect(0,500,150,0)">
<form action="">
<center>
<INPUT type="button" value="Move Block Left"
        onclick="move_left()"> 
<INPUT type="button" value="Move Block right"
        onclick="move_right()"><p>
<INPUT type="button" value="Move Block Up"
        onclick="move_up()"> 
<INPUT type="button" value="Move Block Down"
        onclick="move_down()"><p>
<INPUT type="button" value="Hide Block"
        onclick="hide()"> 
<INPUT type="button" value="Show Block"
        onclick="show()"> 
<INPUT type="button" value="Show Visibility"
        onclick="get_visibility()">
</center>
</FORM>
</DIV>

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

Notice that the test block is positioned using absolute CSS positioning. This is necessary to access the DIV block with Navigator 4.x. Also, notice that the block has a reference to a CSS attribute, layer-background-color, that is not a valid CSS attribute. This is again necessary for Navigator 4.x in order for the background color to fit the block width and height, rather than just being background for the block contents.

Notice in the example that I’m not using units with the positional elements (i.e. using top: 180 instead of top: 180px) in the stylesheet settings. This was a bad habit I got into and one you shouldn’t get into.

You’ll find that this can cause problems with IE 6.0. You’ll also find that if you use standard mode with Mozilla (using a particular doctype of HTML strict or HTML transitional), that the examples won’t work at all.

Read more on Mozilla’s standard and “quirky” modes at Mozilla – reporting a bug.

The HTML page’s HEAD section contains the reference to the X-Objects script file and the DHTML code to implement the functions of the form buttons. These functions move the test block to the right or left, up or down, or show and hide the block. One last form button retrieves the current visibility setting for the block. The implementations of these functions are:

<SCRIPT src="cbobjects.js" language="javascript"
    type="text/javascript">
</SCRIPT>

<SCRIPT LANGUAGE = "JAVASCRIPT"
                   type="text/javascript">
<!--
// move element left
function move_left() {
   var lft = -10;
   theobjs["info"].objMoveRelative(lft,0);
}

// move element right
function move_right() {
   var lft = theobjs["info"].objGetLeft();
   lft += 10;
   theobjs["info"].objSetLeft(lft);

}

// move element up
function move_up() {
   var top = theobjs["info"].objGetTop();
   top+= -10;
   theobjs["info"].objSetTop(top);
}

// move element down
function move_down() {
   var top = theobjs["info"].objGetTop();
   top += 10;
   var lft = theobjs["info"].objGetLeft();
   theobjs["info"].objMoveAbsolute(lft,top);
}

// hide element
function hide() {
   theobjs["info"].objHide();
}

// show element
function show() {
   theobjs["info"].objShow();
}

function get_visibility() {
   alert(theobjs["info"].objGetVisibility());
}
//-->
</script>

You can access the Movement and Visibility testing page, using Internet Explorer 4.x and up, Navigator 4.x and up, and Mozilla.

I converted one of my existing DHTML pages to use the X-Objects. This page contains an Animated Menu using both the movement and visibility methods, and a timer to create the animation effect. Once you open the page, the menu animation starts. To see the code used, View source once the example is opened.

Another existing DHTML example is relatively simple, using hidden DIV blocks to create a “shadow” effect for Web page text. The example moved as is to the new X-Objects (as did the animated menu, above) without any modifications. Check out the Shadow Effect example, using View source once the example is opened to see the script.

Categories
Technology

X-Objects: Creating the new X-Objects

Copy found on Wayback Machine.

The original cross-browser objects encapsulated browser-specific DHTML implementation behind a set of exposed interfaces, making it easier to change the implementation if needed, and easier to create and maintain my DHTML pages. When a new browser version (or browser) releases, I can make changes to handle the new implementation details in one place, the cross-browser object file, rather than within each page that uses DHTML.

So, instead of doing something similar to the following:

   if (navigator.appName == "Microsoft Internet Explorer") {
	document.all.div2.style.posLeft=incr;
	document.all.div1.style.posLeft=incr;
	}
   else {
	document.div2.left=incr;
	document.div1.left=incr;
	}

I can instead use:

   theobjs["div2"].objSetLeft(incr);
   theobjs["div1"].objSetLeft(incr);

If a browser changes its implementation, then all I need to do is modify the interface method, such as objSetLeft from the example, rather than have to modify the DHTML in each page where I set the element’s leftmost position using DHTML.

The cross-browser objects became cross-version when they provided support, with little modification, for the release of IE 5.x — a mark of a successful use of a technology. However, the true test for these beasties is the release of Mozilla/Netscape 6.0. We’ll find in this section, and the ones to come, that the cross-browser objects did require some modifications, though not to the existing interface methods. Instead, new methods have been added to handle new functionality, as well as changed functionality. Because of these changes, and the fact that the objects are now cross-browsercross-version, and cross-DOM, I’ve renamed the objects to X-Objects.

We’ll take a look at how the X-Objects are created, and then in the following sections we’ll look at the browser-specific implementations of the X-Object interface, and examples using the new X-Objects.

X-Objects: object constructors

My old cross-browser objects are an array of DIV objects. I focused on DIV objects as both Netscape 4.x and IE 4.x supported pulling all CSS-positioned DIV blocks from a page. Additionally, I could create most DHTML effects just by manipulating DIV blocks. To maintain backwards-compatibility with Netscape 4.x, my new X-Objects are still pulling in absolutely positioned DIV blocks as DHTML objects. You’ll see later on in the examples that this still gives us considerable room to create our DHTML effects.

Every page that uses the X-objects to create DHTML includes an external Javascript file, pulled into the page using the following tag:

<SCRIPT src="cbobjects.js" type="text/javascript"
language="javascript">
</SCRIPT>

Additionally, the x-objects are created by calling a constructor method from this file: create_objects. The create_objects function tests to see what browser is accessing the page, and calls a browser-specific version of a DHTML object constructor function based on the browser. The X-Objects create_objects tests for the navigator name of “Microsoft Internet Explorer” and calls create_ie_objects if found. The code could be modified to test for version and call the function create_ie_objects function for IE 4.x, and create_dom_objects for 5.x and up. If the browser is Mozilla or Netscape, create_dom_objects is called if the version is newer; else the function create_ns_objects is called for Netscape Navigator 4.x:

function create_objects() {

    // Internet Explorer 4.x, 5.x, 6.x
    if (navigator.appName == "Microsoft Internet Explorer")
	   create_ie_objects();
    else // Navigator or Mozilla
        if (navigator.appName == "Mozilla" ||
                navigator.appName == "Netscape")
           if (navigator.appVersion.indexOf("4.") == -1)
	      create_dom_objects();
           else
  	      create_ns_objects();
}

The create_ns_objects method pulls the CSS positioned DIV blocks from the Layers collection and loads into an array, theobjs. This array is then accessed by the page creating the DHTML effect:

// For Navigator 4.x, pull all DIV blocks into object array
function create_ns_objects(newarray) {
   theobjs = new Array();
   for (i = 0; i < document.layers.length; i++){
     if (document.layers[i].name != "")
   	 theobjs[document.layers[i].name] =
            new ns_object(document.layers[i]);
     }
}

The create_dom_objects uses the getElementsByTagName method exposed with DOM Level 1, and pulls in all DIV blocks:

// For W3C DOM (Netscape 6.x, Mozilla), pull
// all named DIV blocks into an array
function create_dom_objects() {
  theelements = document.getElementsByTagName("DIV");
  theobjs = new Array();
  for (i = 0; i < theelements.length; i++) {
      var obj = theelements[i];
      if (obj.id != "")
         theobjs[obj.id] = new dom_object(obj);
      }
}

As you can see with both the Navigator and the DOM objects, only named DIV blocks are pulled into the DHTML object array.

The IE objects could also use the getElementsByTagName method, but I want the objects to be compatible with IE 4.x, which doesn’t support the DOM Level 1 release, so I’ll use Microsoft’s proprietary document.all collection to pull in the DIV elements:

// For IE, pull all DIV blocks into object array
function create_ie_objects() {
   theelements = document.all.tags("DIV");
   theobjs = new Array();
   for (i = 0; i < theelements.length; i++){
      if (theelements[i].id != "") {
	   theobjs[theelements[i].id] =
             new ie_object(theelements[i]);
	   }
      }
}

The DHTML array contains references to the X-objects created specifically for the browser. Each of these objects consists of the exact same interfaces, but pointing to different implementations of the functionality.

The Navigator object and it’s associated interface is:

// The Navigator DOM Object
//
//************************************************
function ns_object(obj) {
   this.css2 = obj;
   this.name = obj.name;
   this.objResizeBy = domResizeBy;
   this.objHide = nsobjHide;
   this.objShow = nsobjShow;
   this.objDisplay = nsobjDisplay;
   this.objGetLeft = nsobjGetLeft;
   this.objGetTop = nsobjGetTop;
   this.objSetTop = nsobjSetTop;
   this.objSetLeft = nsobjSetLeft;
   this.objMoveAbsolute = domMoveAbsolute;
   this.objMoveRelative = domMoveRelative;
   this.objGetWidth = nsobjGetWidth;
   this.objGetHeight = nsobjGetHeight;
   this.objSetHeight = nsobjSetHeight;
   this.objSetWidth = nsobjSetWidth;
   this.objSetZIndex = nsobjSetZIndex;
   this.objGetZIndex = nsobjGetZIndex;
   this.objSetClipRect = nsobjSetClipRect;
   this.objGetClipRect = nsobjGetClipRect;
   this.objGetClipLeft = nsobjGetClipLeft;
   this.objGetClipRight = nsobjGetClipRight;
   this.objGetClipTop = nsobjGetClipTop;
   this.objGetClipBottom = nsobjGetClipBottom;
   this.replace_html = nsreplace_html;
   this.objReplaceHTML = nsParamReplaceHTML;
   this.objReplaceText = nsReplaceText;
   this.objGetVisibility = nsVisibility;
}

If you’ve used my previous version of the cross-browser objects, you’ll see that the X-Object interface is the same as the cross-browser interface, with the addition of a few new interface methods. This highlights one very important point: the key to a successful use of object-based technology is never to remove or take away from an interface — new changes should result in growth not destruction.

The parameter to the object constructor is the actual browser-implemented object, which is then assigned to the X-Object’s css2 property. This property wrapping is necessary in order to be able to use the properties and methods supplied by the browser-specific object.

The dom_object and the ie_object point to the same method implementations for most of the exposed interface functionality:

// The W3C DOM Object
//
//****************************************************
function dom_object(obj) {
   this.css2 = obj;
   this.name = obj.id;
   this.objResizeBy = domResizeBy;
   this.objHide = domHide;
   this.objShow = domShow;
   this.objDisplay = domDisplay;
   this.objGetLeft = domGetLeft;
   this.objGetTop = domGetTop;
   this.objSetTop = domSetTop;
   this.objSetLeft = domSetLeft;
   this.objMoveAbsolute = domMoveAbsolute;
   this.objMoveRelative = domMoveRelative;
   this.objGetWidth = domGetWidth;
   this.objGetHeight = domGetHeight;
   this.objSetHeight = domSetHeight;
   this.objSetWidth = domSetWidth;
   this.objSetZIndex = domSetZIndex;
   this.objGetZIndex = domGetZIndex;
   this.objSetClipRect = domSetClipRect;
   this.objGetClipRect = domGetClipRect;
   this.objGetClipLeft = domGetClipLeft;
   this.objGetClipRight = domGetClipRight;
   this.objGetClipTop = domGetClipTop;
   this.objGetClipBottom = domGetClipBottom;
   this.replace_html = domReplaceHTML;
   this.objReplaceHTML = domParamReplaceHTML;
   this.objReplaceText = domReplaceText;
   this.objGetVisibility = domGetVisibility;
}


// The IE 4.x and 5.x DOM Object
//
//***************************************************
function ie_object(obj) {
   this.css2 = obj;
   this.name = obj.id;
   this.objResizeBy = domResizeBy;
   this.objHide = domHide;
   this.objShow = domShow;
   this.objDisplay = domDisplay;
   this.objGetLeft = domGetLeft;
   this.objGetTop = domGetTop;
   this.objSetTop = domSetTop;
   this.objSetLeft = domSetLeft;
   this.objMoveAbsolute = domMoveAbsolute;
   this.objMoveRelative = domMoveRelative;
   this.objGetWidth = domGetWidth;
   this.objGetHeight = domGetHeight;
   this.objSetHeight = domSetHeight;
   this.objSetWidth = domSetWidth;
   this.objSetZIndex = domSetZIndex;
   this.objGetZIndex = domGetZIndex;
   this.objSetClipRect = domSetClipRect;
   this.objGetClipRect = domGetClipRect;
   this.objGetClipLeft = domGetClipLeft;
   this.objGetClipRight = domGetClipRight;
   this.objGetClipTop = domGetClipTop;
   this.objGetClipBottom = domGetClipBottom;
   this.replace_html = domReplaceHTML;
   this.objReplaceHTML = ieParamReplaceHTML;
   this.objReplaceText = domReplaceText;
   this.objGetVisibility = domGetVisibility;
}

The main reason why I’m sharing implementations is that the functionality to make the DHTML effect is the same with a DOM-compliant browser as it is for IE — with some minor differences, that we’ll get to in later sections.

We’ll look at the implementation of these interface methods and test them out in the next pages.