Here’s a simple script that can be called from a button press or link click; it merely changes the docu-ment displayed in the iframe: function changeIFrame { document.getElementById'myf
Trang 1</style>
</head>
<body>
<h1>A simple iframe</h1>
<p>Below is an iframe, styled in size with CSS and
displaying a different document.</p>
<iframe id="myframe" src="simple-iframe-content.html">
</iframe>
</body>
</html>
The HTML document displayed by the iframe is trivial and unstyled:
File: simple-iframe-content.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<body>
<p>This is a document <em>in</em> the iframe.</p>
</body>
</html>
Figure 8.1 shows the page display
document.
Trang 2There’s no sign that the iframe document is separate from that which surrounds it
Replacing iframe s
You can change the document that displays inside the iframe using a script loc-ated in the surrounding document If the iframe is styled so as not to draw at-tention to itself, this technique creates the illusion that part of the parent docu-ment has changed
The iframe element’s src attribute is, like other attributes on HTML elements, available as a property of the corresponding DOM object Here’s a simple script that can be called from a button press or link click; it merely changes the docu-ment displayed in the iframe:
<script type="text/javascript">
function changeIFrame() { document.getElementById('myframe').src = 'http://www.google.com/';
}
</script>
This example is so simple it doesn’t even need JavaScript iframes act like normal frames and can therefore be the target of any hyperlink You can display a link’s destination in an iframe by setting the target attribute on the link to the name
of the iframe
Retrieving Data with iframe s
With further scripting, it’s possible for the iframe’s newly-loaded page to pass data back to its parent page Scripts in the iframe content page can call functions
in the parent page by referring to those parent-page functions as
window.parent.functionName This means that we can put any number of smart scripts in the parent page, ready for triggering by the iframe content as needed Let’s look at a simple example First, the main page:
File: simple-iframe-2.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>A simple iframe example</title>
Using <iframe>
Trang 3<link type="text/css" rel="stylesheet"
href="simple-iframe-2.css">
<script type="text/javascript">
function receiveData(data) {
document.getElementById('response').firstChild.nodeValue = data;
}
</script>
</head>
<body>
<p>An iframe to which we send requests</p>
<iframe id="scriptframe" name="scriptframe" src=""></iframe> <p><a href="simple-iframe-content-2.html"
target="scriptframe">Send a request</a></p>
<div>
<h2>Response data received</h2>
<p id="response">No data yet.</p>
</div>
</body>
</html>
There’s a lot going on in this document, so let’s pick through it slowly First, there’s a link to a style sheet It’s trivial stuff:
File: simple-iframe-2.css
div {
border: 1px solid black;
padding: 0 0 1em 0;
width: 20em;
}
h2 {
background-color: black;
color: white;
text-align: center;
margin: 0;
}
div p {
padding: 0 1em;
}
#scriptframe {
width: 300px;
height: 100px;
Trang 4border: 2px solid red;
} Second, there’s a JavaScript function, receiveData Notice that it’s not called from anywhere in this page—it’s not even installed as an event listener It’s just sitting there, waiting for someone else to use it After that, there’s content Fig-ure 8.2 shows the page as it appears when it first loads
Let’s look at the page’s content tags closely First is the iframe Notice that both its id (for styling) and name (for links) are set to scriptframe This iframe will
be the target that receives the HTML document generated by the server in re-sponse to a request for information made by this page
Second, there’s a link It has a target of scriptframe, which matches the iframe This link will produce the request for information from the server
Third, we see a p element with id="response" This paragraph will display the data that has been retrieved from the server If you look closely, you’ll see that the receiveData function declared at the top of the page will do most of the work:
Using <iframe>
Trang 5File: simple-iframe-2.html (excerpt)
function receiveData(data) {
document.getElementById('response').firstChild.nodeValue = data;
}
All that’s missing is something that will call the function, and pass to it the data
to be displayed The response received from the server—and displayed in the iframe—will do just that:
File: simple-iframe-content-2.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<body>
<p>This iframe contains the response from the server.</p> <script type="text/javascript">
if (window.parent.receiveData) {
window.parent.receiveData(
'some data sent from the iframe content page!');
}
</script>
</body>
</html>
When this page loads, the script it contains is run automatically; it reaches up into the parent, delivering the server response in the form of a string of data ('some data…')
Figure 8.3 shows the page after the user has clicked the link
To summarize, clicking the Send a request link on the main page loads the content page into the iframe (note the target attribute on the link); that content page contains JavaScript that calls the receiveData function in the main page The end result is that content from the server is put into the current page without requiring that the whole page be reloaded If the requested page was a PHP-, ASP-, or other server-generated page, it could pass back from the server any dy-namic data required
Trang 6Figure 8.3 Updated iframe page after the link is clicked.
Overcoming iframe Restrictions
There’s an obvious flaw with this method, though: that big, ugly iframe sitting
in the middle of the page Although you wouldn’t need to apply the thick border shown above, it’s still there in the page
You might think that an easy solution would be to style the iframe to zero size,2 and indeed, that does work Setting the width and height properties of the iframe to 0 will effectively hide the iframe from view Links on the page can then load other pages into the iframe and receive data from them
Since a page is loaded into the iframe via a linked URL, it’s even possible to pass data in the request by adding it to the URL’s query string So, for example,
a link in the page could look up an item in a database by requesting iframe-content.php?id=32; the HTML generated by that PHP script would call back the receiveData function in the main page with details of item number 32 from the (server-side) database
2 You might also think of hiding it from view entirely with display: none , until you discovered that Netscape 6 entirely ignores iframe s that are undisplayed and, therefore, that approach, sadly, doesn’t work.
Using <iframe>
Trang 7The next notable flaw with this iframe approach is that it breaks the Back and Forward buttons in the user’s browser The loading of a page into an iframe is added to the browser history, so, from the user’s perspective, the Back button (which simply undoes the page load within the invisible iframe) doesn’t appear
to do anything
A solution is to use JavaScript’s window.location.replace method to load the new document into the iframe, replacing the current history item rather than adding to the history list This means that the browser history is unaffected and the Back (and Forward) buttons continue to work properly
This variation is described in great detail on Apple’s Developer Connection site,
in an article called Remote Scripting with IFRAME3 One further, extremely useful and elegant variation is outlined in that article It’s possible to dynamically create the iframe element with the DOM, rather than rely on it already being present
in the HTML document; this approach keeps the document’s HTML clean We’ll see this technique in action shortly
Example: Autoforms
Let’s conclude the discussion of iframes with a more advanced example
A recent trend in desktop environments has been to move away from dialog boxes with Apply buttons, and move towards a new type of dialog box: one which applies changes as soon as they are made by the user This feature provides a kind of
“real time” awareness of the user interactions, which take effect immediately When users finish making changes, they simply close the dialog box
Nested Form Design
Real-time forms are difficult to duplicate on the Web, because there needs to be
an active Apply Changes button to submit the form—complete with the user’s changes—to the server Remote scripting provides a means to implement this dynamic functionality on the Web
The core of the problem is this: the page that contains the form for dynamic
submission (which I’ll christen an autoform) needs to be able to submit that
form data to the server without submitting the whole page
3 http://developer.apple.com/internet/webcontent/iframe.html
Trang 8One way to achieve this is for the page to open a copy of itself in an iframe When the user changes a form element, the autoform reflects that change in the corresponding field in the copy Once the copy is updated, the page causes the copy’s autoform to submit, thus saving the data on the server without submitting the main page
Since this technique is an alteration to the way in which Web forms normally work, progress hints should be supplied to the user At the very least, you should indicate that the user’s change has been processed Ideally, when the user changes
a field, that field should indicate that the data is being processed; when the re-sponse is received, the field should update again to indicate that the processing
is complete Figure 8.4 shows these steps
Figure 8.4 Editing, saving, and a saved autoform field.
In Figure 8.4, the first field is untouched Below it, we see a field from which the user has clicked away, moving the focus to another field Note how the field changes: a floppy disk symbol is displayed, indicating that the field’s value is being saved (i.e the duplicate form in the iframe is being submitted) Below that field we see another field, which was changed earlier That data has been saved to the server, so the indicator has changed again to display a check mark Choose your own icons if you don’t like these ones
Avoiding Infinite Forms
Since the page is loaded twice—once in the browser window, and once in the hidden iframe—it needs special logic The version that’s loaded into the browser window (the “parent”) needs to create the iframe and load the second copy (the
“child”) into that iframe The child, however, mustn’t do the same thing, or else the browser will descend into an infinitely nested set of pages.4 Our script’s init method must contain some logic to prevent such nesting:
4 It might be fun to try, just to see what happens, though!
Example: Autoforms
Trang 9File: autoform.js (excerpt)
if (parent.document.getElementById('autoform_ifr')) {
aF.init_child();
} else {
aF.init_parent();
}
This code determines whether the current document should be initialized as the main form, or as a duplicate form loaded in an iframe It does this by looking for a containing iframe with ID autoform_ifr in the parent document
If this iframe is detected, then the page running this code must be the “child” containing the duplicate form; hence, we call the init_child method to initialize the page accordingly Otherwise, we call the method init_parent
Let’s now take a step back and look at the basic structure of the page We’ll then
be equipped to write our parent and child initialization methods
Setting up Content and Scripts
In the finished example, we’ll generate our form page using a server script, for reasons we’ll see shortly For the moment, however, let’s work with a static version
of the page:
File: autoform.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>A very simple form</title>
<link type="text/css" href="autoform.css" rel="stylesheet"> <script type="text/javascript" src="autoform.js"></script> </head>
<body>
<h1>A simple form</h1>
<form method="post" id="f1" class="auto">
<p>
<label for="name">Name</label>
<input type="text" name="name" id="name">
</p>
<p>
<label for="age">Age</label>
<input type="text" name="age" id="age">
</p>
Trang 10<p>
<label for="shoesize">Shoe size</label>
<input type="text" name="shoesize" id="shoesize">
</p>
<input type="submit">
</form>
</body>
</html>
Notice that the form has a Submit button, just like a standard Web form, and that there’s no iframe tag in the page Once our script gets to the page, these things will change
Here’s the style sheet for the page It’s quite simple, except that it contains rules for some classes and elements that are not yet present in the page:
File: autoform.css
input { padding-right: 20px;
display: block;
} autoform_pending { background: url(autoform_save.png) center right no-repeat;
} autoform_saved { background: url(autoform_saved.png) center right no-repeat;
}
#autoform_ifr { border: none;
width: 0;
height: 0;
} The second and third rules will apply to fields being auto-submitted, and fields for which auto-submission is complete, respectively The last rule guarantees that the iframe will be invisible to the user
With the basic HTML and CSS in place, we’re ready to consider the JavaScript
As in all the projects in this book, we’re aiming for neatly stored, reusable code, and a library object is the way we’ll achieve this Here’s the object signature for our autoform library object
Example: Autoforms
Trang 11File: autoform.js (excerpt)
var aF = {
addEvent: function(elm, evType, fn, useCapture) { },
init: function() { },
init_parent: function() { },
init_child: function() { },
cancel_submit: function(e) { },
parent_load_child: function() { },
parent_callback: function(elementNames) { },
parent_document_callback: function(docObj) { },
parent_element_change: function(e) { }
}
aF.addEvent(window, 'load', aF.init, false);
The addEvent and init methods serve the same purposes as always Initialization
is a big task for this example, so, as we’ve seen, init gets help from one of two other methods: init_parent or init_child
We’ll meet the remaining methods as we progress, but be aware that two of them
are callbacks Callbacks are methods that are called from outside this script by
code that the script launches In this example, the callback methods in the parent document will be called by the child document contained in the hidden iframe
To kick things off, here’s the full text of the init method; it’s a little more com-plete than the brief snippet we saw before:
File: autoform.js (excerpt)
init: function() {
if (!document.getElementById ||
!document.createElement ||
!document.getElementsByTagName ||
!document.getElementsByName) return;
if (parent.document.getElementById('autoform_ifr')) {
aF.init_child();
} else {
aF.init_parent();
}
},
This code tests for all the DOM facilities that our autoform might need If there’s
a lack of support, the iframe and scripting will not be used: a plain HTML form results