Section K. DOM hyperspace


K. DOM hyperspace

Now that we've discussed the most important parts of the W3C DOM and the Level 0 DOM, it's time to look at a few related subjects. The first is what I call the "DOM hyperspace." It plays a vital role in Usable Forms.

Take this code:

<p >I am a JavaScript hacker!</p> var x = document.getElementById('test'); x.parentNode.removeChild(x); 


The paragraph is removed from the document. But it's not gone. Where is it? I say that it floats in the DOM hyperspace. Since variable x still refers to it, it's still accessible. For instance, this works fine:

x.appendChild(document.createTextNode(' I never hack text nodes.')); document.body.appendChild(x); 


The text node is appended to the paragraph, and the revised paragraph is reinserted into the document.

It's important to realize that the element remains available only as long as a pointer to it exists; in that respect it's similar to cross-window communication, as we discussed in 6B. If I wrote the following, the connection would be lost, and could not be re-established:

var x = document.getElementById('test'); x.parentNode.removeChild(x); x = null; x = document.getElementById('test'); 


The last line doesn't work, since the element with ID="test" cannot be found in the document. The element is lost forever.

Every new element you create starts in the DOM hyperspace:

var x = document.createElement('div'); // append more stuff to the div document.body.appendChild(x); 


The new <div> element is created in the hyperspace, more elements and text nodes are added to it, and only when it's ready do we insert it into the document.

Storing elements in hyperspace

It's possible to store large numbers of elements in hyperspace, and Usable Forms does so. Taking a closer look at this part of the script is instructive.

In 8I, we saw that the script first gathers pointers to the <tr>s that should be removed. After that's done, the script goes through the hiddenFields array and moves the <tr>s to the DOM hyperspace.

However, moving them to hyperspace is not enough. The <tr>s should remain available for reinsertion into the document, and as we just saw, that requires that a variable or an array element continues to point to the element so that it isn't lost.

This part of Usable Forms takes care of that:

[Usable Forms, lines 14 and 41-51, condensed]

var hiddenFormFieldsPointers = new Object(); while (hiddenFields.length) {     var rel = hiddenFields[0].getAttribute('rel');     if (!hiddenFormFieldsPointers[rel])            hiddenFormFieldsPointers[rel] = new Array();     var relIndex = hiddenFormFieldsPointers[rel].length;     hiddenFormFieldsPointers[rel][relIndex] = hiddenFields[0];     // create newMarker (see section L)     hiddenFields[0].parentNode.replaceChild(newMarker,hiddenFields[0]);     waitingRoom.appendChild(hiddenFields.shift()); } 


The script creates an objecthiddenFormFieldPointersthat functions as an associative array (see 5K). As the name suggests, this associative array contains pointers to the <tr>s that aren't currently displayed (the ones that are floating in hyperspace.) In addition to providing a lookup table (see below), it also makes sure that pointers to all hidden form fields continue to exist, so that they're not lost.

hiddenFields[0] is the <tr> that's currently being removed from the document. The script takes the value of its rel attribute and checks if hiddenFormFieldPointer already has a key with this value. If not, it creates this key and assigns an empty array as its value:

var rel = hiddenFields[0].getAttribute('rel'); if (!hiddenFormFieldsPointers[rel])    hiddenFormFieldsPointers[rel] = new Array(); 


It's possible to have several <tr>s with the same value of the rel attribute; they should all be shown or hidden when the user selects the appropriate form fields. Therefore, the script has to find out how many <tr>s with this rel attribute are already in hyperspace. The array already points to all of them, so its length gives this number. I add a pointer to the <tr> as the last element of the array:

var relIndex = hiddenFormFieldsPointers[rel].length; hiddenFormFieldsPointers[rel][relIndex] = hiddenFields[0]; 


Now I can safely remove it from the document and send it to hyperspace; a pointer exists, so it is not lost.

waitingRoom.appendChild(hiddenFields.shift()); 


When the time comes to reinsert form fields into the document, I use hiddenFormFieldPointers as a lookup table for quick and easy access to the relevant <tr>s. Suppose the user clicks on this checkbox:

<input type="checkbox" name="country_other" rel="othercountry"> 


Now the <tr>s with rel="othercountry" should be reinserted into the document. That's easy: hiddenFormFieldsPointers['othercounty'] contains the correct <tr>s:

[Usable Forms, lines 121-136, condensed]

function intoMainForm(relation) {    var Elements = hiddenFormFieldsPointers[relation];    for (var i=0;i<Elements.length;i++) {           // find insert point    insertPoint.parentNode.insertBefore(Elements[i],insertPoint);    } } 


This script uses the rel value as a key to hiddenFormFieldsPointers, and finds an array. It goes through this array and reinserts all the <tr>s in the page. (We'll get back to finding the insert point in 8L.)

Hyperspace and innerHTML

Warning

Browser incompatibilities ahead


The same principles should apply when you work with innerHTML:

var x = document.getElementById('test'); document.body.innerHTML = ''; x.appendChild(document.createTextNode(' I never hack text nodes.')); document.body.appendChild(x); 


In most browsers, the changed paragraph is reinserted into the document. Unfortunately, in Explorer the original text of the paragraph is lost, and only the newly appended text node is visible. Therefore, these techniques are not usable in combination with innerHTML.

Is an element in hyperspace?

Occasionally you're not sure if a certain element is in hyperspace or not. You can check its position by seeing if the element has a parentNode. Generally, elements in hyperspace don't have one, since they're currently "free-floating." On the other hand, every HTML element in the page has a parentNode.

I use this trick in Sandwich Picker:

[Sandwich Picker, lines 188-191]

function removeButton() {     if (extraButton.parentNode)            extraButton.parentNode.removeChild(extraButton); } 


This function removes the extra button 'Collect all orders', and as we saw in 8D, removeChild() requires us to go to the element's parentNode first. However, if the element is already in hyperspace, it doesn't have a parentNode, so executing extraButton.parentNode.removeChild would give an error. That's why the function first checks if extraButton has a parentNode.



ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ppk on JavaScript. Modern, Accessible, Unobtrusive JavaScript Explained by Means of Eight Real-World Example Scripts2006
ISBN: N/A
EAN: N/A
Year: 2005
Pages: 116

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net