DOM Scripting - UCC CS | Intro

Download Report

Transcript DOM Scripting - UCC CS | Intro

Manipulating styles
through DOM scripting
Last year, we saw three
approaches to style specification
• Accessing an external stylesheet
– Using the link element
• Specifying an in-head stylesheet
– Using the <style> element
• Specifying style inline
– Using the style attribute
We also saw some simple
JavaScript-based style manipulation
• We saw that inline event handlers like
<p onmouseover = “highlight(this);”
onmouseout = “restore(this);” >
This is a paragraph. </p>
could invoke JavaScript functions to manipulate
the style of HTML elements
function highlight(someElement)
{ someElement.style.color=“red”; }
function highlight(someElement)
{ someElement.style.color=“red”; }
DOM Level 2 Style Specification
• In DOM Level 2 Style
http://www.w3.org/TR/DOM-Level-2-Style/
the W3C specified a much richer form of
style manipulation
DOM Level 2 Style has
two main parts
• DOM Level 2 Style
http://www.w3.org/TR/DOM-Level-2-Style/
has two main parts
• DOM Style Sheets, which provides generic
features for manipulating arbitrary stylesheets
http://www.w3.org/TR/DOM-Level-2-Style/stylesheets.html
• DOM CSS, which provides specific features
for manipulating CSS stylesheets
http://www.w3.org/TR/DOM-Level-2-Style/css.html
State of browser support
• Currently (2007):
– Both DOM Style Sheets and DOM CSS are
implemented by the Mozilla Firefox and Opera
browsers
– But Microsoft Internet Explorer
• Does not implement DOM Style Sheets
• Although it does seem to implement most
(but not all) of DOM CSS
• Thus, we will just look at DOM CSS
Some basic features of DOM CSS
• While the DOM CSS provides many other
features
– the most widely-used aspects are similar to
those features we encountered last year when
seeing how to control CSS with JavaScript:
• If someElement is a DOM object which represents
a XHTML element,
we can control the element’s presentation by
manipulating someElement.style
• Any CSS <property> can be controlled by
manipulating someElement.style.<property>
For example,
someElement.style.color=“green”;
• This is supported by all the main browsers
DOM CSS example 1
No style in XHTML page; all style applied in JavaScript
<!DOCTYPE html PUBLIC "-//W3C//DTD
XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/x
html1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DOM CSS Example 1</title>
<script src="script1.js"
type="text/javascript"></script>
</head>
<body>
<h1>Heading 1</h1>
<h1>Heading 2</h1>
<h1>Heading 3</h1>
<h1>Heading 4</h1>
</body>
</html>
function applyStyle()
{
var headers =
document.getElementsByTagName("h1");
for (var i=0; i < headers.length; ++i)
{ headers[i].style.color='green';
}
}
if (window.attachEvent)
{ window.attachEvent("onload",
applyStyle); }
else if (window.addEventListener)
{window.addEventListener("load",
applyStyle,false); }
Viewing this page in MS Internet Explorer
http://www.cs.ucc.ie/j.bowen/cs4408/resources/DOMStyle/DOMCSS/ex1/index.html
Viewing this page in Mozilla Firefox
http://www.cs.ucc.ie/j.bowen/cs4408/resources/DOMStyle/DOMCSS/ex1/index.html
Viewing this page in Opera 9.1
http://www.cs.ucc.ie/j.bowen/cs4408/resources/DOMStyle/DOMCSS/ex1/index.html
Of course, you can use DOM scripting to change
the stylesheet attached to an XHTML page
<!DOCTYPE html PUBLIC "-//W3C//DTD
XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/x
html1/DTD/xhtml1-strict.dtd">
<html
xmlns="http://www.w3.org/1999/xhtml"
>
<head>
<title>DOM CSS Exampe 2</title>
<script src="script1.js"
type="text/javascript"></script>
<link rel="stylesheet" href="style1.css"
type="text/css" />
</head>
<body>
<h1>First Header</h1>
<h1>Second Header</h1>
<h1>Third Header</h1>
</body>
</html>
• This XHTML page has a link
element which attaches the
stylesheet in file style1.css,
which contains just one rule:
h1 {color:red}
• But, when the page is loaded,
the program in file script1.js
will alter the href in the link
element, making it point to file
style2.css which contains:
h1 { color : green }
When the page is loaded, the headers are painted
red and an alert box is displayed
http://www.cs.ucc.ie/j.bowen/cs4408/resources/DOMStyle/DOMCSS/ex2/index.html
When the user clicks OK, a different stylesheet file
is used to display the XHTML page
http://www.cs.ucc.ie/j.bowen/cs4408/resources/DOMStyle/DOMCSS/ex2/index.html
The JavaScript file which produced this behaviour
function changeStyleSheet()
{
alert('When you click OK, the \n
stylesheet will be changed. \n\n
Watch the colours change \n
from red to green \n ');
document.
getElementsByTagName("link")[0].
setAttribute("href","style2.css");
}
if (window.attachEvent)
{ window.attachEvent("onload",
changeStyleSheet); }
else
if (window.addEventListener)
{window.addEventListener("load",
changeStyleSheet,false); }
• This cross-browser
JavaScript file attaches
the function
changeStyleSheet to
process a load event on
the window
• The function makes the
link element in the
XHTML file point to a
different CSS file
Adaptive forms
By using JavaScript for event handling,
you can make forms which adapt
themselves in response to user choices
• Using JavaScript there are two ways in which,
you can make forms which adapt
themselves in response to user choices
– You can use style control to determine
whether or not particular parts of a form are
displayed to a user
– You can use the DOM createElement method
to actually create new parts of a form
Using style control to create adaptive forms
• For example, suppose you have a form on which the
user is asked to give his address
– The form will include a section for getting the user’s
country
– It will also include a section for getting the user’s postcode
• But, initially, display of this section could be turned off,
because not all countries have post-codes
– But if the user specifies a country which is known to
use post-codes, the post-code section could be
displayed
• This example is implemented here:
http://www.cs.ucc.ie/j.bowen/cs4408/resources/DOMStyle/DOMCSS/ex3/prop1.php
• It is discussed in the next few slides
Initial view
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DOM CSS Exampe 3</title>
<script src="script1.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="style1.css" />
</head>
<body>
<form method="post"
action="/j.bowen/cs4408/resources/DOMStyle/DOMCSS/ex3/p
rog1.php">
<h1>Where are you?</h1>
<fieldset>
<legend>What country are you in?</legend>
<select name="countryCode" id="country">
<option value="">Select country</option>
<option value="fr">France</option>
<option value="ie">Ireland</option>
<option value="uk">United Kingdom</option>
</select>
</fieldset>
<fieldset id="codeField">
<legend>What is your post code?</legend>
<input type=text name=postCode />
</fieldset>
<button type="submit">Submit</button>
</form>
</body>
</html>
• The browser displayes
only one fieldset on this
form
• But there are actually two
fieldsets in the form
– The form contains a
fieldset for getting the
user’s post code but,
initially, it is not
displayed
– As will be seen later,
display of the postcode fieldset is turned
off in file script1.js
User has choice of countries,
two of which (UK and France) use post-codes
User selects UK, causing display of
the post code fieldset
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/x
html1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DOM CSS Exampe 3</title>
<link rel="stylesheet" type="text/css"
href="style1.css" />
</head>
<body>
<?php
$countryCode=$_POST['countryCode'];
if ($countryCode)
{ echo "<p>Your country code is: $countryCode</p>";
$postCode=$_POST['postCode'];
if ($postCode)
{ echo "<p>Your post-code is: $postCode</p>"; }
}
else
{?>
<script src="script1.js"
type="text/javascript"></script>
<form method="post"
action="<?php echo $_SERVER['PHP_SELF']; ?>">
<h1>Where are you?</h1>
<fieldset>
<legend>What country are you in?</legend>
<select name="countryCode" id="country">
<option value="">Select country</option>
<option value="fr">France</option>
<option value="ie">Ireland</option>
<option value="uk">United Kingdom</option>
</select>
</fieldset>
<fieldset id="codeField">
<legend>What is your post code?</legend>
<input type=text name=postCode />
</fieldset>
<button type="submit">Submit</button>
</form>
<?php
}
?>
</body>
</html>
The PHP program
• There is nothing special about
the PHP program which
generates the form and which
responds to a completed form
– If a request which activates the
program contains data, it reports
these data
– Otherwise, it generates a form to
get the data
• Both pages generated by the
program use a stylesheet style1.css
• Only the page containing the form
invokes the JavaScript file script1.js
The stylesheet file
form {width:500px;
background-color:yellow;
border-width:1;
border-style:solid;
padding:3.0mm}
fieldset { margin: 3.0mm;
padding:1.0mm }
p { color:red }
• There is nothing special
about the stylesheet file
The JavaScript file
function handleCountryChange(someEvent)
{
var country = document.getElementById("country");
if (country.value=="")
{ document.getElementById("codeField").style.display="none"; }
else
{
if (country.value!="ie")
{ document.getElementById("codeField").style.display=""; }
else
{ document.getElementById("codeField").style.display="none"; }
}
}
function setupForm(someEvent)
{
document.getElementById("codeField").style.display="none";
var country = document.getElementById("country");
if (window.attachEvent)
{ country.attachEvent("onchange",handleCountryChange); }
else if (window.addEventListener)
{ country.addEventListener("change",handleCountryChange,false); }
}
if (window.attachEvent)
{ window.attachEvent("onload",
setupForm); }
else if (window.addEventListener)
{ window.addEventListener("load",
setupForm,false); }
• The JavaScript file
contains two function
definitions and one
statement
• This statement uses a
cross-browser approach
to specify that a function
called setupForm should
be invoked when the
page has been loaded
The JavaScript file (contd.)
function handleCountryChange(someEvent)
{
var country = document.getElementById("country");
if (country.value=="")
{ document.getElementById("codeField").style.display="none"; }
else
{
if (country.value!="ie")
{ document.getElementById("codeField").style.display=""; }
else
{ document.getElementById("codeField").style.display="none"; }
}
}
function setupForm(someEvent)
{document.getElementById("codeField").
style.display="none";
var country = document.getElementById("country");
if (window.attachEvent)
{ country.attachEvent("onchange",
handleCountryChange); }
else if (window.addEventListener)
{ country.addEventListener("change",
handleCountryChange,false); }
}
if (window.attachEvent)
{ window.attachEvent("onload",
setupForm); }
else if (window.addEventListener)
{ window.addEventListener("load",
setupForm,false); }
• The function setupForm
acts as follows:
– Sets the display of the
post-code fieldset,
which has ID
codeField, to none
– Uses a cross-browser
approach to specify
that, whenever the
user selects a country,
a function called
handleCountryChange
should be executed
The JavaScript file (contd.)
function handleCountryChange(someEvent)
{
var country = document.getElementById("country");
if (country.value=="")
{ document.getElementById("codeField").style.display="none"; }
else
{
if (country.value!="ie")
{ document.getElementById("codeField").style.display=""; }
else
{ document.getElementById("codeField").style.display="none";
}
}
}
function setupForm(someEvent)
{document.getElementById("codeField").
style.display="none";
var country = document.getElementById("country");
if (window.attachEvent)
{ country.attachEvent("onchange",
handleCountryChange); }
else if (window.addEventListener)
{ country.addEventListener("change",
handleCountryChange,false); }
}
if (window.attachEvent)
{ window.attachEvent("onload",
setupForm); }
else if (window.addEventListener)
{ window.addEventListener("load",
setupForm,false); }
• The function
handleCountryChange
acts as
follows:
– Sets a local variable,
called country, to refer
to the select element
(which has ID country)
– Examines the value of
the option chosen for
the select element and
– Decides whether or
not to set the display
of the post-code
fieldset to none
Using createElement for adaptive forms
• Instead of using style control to show/hide elements of a
form which already exist
– You could use the DOM createElement method to create
elements on the form in response to user choices
• Consider, again, the form on which the user is asked to
give his address
– The form will include a section for getting the user’s
country
– Initially, it will not contain a section for getting the
user’s post-code
– But if the user specifies a country which is known to
use post-codes, the post-code section could be
created and displayed
• See an implementation here:
http://www.cs.ucc.ie/j.bowen/cs4408/resources/DOMStyle/DOMCSS/ex4/prog1.php
• It is discussed on the next few slides
What’s different?
• If you run this new implementation, the
user-interface looks the same as the
previous implementation
• But appearances can be deceptive
• This implementation actually behaves very
differently
• So, what is different?
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/x
html1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>DOM CSS Exampe 4</title>
<link rel="stylesheet" type="text/css" href="style1.css" />
</head>
<body>
<?php
$countryCode=$_POST['countryCode'];
if ($countryCode)
{ echo "<p>Your country code is: $countryCode</p>";
$postCode=$_POST['postCode'];
if ($postCode)
{ echo "<p>Your post-code is: $postCode</p>"; }
}
else
{?>
<script src="script1.js"
type="text/javascript"></script>
<form method="post"
action="<?php echo $_SERVER['PHP_SELF']; ?>">
<h1>Where are you?</h1>
<fieldset>
<legend>What country are you in?</legend>
<select name="countryCode" id="country">
<option value="">Select country</option>
<option value="fr">France</option>
<option value="ie">Ireland</option>
<option value="uk">United Kingdom</option>
</select>
</fieldset>
<button type="submit">Submit</button>
</form>
<?php
}
?>
</body>
</html>
The PHP program is
different
• When it generates the
form page, it does not
generate a fieldset for
the post-code
– See the program code
on the left-hand side of
this slide
• The post-code fieldset
will generated, when
necessary, by the
JavaScript in script1.js
The JavaScript file, script1.js
function handleCountryChange(someEvent)
{
…..
}
function setupForm(someEvent)
{
var country =
document.getElementById("country");
if (window.attachEvent)
{ country.attachEvent("onchange",
handleCountryChange); }
else if (window.addEventListener)
{ country.addEventListener("change",
handleCountryChange,false); }
}
if (window.attachEvent)
{ window.attachEvent("onload",setupForm);
}
else if (window.addEventListener)
{
window.addEventListener("load",setupForm,
false); }
• As before, the JavaScript
file contains two function
definitions and one
statement
• The statement is the
same as before
• The function setupForm
is the same as before
• The only difference (and
it is very significant) is in
the function
handleCountryChange
Before we examine handleCountryChange
• Before we examine handleCountryChange,
– We need to notice something important about
the behaviour of the form page
• Look at the next few slides
The initial view
• The user has not
selected any country
• There is no fieldset
for the post-code
The user makes an initial selection
• The user has selected
the United Kingdom
as his country
• A fieldset for the postcode has been
created
The user changes his mind
• The user changes his
mind
• He selects Ireland
• The fieldset for the
post-code has been
removed
The user changes his mind again
• The user changes his
mind again
• He selects France
• A fieldset for the postcode has been
created
Therefore …
• Thus, we notice that the function
handleCountryChange must both create
and remove a fieldset
• The safest thing to do is as follows:
– When it executes, the function should ensure
the form is in a known state
• By removing any fieldset it may have created on a
previous execution (if any)
– Then, the function can decide whether or not
to create a new fieldset
handleCountryChange
function handleCountryChange(someEvent)
{ var form = document.
getElementsByTagName("form")[0];
//Maybe the user has just changed his mind about the country.
//If so, remove any postCodeField element that had been created
//when he made his previous choice.
possibleExistingPostCodeField=
document.getElementById("postCodeField");
if (possibleExistingPostCodeField)
{form.
removeChild(possibleExistingPostCodeField);
}
//Now, react to the new value for country
var country = document.getElementById("country");
if (country.value!="")
{
if (country.value!="ie")
{
//Create new fieldset element
newField = document.createElement("fieldset");
newField.setAttribute("id","postCodeField");
//Insert the new fieldset just before the button
var button = form.getElementsByTagName("button")[0];
form.insertBefore(newField,button);
//Now add a legend to the new fieldset
newLegend = document.createElement("legend");
newField.appendChild(newLegend);
newLegendText = document.createTextNode("What is your post code?");
newLegend.appendChild(newLegendText);
//Now insert an input box into the new fieldset
newInput = document.createElement("input");
newField.appendChild(newInput);
newInput.setAttribute("type","text");
newInput.setAttribute("name","postCode");
}
}
}
• The function
handleCountryChange first
makes a local variable, called
form, point to the form on the
page
– (it is the first, and only, form
in the array of forms on the
page)
• Then it tries to make a local
variable called
possibleExistingPostCodeField
refer to an element with the ID
postCodeField
• Then, if such an element
exists, it removes the element
from the form
handleCountryChange (contd.)
function handleCountryChange(someEvent)
{ var form = document.
getElementsByTagName("form")[0];
//Maybe the user has just changed his mind about the country.
//If so, remove any postCodeField element that had been created
//when he made his previous choice.
possibleExistingPostCodeField=
document.getElementById("postCodeField");
if (possibleExistingPostCodeField)
{form.
removeChild(possibleExistingPostCodeField);
}
//Now, react to the new value for country
var country = document.getElementById("country");
if ( country.value!="“ )
{
if ( country.value!="ie“ )
{
//Create new fieldset element
newField = document.createElement("fieldset");
newField.setAttribute("id","postCodeField");
//Insert the new fieldset just before the button
var button = form.getElementsByTagName("button")[0];
form.insertBefore(newField,button);
//Now add a legend to the new fieldset
newLegend = document.createElement("legend");
newField.appendChild(newLegend);
newLegendText = document.createTextNode("What is your post code?");
newLegend.appendChild(newLegendText);
//Now insert an input box into the new fieldset
newInput = document.createElement("input");
newField.appendChild(newInput);
newInput.setAttribute("type","text");
newInput.setAttribute("name","postCode");
}
}
}
• Having ensured that the form
does not contain a post-code
fieldset, the function now
decides whether one should
be created
• It makes a local variable,
called country, refer to the
select element on the form
– which has the ID country
• The function will create a postcode fieldset only if
– The value of the option
chosen for the select
element is not the empty
string and
– is not the string “ie”
handleCountryChange (contd.)
function handleCountryChange(someEvent)
{ var form = document.
getElementsByTagName("form")[0];
//Maybe the user has just changed his mind about the country.
//If so, remove any postCodeField element that had been created
//when he made his previous choice.
possibleExistingPostCodeField=
document.getElementById("postCodeField");
if (possibleExistingPostCodeField)
{form.
removeChild(possibleExistingPostCodeField);
}
//Now, react to the new value for country
var country = document.getElementById("country");
if ( country.value!="“ )
{
if ( country.value!="ie“ )
{
//Create new fieldset element
newField = document.createElement("fieldset");
newField.setAttribute("id","postCodeField");
//Insert the new fieldset just before the button
var button = form.getElementsByTagName("button")[0];
form.insertBefore(newField,button);
//Now add a legend to the new fieldset
newLegend = document.createElement("legend");
newField.appendChild(newLegend);
newLegendText = document.createTextNode("What is your post code?");
newLegend.appendChild(newLegendText);
//Now insert an input box into the new fieldset
newInput = document.createElement("input");
newField.appendChild(newInput);
newInput.setAttribute("type","text");
newInput.setAttribute("name","postCode");
}
}
}
• If it has decided that a postcode fieldset should be
created, the function
– creates a new fieldset
element and makes a local
variable called newField
refer to it
– gives the ID postCodeField
to this new element
– Then … (see next slide)
handleCountryChange (contd.)
function handleCountryChange(someEvent)
{ var form = document.
getElementsByTagName("form")[0];
//Maybe the user has just changed his mind about the country.
//If so, remove any postCodeField element that had been created
//when he made his previous choice.
possibleExistingPostCodeField=
document.getElementById("postCodeField");
if (possibleExistingPostCodeField)
{form.
removeChild(possibleExistingPostCodeField);
}
//Now, react to the new value for country
var country = document.getElementById("country");
if ( country.value!="“ )
{
if ( country.value!="ie“ )
{
//Create new fieldset element
newField = document.createElement("fieldset");
newField.setAttribute("id","postCodeField");
//Insert the new fieldset just before the button
var button =
form.getElementsByTagName("button")[0];
form.insertBefore(newField,button);
//Now add a legend to the new fieldset
newLegend = document.createElement("legend");
newField.appendChild(newLegend);
newLegendText = document.createTextNode("What is your post code?");
newLegend.appendChild(newLegendText);
//Now insert an input box into the new fieldset
newInput = document.createElement("input");
newField.appendChild(newInput);
newInput.setAttribute("type","text");
newInput.setAttribute("name","postCode");
}
}
}
• Then …
– It makes a local variable,
called button, refer to the
only button element on the
form, and
– inserts the newField
element just before this
button element
– Then… (see next slide)
handleCountryChange (contd.)
function handleCountryChange(someEvent)
{ var form = document.
getElementsByTagName("form")[0];
//Maybe the user has just changed his mind about the country.
//If so, remove any postCodeField element that had been created
//when he made his previous choice.
possibleExistingPostCodeField=
document.getElementById("postCodeField");
if (possibleExistingPostCodeField)
{form.
removeChild(possibleExistingPostCodeField);
}
//Now, react to the new value for country
var country = document.getElementById("country");
if ( country.value!="“ )
{
if ( country.value!="ie“ )
{
//Create new fieldset element
newField = document.createElement("fieldset");
newField.setAttribute("id","postCodeField");
//Insert the new fieldset just before the button
var button =
form.getElementsByTagName("button")[0];
form.insertBefore(newField,button);
//Now add a legend to the new fieldset
newLegend = document.createElement("legend");
newField.appendChild(newLegend);
newLegendText =
document.createTextNode(
"What is your post code?");
newLegend.appendChild(newLegendText);
//Now insert an input box into the new fieldset
newInput = document.createElement("input");
newField.appendChild(newInput);
newInput.setAttribute("type","text");
newInput.setAttribute("name","postCode");
}
}
}
• Then …
– It creates a new legend
element
– It attaches this legend
element as a child to the
fieldset element
– It creates a new text node
containing the string “What
is your post code?”
– It attaches this text node as
a child of the legend
element
– Then… (see next slide)
handleCountryChange (contd.)
function handleCountryChange(someEvent)
{ var form = document.
getElementsByTagName("form")[0];
//Maybe the user has just changed his mind about the country.
//If so, remove any postCodeField element that had been created
//when he made his previous choice.
possibleExistingPostCodeField=
document.getElementById("postCodeField");
if (possibleExistingPostCodeField)
{form.
removeChild(possibleExistingPostCodeField);
}
//Now, react to the new value for country
var country = document.getElementById("country");
if ( country.value!="“ )
{if ( country.value!="ie“ )
{
//Create new fieldset element
newField = document.createElement("fieldset");
newField.setAttribute("id","postCodeField");
//Insert the new fieldset just before the button
var button =
form.getElementsByTagName("button")[0];
form.insertBefore(newField,button);
//Now add a legend to the new fieldset
newLegend = document.createElement("legend");
newField.appendChild(newLegend);
newLegendText =
document.createTextNode(
"What is your post code?");
newLegend.appendChild(newLegendText);
//Now insert an input box into the new
fieldset
newInput =
document.createElement("input");
newField.appendChild(newInput);
newInput.setAttribute("type","text");
newInput.setAttribute("name","postCode");
}
}
}
• Then …
– It creates a new input
element
– It attaches this input
element as a child to the
fieldset element
– It specifies that the new
input element is of type text
– It gives the name postCode
to the new input element
• And, finally, that’s it