reptile7's JavaScript blog
Sunday, June 28, 2009
 
Opening New Browser Windows at Elevated pH
Blog Entry #149

In our last episode, we discussed HTML Goodies' "Quick Window" script, which pops up a fenster window
The 'Quick Window' window
upon mousing over Joe's Open the message window onMouseOver link if the user's browser is not set to block pop-up windows. We significantly noted that click events can in most cases override pop-up window blocking and that we should consequently use an onclick event handler to call the msgopen( ) function that opens the fenster window.

As you can see, there's not much to the fenster window/document, and therefore I thought we might wrap up our look at the "Quick Window" script by 'upgrading' fenster in a way that makes use of the base element in the fenster document head. Towards this end, I came up with the idea of converting fenster into a navigational device for the various "Opening New Windows With JavaScript" tutorials, something like this:
The fenster window as a navigational device
The base element

Like the document.open( )/document.close( ) methods and the with statement, the base element is another "Quick Window" first for us - who knew that we would break all of this new ground in such a simple script? Covered at the end of the "Links" chapter in the HTML 4.01 Specification, the HTML base element specifies a "base" URL for the "resolution" of relative URLs - i.e., for the conversion of relative URLs to full URLs - in a document. (At least this is the base element's main purpose - as noted below, the base element can also be used to specify a common target for a document's links.) The base element necessarily appears in the document head and is an empty element. In the HTML 4.01 Strict DTD, the base element has a single attribute, a #REQUIRED href attribute whose value is the base URL. In the HTML 4.01 Transitional DTD, the base element has two attributes, href and target, which are both #IMPLIED; the target attribute of the base element serves the same function as does that for the anchor element.

To refresh your memory, here is the fenster document's base element and also the anchor element to which it presumably applies:

<base target='Frame 0' /> ...
/* Frame0 is the name of the display - change it if necessary */
...
<a href='http://www.htmlgoodies.com/'>http://www.htmlgoodies.com</a>


It's not clear from the script if the author is targeting the http://www.htmlgoodies.com link to the opener window or to another new window. Both Frame 0 and Frame0 are valid values for the name property of the window object - we'll go with the former for the following discussion. (Yes, window names can contain space characters, at least for the time being. However, I see that the data type designation for the name attribute of the frame element switches from CDATA (whitespace is OK) to NMTOKEN (whitespace is not OK) in going from HTML to XHTML.)

Frame 0 is the name of the display? I don't know what this means. Frame 0 would be the name of the opener window if the opener document's source included a window.name = "Frame 0"; or self.name = "Frame 0"; script command, which it doesn't. Otherwise, Frame 0 would be the name of a newly opened third window (or a new tab, depending on the browser) into which http://www.htmlgoodies.com/ would be loaded upon clicking the http://www.htmlgoodies.com link in accord with 'precedence #4' of the "Target semantics" section of the HTML 4.01 Specification, and this is what actually happens with Joe's demo. You can verify that Frame 0 is the name of the new window by typing javascript:window.alert(window.name); into the browser's address bar and then hitting the return/enter key.

We could just as easily put the target='Frame 0' attribute in the anchor element start-tag and then ditch the base element. But for a larger document with a bunch of links that load their href resources into a common window, putting the target information in a single base element is definitely a good idea.

Tutorial navigation

The "Quick Window" script's base element doesn't have a href attribute, and that's where we and our tutorial navigation device come in. Let's first list the full URLs for the "Opening New Windows With JavaScript" tutorials:

(1) "New Window: No Title Bar": http://www.htmlgoodies.com/beyond/javascript/article.php/3471181
(2) "Quick Window": http://www.htmlgoodies.com/beyond/javascript/article.php/3471271
(3) "Remote Image": http://www.htmlgoodies.com/beyond/javascript/article.php/3471311
(4) "Hello Goodbye": http://www.htmlgoodies.com/beyond/javascript/article.php/3470941
(5) "So, You Want A Pop-Under Window, Huh?": http://www.htmlgoodies.com/beyond/javascript/article.php/3471241
(6) "The Same Size": http://www.htmlgoodies.com/beyond/javascript/article.php/3471321
(7) "Remote Control JavaScript": http://www.htmlgoodies.com/beyond/javascript/article.php/3471281

All of the above URLs are of the form http://www.htmlgoodies.com/beyond/javascript/article.php/#######, and consequently we will use their common http://www.htmlgoodies.com/beyond/javascript/article.php/ scheme://hostname/path/ for a base href value:

<base href='http://www.htmlgoodies.com/beyond/javascript/article.php/' />

We can now use the seven-digit endings of the full URLs as relative URLs in writing links to the tutorials, e.g.:

<a href='3471181'>New Window: No Title Bar</a>
<a href='3471311'>Remote Image</a> <!-- etc. -->


Next issue: where shall we target the tutorial links, to the opener window or to a new window? If you want the opener document - the "Quick Window" tutorial in this case - to still be on hand (and I would want it to be on hand), then you'll want to open the links in a new window. To do this, we pick an unused name for the new window - I don't care for Frame 0 or Frame0, so how 'bout spot? - and assign it to the target attribute of the base element:

<base href='http://www.htmlgoodies.com/beyond/javascript/article.php/' target='spot' />

We should be good to go at this point, right? In practice, I ran into a tar pit of inconsistent browser behavior in piecing everything together. Here's what I observe on my computer:

(1) The gold goes to Firefox, which allows me to target the tutorial links either to a new window, a new tab, or the opener window per my preference without incident. Another plus: Firefox keeps the focus with the navigation window, where it belongs.

(2) Opera and Safari will open the tutorial links in a new window but not in a new tab. Safari consistently puts the focus with the new window; however, focus is readily transferred to the navigation window by equipping each link with an onclick='window.setTimeout(\"window.focus( )\", 500);' attribute. Strangely, Opera insists on putting the focus with the new window upon following a first link but then regularly puts the focus with the navigation window upon following subsequent links. Targeting the links to the opener window is somewhat problematic for both of these browsers, which will reliably open a first link in the opener window but then open subsequent links in one or more new windows. (The aforementioned javascript:window.alert(window.name); test shows that these windows and the opener window all have the same name - this isn't supposed to happen!)

(3) Internet Explorer - more specifically MSIE 5.2.3 for Mac OS X, not exactly the cutting edge vis-à-vis MSIE development - does not offer tab-browsing. MSIE will open the tutorial links in a new window with the same size as the 650px-by-225px navigation window, which might be OK for a small-screen computer but is distinctly annoying on a large screen - in contrast, the corresponding Firefox, Opera, and Safari new windows have the same size as the opener window. MSIE puts the focus with the new window; targeting the links to the opener window, which is trouble-free with MSIE, similarly puts the focus with the opener window; in both cases, focus can be transferred to the navigation window with onclick='window.setTimeout(\"window.focus( )\", 500);' link attributes as for Safari. Finally, I find that MSIE acts on the target attribute but not the href attribute of a

write("<base href='http://www.htmlgoodies.com/beyond/javascript/article.php/' target='spot' />");

command; it is necessary to code a 'normal' non-script base element in the opener document head for MSIE to recognize the base URL. (Alternatively, the openee document can be placed in a separate file so that it doesn't have to be written with a script.)

Time to try it all out:

Welcome to the "Quick Window" Demo Opener Div.

Click here to open the "Opening New Windows With JavaScript" navigation device.


If you have turned off JavaScript support for your browser or if your browser does not support JavaScript, then clicking the demo link will reload the current page.

In the following entry, we'll take up "Remote Image", the next "Opening New Windows With JavaScript" tutorial.

reptile7

Wednesday, June 17, 2009
 
A Quick One, While the W3C's Away
Blog Entry #148

Today we begin a tour of HTML Goodies' "Opening New Windows With JavaScript" series of tutorials.

I've still got some homework to do for the "New Window: No Title Bar" tutorial, so in this post we will check over the "Quick Window" tutorial. "Quick Window" presents a script that pops up a new window and document in response to a mouseover event. The "Quick Window" script is reproduced in the div below:

<script type="text/javascript">
<!-- vor alten Browsern verstecken
/* Copyright Arnold Schiller
http://homepages.muenchen.org/bm596269 (not a functional link) */

function msgopen( ) {
var fenster = window.open("", "Welcome", "scrollbar=yes,width=400,height=200");
fenster.document.open( );
with (fenster.document) {
write("<html><base target='Frame 0' /><body onblur='window.close( );'>");
/* Frame0 is the name of the display - change it if necessary */
write("Hi There! 
");
write(" Here's a quick way to get another window up here.<br />");
write("<a href='http://www.htmlgoodies.com/'>http://www.htmlgoodies.com</a><br />");
write("<p>Close the window to grab the script.<br />");
write("</body></html>");
}
fenster.document.close( );
} 

// -->

</script>

<!-- Joe's triggering code: -->

<a onmouseover="msgopen( );" href="http://www.htmlgoodies.com">Open the message window onMouseOver.</a>

As it appears on the quickwindowcode.html page, the "Quick Window" script contains a mistake (not present in the "Quick Window" tutorial source) that would prevent it from running:

write("Hi There!
");
// One more time, folks: JavaScript strings cannot contain line breaks.


should actually be

write("Hi There! <br>");

Even without this mistake, Joe's Open the message window onMouseOver. link demo will probably not work for you if your browser is set to block pop-up windows - more on this in the "Triggering msgopen( )" section below.

Joe provides no deconstruction at all for the "Quick Window" tutorial, and thus it is up to us to heroically rise to the task...

The new window

The tutorial script mostly consists of a msgopen( ) function that opens a new window and then creates a document contained by that window - cf. HTML Goodies' JavaScript Primers #12, which discusses a similar script. The msgopen( ) command that opens the window is:

var fenster = window.open("", "Welcome", "scrollbar=yes,width=400,height=200");

We went over the basics of the open( ) method of the window object in Blog Entry #25, but allow me to take the opportunity to provide you with up-to-date references in this regard:
(1) The window.open( ) page of Mozilla's Gecko DOM Reference
(2) The MSDN Library's window.open( ) page
(The W3C's Window Object 1.0 Specification seems to be stuck in neutral, I'm sorry to report.)

The new window's object reference will be fenster. The first window.open( ) argument is an empty string, meaning that fenster will not load an external resource; rather, fenster's document will be coded by msgopen( )'s subsequent commands. The second window.open( ) argument assigns Welcome to the value of fenster's name property - no targeting use is made of this name, so we could use an empty string here too if we wanted to.

Regarding the third window.open( ) argument, the "scrollbar" feature should actually be scrollbars: this feature would equip fenster with a vertical/right scrollbar if the height of fenster's document exceeded fenster's height. With respect to the original "Quick Window" code, fenster is taller than its document and thus the scrollbars feature has no effect, or at least that's what I observe with the browsers on my computer. The width=400 and height=100 fenster features are self-explanatory.

The new document

The "Quick Window" script uses document.open( ) and document.close( ) commands to respectively "open" and "close" the fenster document:

fenster.document.open( );
...fenster.document.write( ) commands...
fenster.document.close( );


Although the open( ) and close( ) methods of the document object were implemented in JavaScript 1.0, the first version of JavaScript, this is our first brush with these methods. Quoting Mozilla, [t]he document.open( ) method opens a document for writing, whereas [t]he document.close( ) method finishes writing to a document [that was] opened with document.open( ). The document.open( ), document.write( ), and document.close( ) methods all appear in the DOM Level 2 HTML Specification, and the W3C insinuates that a document.write( ) command is necessarily preceded by a document.open( ) command, but in practice this is not true - Joe did not use document.open( ) and document.close( ) commands in Primer #12, for example. We can in fact comment out the above fenster.document.open( ) and fenster.document.close( ) commands without any problems, but their use here is consonant with the design of the script, which does indeed create the fenster document from scratch, so let's just leave them in place, shall we?

Perhaps you are wondering, "What good are document.open( ) and document.close( ), anyway?" Mozilla notes that these methods can be used to clear and overwrite a document on the fly, if for some reason you wanted to do that.

Moving on...sandwiched between the fenster.document.open( ) and fenster.document.close( ) commands is a with package of six fenster.document.write( ) commands that writes the fenster document:

with (fenster.document) {
write("<html><base target='Frame 0' /><body onblur='window.close( );'>");
/* Frame0 is the name of the display - change it if necessary */
write("Hi There! <br />");
write(" Here's a quick way to get another window up here.<br />");
write("<a href='http://www.htmlgoodies.com/'>http://www.htmlgoodies.com</a><br />");
write("<p>Close the window to grab the script.<br />");
write("</body></html>"); }


This is our first encounter with the with statement, which establishes the default object for a set of statements, again quoting Mozilla. By placing the fenster.document object reference in the parentheses following the with keyword, we can subsequently truncate fenster.document.write( ) to write( ) in the with statement body - cool, huh? And yes, the fenster.document.open( )/fenster.document.close( ) commands could have been included in the with block.

Anything interesting in those write( ) commands? write( ) commands #2-6 are unremarkable, but write( ) command #1 raises a couple of red flags:

(1) The fenster document head contains a <base target='Frame 0' /> base element, of which no use is made and which could easily be deleted. Of course, there's nothing stopping us from making use of it, and we'll do so later in the post.

(2) The fenster document's body element is equipped with an onblur='window.close( );' attribute that closes the fenster window when it loses focus, e.g., when the user clicks outside of the window or types Command-` to 'cycle' to the opener window. This code works with all of the browsers on my computer but would be W3C-invalid in a non-script HTML context; according to the W3C, onblur is legit for the anchor, area, label, input, select, textarea, and button elements but not for the body element. Alternatively, the onblur action can be achieved in a purely programmatic (non-HTML) way with the following line of code, which can be placed before or after the with block in the msgopen( ) function:

fenster.onblur = closeMe; function closeMe( ) { fenster.close( ); }

(FWIW: Netscape says it's OK to use onblur with the body element but Microsoft doesn't, and yet in Joe's demo fenster is indeed sent packing when I blur it when using MSIE 5.2.3 for Mac OS X.)

Triggering msgopen( )

As shown in the div at the beginning of the post, the code that triggers the msgopen( ) function is:

<a onmouseover="msgopen( );" href="http://www.htmlgoodies.com">Open the message window onMouseOver.</a>

We could of course pair onmouseover with a variety of elements - a button element, an img element, a span element, etc. - but because msgopen( ) does access a new resource, i.e., the fenster document, I would say that the use of an anchor element is appropriate in this case. But at the risk of nixing the "Quick Window" tutorial's raison d'être, onmouseover is not the best choice of event handlers here.

Modern browsers are typically set to block pop-up windows by default - at least this is true for Firefox, Opera*, and Safari on my computer. In practice, browsers vary in their pop-up window blocking behavior: a given browser will allow some events to pop up new windows but not others, whereas the specific events that are able to override pop-up window blocking vary from browser to browser.* For example, I find that I can trigger msgopen( ) with keypress events when using Safari but not when using Firefox. Firefox, Opera, and Safari all prevent a mouseover event from popping up a new window but they all allow a click event to do so, and thus we are better off triggering msgopen( ) with an onclick event handler instead.

*For Opera, the default Block unwanted pop-ups setting gives the behavior described above; Opera also has a Block all pop-ups setting that chokes off all pop-up windows, including those that would be generated by click events.

A complication arises in switching from onmouseover to onclick: besides calling msgopen( ) and opening fenster, clicking the Link text also loads the href destination document, http://www.htmlgoodies.com in the preceding code, into the opener window. In this situation, Mozilla recommends that we append to the onclick event handler a return false statement that will cancel the link's default href action for JavaScript-enabled browsers but will preserve the href capability for browsers that do not enable or support JavaScript. Using

<a onclick="msgopen( ); return false;" href="http://www.google.com">Click here to open the message window.</a>

as a test element, I can confirm that this return false business works as advertised for all of the browsers on my computer.

If you would rather zero out the href action altogether, you could, I suppose, in time-(dis)honored manner, set the href attribute to # or to an empty string (it's not invalid to do this, but it's poor design), although I myself would go with a push button as the 'user interface' in that case.

Demo

Let's put this off until next time.

reptile7

Saturday, June 06, 2009
 
Slide Show and Tell
Blog Entry #147

Demo #1

Having retooled the script in HTML Goodies' "So, You Want A Slide Show, Huh?" tutorial in the previous entry, it's time for a demo:

[Slide show]
Something I forgot to mention last time: XHTML 1.0 deprecates the name attribute for both the img and form elements: you may want to remove the mypic and joe identifiers and replace their object references with document.images[0] and document.forms[0], respectively. I'll tell you where I got the demo's button images later, but for now, I would like to go through some of the comments that follow the tutorial. Q & A (1) We begin with wolfie, who asks, Here is something I have not been able to find: How do I place a slide show (say 3 images) into a column in a table? You can't put a slide show in the HTML col element, which is an empty, 'contentless' element. But you could definitely load all of your slide show HTML into a td element, and then stretch that cell into a column by setting its rowspan attribute to an appropriate value. Content model-wise, the td element can contain just about anything (more specifically, the td element has a %flow; content model, meaning that it can contain block-level and/or inline children). (2) CassieLain's hand is raised: Is there any way that you can make the images a link to another page? No problemo - just follow these three simple steps: (a) As we did for the slide show image captions in the previous post, create an array of URLs that your images will link to: var myLink = new Array( ); myLink[1] = "http://www.someWebsite1.com"; myLink[2] = "http://www.someWebsite2.com"; myLink[3] = "http://www.someWebsite3.com"; myLink[4] = "http://www.someWebsite4.com"; (b) Place the slide show img placeholder in an anchor element whose href attribute is set to the URL for the first image: <a href="http://www.someWebsite1.com" target="_blank"><img width="" height="" src="image1.gif" alt="[Slide show]" /></a> <!-- The target="_blank" attribute will cause your links to open in new windows; take it out if you don't want it. --> (c) Add the following command to the slideshow( ) function: document.links[0].href = myLink[num]; Try it out below:
[Slide show]
(3) We lastly call on e&b: One question though, I would like to have it auto play and auto rotate in an infinite loop. Without the back and next buttons. Have tried a few things but no luck. Can you help?

An excellent question, e&b. But you don't want a slide show - you want an animation:

[Animation]
You're in luck: in JavaScript 1.1, Netscape presented a code template for a JavaScript animation on which the preceding demo is based, and which was very helpful in my putting together this and the previous posts, giving credit where credit is due. The key to Netscape's animation code is the img element's onload event handler: <img src="image1.gif" alt="[Animation]" onload="window.setTimeout('animate( );', delay);" /> (FYI: onload is a W3C-valid (X)HTML attribute only for the body and frameset elements, but if both Microsoft and (of course) Netscape say that onload can be used with the img element, then we should be good to go, eh?) In brief: (1) The onload attribute calls the animate( ) function after delay milliseconds. delay can be increased or decreased via the slower( ) or faster( ) function, which is called by clicking the or button, respectively. (2) The animate( ) function assigns a new image to the img placeholder and increments imageNum (the image index) à la our slideshow( ) function. When the new image loads, the onload attribute refires; animate( ) is called after delay milliseconds; the next image loads and imageNum is incremented; the onload attribute refires; and so on: a form of indirect recursion, if you will. Practical notes • Netscape's initial delay value, 100, results in an absurdly fast animation, at least for what we're doing here; delay = 1000 gives a much saner display. Moreover, Netscape's slower( )/faster( ) increment/decrement, 10, is way too small for my tastes, so I've beefed it up to 500: function slower( ) { delay += 500; if (delay > 3000) delay = 3000; } function faster( ) { delay -= 500; if (delay < 0) delay = 0; } • The animation does not run smoothly (and with MSIE and Safari, it doesn't run at all) unless imageNum is incremented before the image src-writing statement in the animate( ) function: function animate( ) { imageNum++; document.images[0].src = theImages[imageNum].src; document.forms[0].input0.value = myText[imageNum]; if (imageNum == 6) imageNum = 0; } Go back and look at Joe's slideshowUp( )/slideshowBack( ) functions - note that num is incremented/decremented before a new image is assigned to mypic's src. Where'dja get those buttons? Once upon a time, HTML Goodies hosted a "Free Images" page at http://www.htmlgoodies.com/freeimages/ (not a functional link) that offered for free download a collection of >400 clip art images. If I recall correctly, I learned about the "Free Images" page from HTML Goodies' "Basic HTML: Images" tutorial, which linked to it. Courtesy of the Internet Archive's "Wayback Machine", the "Free Images" page can still be seen here (among other archive locations). Most of its images are no longer available; however, following the WebSpice Images link leads to a "WebSpice Images" page on which the websp5.zip and websp6.zip archive links are still live, believe it or not - my button images have been taken from these archives. The button images' original file names are fretrial.gif, iaccept.gif, java2.GIF, joinnow.gif, notacept.gif, and notagree.gif, which can be changed/ordinalized for preloading the images automatedly, as discussed in the previous entry. If you're looking for free clip art images for your own slide show/animation, a "clip art" free Google search will lead you to plenty of relevant Web sites.

The next Beyond HTML : JavaScript tutorial is "So, You Want A Shopping Cart, Huh?", which offers a bewildering shopping cart application that spans several files (including a frameset file whose source contains twenty-five functions, some of which are used to create custom objects) and that would probably take us months to slog through, and I would just as soon not do that. ("You wimp!") After that we have an "Opening New Windows With JavaScript" series of tutorials, which would for us be largely a retread but maybe we could go through them quickly.

reptile7


Powered by Blogger

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