reptile7's JavaScript blog
Monday, January 03, 2011
 
Compucide...Not
Blog Entry #201

At the end of our last episode, we were taking a hard look at the code of the help.html remote control page of HTML Goodies' "How to Populate Fields from New Windows Using JavaScript" tutorial. As it is desirable for various reasons to keep JavaScript and HTML separate, we would specifically like to specify help.html's user interfaces as normal HTML and not via document.write( ) commands. How might we do that and yet preserve the original code's functionality for both JavaScript-enabled users and sans-JavaScript users?

none to block

Drawing inspiration from HTML Goodies' "Drop-Down Menu" tutorial, which we dissected in Blog Entry #96, it occurred to me that we could initially conceal the help.html user interfaces via a display:none CSS declaration and then visualize them for JavaScript-enabled users by switching the display value to block. Towards this end, let's first
(a) take the interfaces out of the document.write( ) commands that write them, and then
(b) place them, as normal HTML, in div element containers.

<div>[<a href="#" onclick="checkRadio('myForm', 'inputRadio', 0); return false;">Choose input #4</a>]</div>

(On the original help.html page, each interface has a de facto block-level rendering by virtue of being sandwiched between a p element and an h2 element (excepting the Input #3 interface, whose preceding <p>Input #3 is a required field... lacks a closing </p> tag and thus has an inline rendering), so a div container seems appropriate.)

Per the above, the div elements are annihilated (as in "reduced to nothingness") with

<style type="text/css">
div { display: none; }
</style>


and then brought back to life via:

<script type="text/javascript">
function visualize( ) {
    invisElements = document.getElementsByTagName("div");
    for (i = 0; i < invisElements.length; i++)
        invisElements[i].style.display = "block"; }
window.onload = visualize;
</script>


For the Input #1, Input #2, and Input #7 interfaces, the <form onsubmit="return false;"></form> containers can and should be thrown out.

<div>
<label>Fill in input #1: <input id="inputA" type="text" name="myInput" value="Text."></label>
<input type="button" onclick="input('myForm', 'input1', 'inputA');" value="Update">
</div>


Although wrapping the Input #[1|2|7] interfaces in form elements allows their buttons to access the myInput/myOtherInput/myTextarea values via this.form.controlName.value expressions, I would nonetheless argue that it is semantically wrong for the forms to be there as we are not submitting the myInput/myOtherInput/myTextarea values to a processing agent but are merely sending them to forms.html's myForm form.

Alternatively, if we were to equip the myInput/myOtherInput/myTextarea fields with id attributes - say, id="inputA", id="inputB", and id="textareaA", respectively - then the buttons could send the id values to a

function input(formName, obj, controlID) {
    opener.document.forms[formName].elements[obj].value = document.getElementById(controlID).value;
    self.close( ); }


function that only needs to reference a field value and not a containing form.

For the Input #6 interface, we can add to the visualize( ) function a statement that will conditionally set the Choose|Deselect input #6 link text:

if (!opener.document.myForm.inputCheck.checked)
    document.getElementById("checkboxA").innerHTML = "Choose input #6";
else
    document.getElementById("checkboxA").innerHTML = "Deselect input #6";
...
<div>[<a id="checkboxA" href="#" onclick="check('myForm', 'inputCheck', true); return false;">Choose input #6</a>]</div>


The above code does not set the check( ) arguments[2] value to false for a Deselect input #6 link, but recasting check( ) as

function check(formName, obj, choice) {
    if (opener.document.myForm.inputCheck.checked)
        choice = false;
    opener.document.forms[formName].elements[obj].checked = choice;
    self.close( ); }


will take care of that.

Disable it

The display:none|block approach works well with any semimodern*, CSS-supporting GUI browser whether its "Enable JavaScript" preference is switched on or off. (*The getElementsByTagName( ) method goes back to the DOM Level 1 Core - it's been around for a while.) I've also come up with a lower-end approach for users of browsers that support neither JavaScript nor CSS, e.g., Lynx and some mobile browsers: we can't display:none the help.html interfaces for such users, but we can at least disable the controls of those interfaces via the HTML disabled attribute.

<label>Fill in input #1: <input disabled id="inputA" type="text" name="myInput" value="Text."></label>
<input disabled type="button" onclick="input('myForm', 'input1', 'inputA');" value="Update">


For JavaScript-enabled users, the disabled controls can be undisabled à la the display:none|block approach:

disabledInputs = document.getElementsByTagName("input");
for (i = 0; i < disabledInputs.length; i++)
    disabledInputs[i].disabled = false;
document.getElementById("textareaA").disabled = false;


Ah, but what about the interface links? As far as I know, links cannot be disabled in the same way that controls can. But I don't like the use of links for non-link purposes anyway - we really should convert the interface links to push buttons, shouldn't we?

[<input disabled type="button" onclick="checkRadio('myForm', 'inputRadio', 0);" value="Choose input #4">]

Although select is not a JavaScript reserved word, I find that I am strangely unable to call help.html's select( ) function from an <input type="button"> button - no errors are thrown, nothing happens at all - with the OS X GUI browsers on my computer excepting Internet Explorer, perhaps because this function is being mistaken for the select( ) method of the DOM's HTMLInputElement interface: giving the select( ) function a clean name (chooseOption( ), makeSelection( ), whatever) solves this problem.

If we trade in the Input #6 interface link for an <input type="button"> button, then the label on that button can be toggled via a document.getElementById("checkboxA").value expression - the innerHTML property is of course not relevant to empty elements.

One small change to forms.html

JavaScript-enabled users are able to see the forms.html and help.html pages at the same time; this can be so for sans-JavaScript users as well if we
(a) equip each help icon link with a target="helpWindow" attribute, or equivalently and preferably
(b) add a <base href=currentDocumentURL target="helpWindow"> element to the document head.

Validate it

Because the author went to the trouble of splitting the HTML end-tags written by help.html's document.write( ) commands, and because forms.html and help.html both begin with a document type declaration and are already sitting on the Web at the WebReference.com site, I thought, "Why don't we run forms.html and help.html through the W3C's markup validator and see if they're really valid?" Let's do that now, shall we?

http://www.webreference.com/programming/javascript/jf/column11/forms.html
This document passes validation but also generates two "warnings" because of a misspecified public identifier in its document type declaration. For an HTML 4.01 Strict document, the correct public identifier is -//W3C//DTD HTML 4.01//EN and not -//W3C//DTD HTML 4.01 Strict//EN. FYI: Document type declarations for HTML 4.01 Strict, Transitional, and Frameset documents are detailed in the "HTML version information" section of the HTML 4.01 Specification.

http://www.webreference.com/programming/javascript/jf/column11/help.html
This document does not pass validation because of the unescaped textarea element end-tag in the Input #7 section:

document.write('<label>Fill in input #7: <textarea name="myTextarea" value="Text goes here."></textarea></' + 'label>');

More specifically, the validator interprets the </textarea> as non-script markup and correspondingly throws an end tag for element 'TEXTAREA' which is not open error. The error disappears if the </textarea> is split à la the other document.write( )-written end-tags:

document.write('<label>Fill in input #7: <textarea name="myTextarea" value="Text goes here."></' + 'textarea></' + 'label>');

Less importantly, this document's document type declaration also holds an incorrect -//W3C//DTD HTML 4.01 Strict//EN public identifier, which generates the same warnings that it did in the preceding validation but, as noted above, is not sufficient to invalidate the document.

A related aside

HTML Goodies' Getting Started Tutorial sector sports a "So, You Want An HTML Declaration, Huh?" tutorial that deconstructs a system identifier-lacking document type declaration and briefly addresses the age-old question, "Does my document really need a document type declaration in the first place?" I would argue that the latter question can be answered by answering another question: "Just how serious am I about what I'm doing?"

Let's suppose you are starting an e-business selling the next big thing, whatever it is, and you are determined to reach out to as many users as possible, regardless of what types of devices/user agents they might be using. Towards this end, you religiously code your Web site's pages to the W3C's standards and plan to run them through the W3C's markup validator to ensure that they are up to scratch. Do you need document type declarations at the top of your pages? Yep - your pages won't validate without them. Conversely, if what you're doing isn't so crucial - let's say you are a 'weekend silicon warrior' who merely wants to create a page showing off your vacation photos - and standards are less of an issue for you (you can't ignore them completely, but you know what I mean), then you can forgo the declaration.

In the following entry, we'll move on to the next Beyond HTML : JavaScript sector tutorial, "Bring Your Forms to Life With JavaScript".

reptile7

Comments: Post a Comment

<< Home

Powered by Blogger

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