Tải bản đầy đủ (.pdf) (58 trang)

ASP.NET AJAX Programmer’s Reference - Chapter 6 pps

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (430.46 KB, 58 trang )

DOM Extensions
Document Object Model (DOM) programming is one of the most common client-side programming
tasks in the world of Web development. The ASP.NET AJAX DOM extensions extend traditional
DOM programming to add support for .NET-like methods and properties. This chapter provides
in-depth coverage of these extensions. As you’ll see in subsequent chapters, this convenient set of
classes and enumerations are used extensively in the ASP.NET AJAX client-side framework.
DomElement
As Listing 6-1 shows, the ASP.NET AJAX DOM extensions define a new JavaScript class named

DomElement . As you’ll see in the following sections, this class exposes static methods and proper-
ties that introduce .NET-like programming convenience into your client-side DOM scripting.
Because all these methods and properties are static, you must call them directly on the
DomElement
class itself. Note that the
DomElement class belongs to the Sys.UI namespace. Also note that you
should not directly instantiate an instance of this class because all members of the class are static.
Listing 6-1: The DomElement Class
Sys.UI.DomElement = function Sys$UI$DomElement() { }
Sys.UI.DomElement.registerClass(‘Sys.UI.DomElement’);
get ElementById
This static method of the DomElement class takes up to two parameters. The first parameter
contains the value of the
id HTML attribute of a DOM element. The second parameter, which is
optional, references the parent DOM element of the DOM element whose
id HTML attribute’s
value is given by the first parameter. The main responsibility of the
getElementById method is
to return a reference to the JavaScript object that represents the DOM element whose
id HTML
attribute is given by the first parameter.
To see how the


getElementById method returns this reference, let’s take a look at the internal
implementation of this method as shown in Listing 6-2 .
c06.indd 161c06.indd 161 8/20/07 7:58:09 PM8/20/07 7:58:09 PM
Chapter 6: DOM Extensions
162
Listing 6-2: The Internal Implementation of the get ElementById Method of the
DomElement Class
var $get = Sys.UI.DomElement.getElementById = function(f, e)
{
if(!e)
return document.getElementById(f);

if(e.getElementById)
return e.getElementById(f);

var c = [], d = e.childNodes;
for(var b = 0; b < d.length; b ++ )
{
var a = d[b];
if(a.nodeType == 1)
c[c.length] = a;
}
while(c.length)
{
a = c.shift();
if(a.id == f)
return a;
d = a.childNodes;
for(b = 0; b < d.length; b ++ )
{

a = d[b];
if(a.nodeType == 1)
c[c.length] = a;
}
}
return null;
}
The getElementById method first checks whether its second parameter has been specified. If not, it
simply delegates to the
getElementById method of the current document JavaScript object. In other
words, by default, the
getElementById method uses the current document object as the parent of the
DOM element with the
id HTML attribute given by the first parameter:
if(!e)
return document.getElementById(f);
If the second argument of the method has indeed been specified, the method checks whether the parent
DOM element that the second argument references supports a method named
getElementById . If so, it
simply delegates to the
getElementById method of the parent element. For example, if your page uses
a frameset consisting of two frames, and you want to access a child element of one of these frames from
the other frame, you can pass the
document DOM object of the other frame as the second argument
of the
getElementById method:
if(e.getElementById)
return e.getElementById(f);
c06.indd 162c06.indd 162 8/20/07 7:58:09 PM8/20/07 7:58:09 PM
Chapter 6: DOM Extensions

163
This tells the getElementById method to call the getElementById method of the document element of
the other frame as opposed to the
document element of the current frame. You’ll see an example of this
scenario shortly.
If the second argument of the
getElementById method of the DomElement class has indeed been
specified but it does not support the
getElementById method, the getElementById method of the

DomElement class simply searches through the descendants of the parent element for the element with
the specified
id attribute value:
var c = [], d = e.childNodes;
for(var b = 0; b < d.length; b ++ )
{
var a = d[b];
if(a.nodeType == 1)
c[c.length] = a
}
while(c.length)
{
a = c.shift();
if(a.id == f)
return a;
d = a.childNodes;
for(b = 0; b < d.length; b ++ )
{
a = d[b];
if(a.nodeType == 1)

c[c.length] = a
}
}
return null
This is great for situations where you want to limit the search to the descendant of a particular DOM
element. You’ll see an example of this scenario shortly.
As the internal implementation of the
getElementById method of the DomElement class shows, this
method handles the following three scenarios:
❑ The default scenario where the search for the DOM element with the specified id HTML attri-
bute is limited to the descendant DOM elements of the current document object
❑ The scenario where the search for the DOM element with the specified id HTML attribute is
limited to the descendant DOM elements of the specified
document object, which may or may
not be the current
document object
❑ The scenario where the search for the DOM element with the specified id HTML attribute is
limited to the descendant DOM elements of the specified DOM element
The following code presents an example of the first scenario. As the boldfaced portion of this code
shows, the
getElementById method of the DomElement class is called without specifying the second
argument. This instructs the
getElementById method to search through the descendant DOM elements
of the current document.
c06.indd 163c06.indd 163 8/20/07 7:58:10 PM8/20/07 7:58:10 PM
Chapter 6: DOM Extensions
164
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ /><html xmlns=” /><head runat=”server”>

<title>Untitled Page</title>
<script language=”javascript” type=”text/javascript”>
function frame1ClickCallback()
{
var frame1TextBox = Sys.UI.DomElement.getElementById(“frame1TextBox”);
alert(frame1TextBox.value);
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />
<input type=”text” id=”frame1TextBox” />&nbsp;
<input type=”button” onclick=”frame1ClickCallback()”
value=”Send” />
</form>
</body>
</html>
Now, let’s take look at the example of the second scenario shown in the following code. The boldfaced
portion of this code passes the
document.form1 element as the second argument of the getElementById
method. As you can see,
document.form1 is the parent of the frame1TextBox element. This limits the
search to the child elements of the
document.form1 element.
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
<script language=”javascript” type=”text/javascript”>

function frame1ClickCallback()
{
var frame1TextBox = Sys.UI.DomElement.getElementById(“frame1TextBox”,
document.form1);
alert(frame1TextBox.value);
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />
<input type=”text” id=”frame1TextBox” />&nbsp;
<input type=”button” onclick=”frame1ClickCallback()”
value=”Send” />
</form>
</body>
</html>
c06.indd 164c06.indd 164 8/20/07 7:58:10 PM8/20/07 7:58:10 PM
Chapter 6: DOM Extensions
165
Now, let’s take a look at an example of the third scenario. This example consists of three ASP.NET pages.
The first page uses a frameset as shown in Listing 6-3 . The frameset consists of two frames named

frame1 and frame2 that respectively display the contents of the frame1.aspx and frame2.aspx pages.
Listing 6-3: The page that uses the frameset
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
</head>

<frameset cols=”60%,40%”>
<frame src=”frame1.aspx” name=”frame1”/>
<frame src=”frame2.aspx” name=”frame2”/>
</frameset>
</html>
Listing 6-4 presents the frame2.aspx page. As you can see, this page is very simple. It consists of a
single text box element.
Listing 6-4: The frame2.aspx Page
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
</head>
<body>
<form id=”form1” runat=”server”>
<input type=”text” id=”frame2TextBox” />
</form>
</body>
</html>
Listing 6-5 presents the frame1.aspx page.
Listing 6-5: The frame1.aspx Page
<%@ Page Language=”C#” %>
<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
<script language=”javascript” type=”text/javascript”>
(continued)
c06.indd 165c06.indd 165 8/20/07 7:58:10 PM8/20/07 7:58:10 PM
Chapter 6: DOM Extensions
166

Listing 6-5 (continued)
function frame1ClickCallback()
{
var frame1TextBox = Sys.UI.DomElement.getElementById(“frame1TextBox”);
var frame2TextBox = Sys.UI.DomElement.getElementById(“frame2TextBox”,
parent.frame2.document);
frame2TextBox.value = frame1TextBox.value;
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />
<input type=”text” id=”frame1TextBox” />&nbsp;
<input type=”button” onclick=”frame1ClickCallback()”
value=”Send” />
</form>
</body>
</html>
This page consists of a text box and a button. When you enter a value into the text box and click the
button, the
frame1ClickCallback JavaScript function is called. As the boldfaced portion of Listing 6-5
shows, this JavaScript function takes the following actions:
1. It calls the getElementById method of the DomElement class to return a reference to the text
box displayed in the
frame1.aspx — that is, the current document.
var frame1TextBox = Sys.UI.DomElement.getElementById(“frame1TextBox”);
2. It calls the getElementById method of the DomElement class to return a reference to the text
box


displayed in the other frame — that is, frame2.aspx . Note that the frame1ClickCallback
method passes the document object of the other frame as the second argument to the

getElementById method to instruct this method to search through the child DOM elements
of the other frame for the specified text box.
var frame2TextBox = Sys.UI.DomElement.getElementById(“frame2TextBox”,
parent.frame2.document);
3. It assigns the value of the text box of frame1.aspx to the text box of frame2.aspx .
frame2TextBox.value = frame1TextBox.value;
add CssClass
The addCssClass static method of the DomElement class adds a new CSS class name to the specified
DOM element, if it hasn’t been already added. Listing 6-6 presents the internal implementation of this
method. Note that this method first calls the
containsCssClass static method of the DomElement class
to check whether the DOM object already contains the specified CSS class name. If not, it simply appends
the new CSS class name to the
className property of the DOM object.
c06.indd 166c06.indd 166 8/20/07 7:58:11 PM8/20/07 7:58:11 PM
Chapter 6: DOM Extensions
167
Listing 6-6: The Internal Implementation of the add CssClass Method
Sys.UI.DomElement.addCssClass = function(a, b)
{
if(!Sys.UI.DomElement.containsCssClass(a, b))
{
if(a.className === “”)
a.className = b;
else
a.className += “ “ + b;
}

}
contains CssClass
The containsCssClass static method of the DomElement class returns a Boolean value that specifies
whether a specified DOM object contains the specified CSS class name. Listing 6-7 presents the internal
implementation of this method. Note that this method simply delegates to the
contains static method
of the
Array class. The ASP.NET AJAX client-side script framework extends the Array class to add
support for the
contains static method, as discussed in chapter 2 .
Listing 6-7: The Internal Implementation of the contains CssClass Method
Sys.UI.DomElement.containsCssClass = function(b, a)
{
return Array.contains(b.className.split(“ “), a)
}
remove CssClass
The removeCssClass static method of the DomElement class removes a specified CSS class name from
the specified DOM object. Listing 6-8 contains the code for the internal implementation of this method.
As you can see, this method uses a simple string manipulation to remove the specified CSS class name.
Listing 6-8: The Internal Implementation of the remove CssClass Method
Sys.UI.DomElement.removeCssClass = function(d, c)
{
var a =” “ + d.className + “ “,
b = a.indexOf(“ “ + c + “ “);

if(b >= 0)
d.className =
(a.substring(0, b) + “ “ + a.substring(b + c.length + 1,
a.length)).trim();
}

Take a look at the example in Listing 6-9 , which uses the addCssClass and removeCssClass methods
of the
DomElement class.
c06.indd 167c06.indd 167 8/20/07 7:58:11 PM8/20/07 7:58:11 PM
Chapter 6: DOM Extensions
168
Listing 6-9: A page that uses the add CssClass and remove CssClass Methods
<%@ Page Language=”C#” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
<style type=”text/css”>
.CssClass1 {
background-color: Blue;
color: Yellow;
font-weight: bold;
}
.CssClass2 {
background-color: Yellow;
color: Blue;
font-weight: bold;
}
</style>
<script language=”javascript” type=”text/javascript”>
var myLinkDomElementObj;
var myList;

function addCallback()
{

var myCssClass = myList.options[myList.selectedIndex].value;
Sys.UI.DomElement.addCssClass(myLinkDomElementObj, myCssClass);
}

function removeCallback()
{
var myCssClass = myList.options[myList.selectedIndex].value;
Sys.UI.DomElement.removeCssClass(myLinkDomElementObj, myCssClass);
}

function pageLoad()
{
myLinkDomElementObj = Sys.UI.DomElement.getElementById(“myLink”);
myList = document.getElementById(“myList”);
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />
<a href=”” id=”myLink”>
Wrox Web Site</a>&nbsp;&nbsp;
<select id=”myList”>
<option value=”CssClass1”>CSS Class 1</option>
<option value=”CssClass2”>CSS Class 2</option>
</select>&nbsp;&nbsp;
c06.indd 168c06.indd 168 8/20/07 7:58:11 PM8/20/07 7:58:11 PM
Chapter 6: DOM Extensions
169
<input type=”button” value=”Add” onclick=”addCallback()” />&nbsp;

<input type=”button” value=”Remove” onclick=”removeCallback()” />
</form>
</body>
</html>
Figure 6-1 shows what you’ll see when you access this page. Run the program, select a CSS class name
from the list, and click the Add button. You should see the effects of the selected CSS class. Now click the
Remove button. The link should go back to its default format.
Figure 6-1
toggle CssClass
The toggleCssClass static method of the DomElement class toggles a specified CSS class name on or
off on a specified DOM object. The best way to understand what this method does is to use it in an
example. Listing 6-10 presents a page that uses this method.
Listing 6-10: A page that uses the toggle CssClass Method
<%@ Page Language=”C#” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
<style type=”text/css”>
.CssClass1 {
background-color: Blue;
color: Yellow;
font-size: 40px;
}
</style>
<script language=”javascript” type=”text/javascript”>
function toggleCssClass(myLink)
{
Sys.UI.DomElement.toggleCssClass(myLink, “CssClass1”);
}

</script>
(continued)
c06.indd 169c06.indd 169 8/20/07 7:58:12 PM8/20/07 7:58:12 PM
Chapter 6: DOM Extensions
170
Listing 6-10 (continued)
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1” />
<a href=””
onmouseover=”toggleCssClass(this)”
onmouseout=”toggleCssClass(this)”>Wrox Web Site</a>
</form>
</body>
</html>
If you run this code, you’ll see the result shown in Figure 6-2 , which is a very simple page that contains a
single hyperlink. Now if you move the mouse over the link, you’ll get the result shown in Figure 6-3 . If
you move the mouse away from the link, you’ll get the result shown in Figure 6-2 again. Therefore,
moving the mouse over and out of the link switches the style of the class between what you see in the
two figures.
Figure 6-2
Figure 6-3
Listing 6-11 shows the internal implementation of the
toggleCssClass method. This method first calls
the
containsCssClass method to check whether the specified DOM object already contains the speci-
fied CSS class name. If so, it calls the
removeCssClass method to remove the CSS class name. If not, it
calls the

addCssClass method to add the CSS class name.
c06.indd 170c06.indd 170 8/20/07 7:58:12 PM8/20/07 7:58:12 PM
Chapter 6: DOM Extensions
171
Listing 6-11: The Internal Implementation of the toggle CssClass Method
Sys.UI.DomElement.toggleCssClass = function(b, a)
{
if(Sys.UI.DomElement.containsCssClass(b, a))
Sys.UI.DomElement.removeCssClass(b, a);
else
Sys.UI.DomElement.addCssClass(b, a);
}
get Location
Listing 6-12 presents the simplified version of the internal implementation of the DomElement class’s

getLocation static method.
Listing 6-12: The Simplified Version of the Internal Implementation of the get Location
Method
Sys.UI.DomElement.getLocation = function(d)
{
var b = 0, c = 0, a;
for(a = d; a; a = a.offsetParent)
{
if(a.offsetLeft)
b += a.offsetLeft;

if(a.offsetTop)
c += a.offsetTop
}
return { x : b, y : c }

}
This method returns a JavaScript object literal that contains the x and y coordinates of the specified DOM
element with respect to the top-left corner of the browser window. Note that the internal implementation
of the
getLocation method uses the following three important properties of DOM elements:
❑ offsetParent : Returns a reference to the first positioned DOM element in the containment
hierarchy of the current DOM element.
❑ offsetLeft : Returns the number of pixels that the current DOM element is offset to the left
within its
offsetParent DOM element.
❑ offsetTop : Returns the number of pixels that the current DOM element is offset from the top
within its
offsetParent DOM element.
As Listing 6-12 shows, the
getLocation method iterates through the DOM elements in the containment
hierarchy of the specified DOM element and accumulates the values of the
offsetLeft and offsetTop
properties of these enumerated DOM elements. Therefore, the two accumulated values at the end specify
the number of pixels that the specified DOM element is offset to the left and to the top within the
browser window.
c06.indd 171c06.indd 171 8/20/07 7:58:12 PM8/20/07 7:58:12 PM
Chapter 6: DOM Extensions
172
Listing 6-13 shows an example that uses the getLocation method.
Listing 6-13: A page that uses the get Location Method
<%@ Page Language=”C#” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ /><html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>

<script language=”javascript” type=”text/javascript”>
function clickCallback(myspan)
{
var obj = Sys.UI.DomElement.getLocation(myspan);
alert(“x=” + obj.x + “\n” + “y=” + obj.y);
}
</script>
</head>
<body>
<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” />
<span id=”myspan” onclick=”clickCallback(this)”>Click here!</span>
</form>
</body>
</html>
If you run this program and click the Click here! link, you should get a pop-up message the displays the
x and y coordinates of the label.
set Location
The setLocation static method of the DomElement class sets the x and y coordinates of a specified
DOM element to specified values. As such, it takes the following three arguments:
❑ b : References the DOM element whose x and y coordinates are being set.
❑ c : Specifies the new value in pixels of the x coordinate.
❑ d : Specifies the new value in pixels of the y coordinate.
As Listing 6-14 shows, the
setLocation method also sets the position style property to absolute . In
other words, this method absolutely positions the specified DOM element.
Listing 6-14: The Internal Implementation of the set Location Method
Sys.UI.DomElement.setLocation = function(b, c, d)
{
var a = b.style;

a.position=”absolute”;
a.left = c + “px”;
a.top = d + “px”;
}
c06.indd 172c06.indd 172 8/20/07 7:58:13 PM8/20/07 7:58:13 PM
Chapter 6: DOM Extensions
173
Listing 6-15 shows an example of how the getLocation and setLocation methods are used.
Listing 6-15: An ASP.NET page that uses the get Location and set Location Methods
<%@ Page Language=”C#” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ />
<html xmlns=” /><head id=”Head1” runat=”server”>
<title>Untitled Page</title>
<script language=”javascript” type=”text/javascript”>
function mousedowncb(event)
{
event = event || window.event;
document.oldClientX = event.clientX;
document.oldClientY = event.clientY;
document.onmousemove = mousemovecb;
document.onmouseup = mouseupcb;
return false;
}

function mouseupcb(event)
{
event = event || window.event;
document.onmousemove = null;

document.onmouseup = null;
return false;
}

function mousemovecb(event)
{
event = event || window.event;
var deltaClientX = event.clientX - document.oldClientX;
var deltaClientY = event.clientY - document.oldClientY;

var sender = $get(“mydiv”);
var senderLocation = Sys.UI.DomElement.getLocation(sender);
Sys.UI.DomElement.setLocation(sender, senderLocation.x+deltaClientX,
senderLocation.y+deltaClientY);

document.oldClientX = event.clientX;
document.oldClientY = event.clientY;

return false;
}

</script>
</head>
(continued)
c06.indd 173c06.indd 173 8/20/07 7:58:13 PM8/20/07 7:58:13 PM
Chapter 6: DOM Extensions
174
Listing 6-15 (continued)
<body>
<div id=”mydiv” style=”position: absolute; left: 0px; top: 0px”

onmousedown=”mousedowncb(event)”>
<a href=”javascript:void(0)” id=”myspan”
style=”font-weight: bold”>Wrox Web Site</a>
</div>
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server” />
</form>
</body>
</html>
This page simply renders the “Wrox Web Site” text and allows you to move this text by clicking the text
and holding the mouse button down while moving the mouse around. Note that this page registers the

mousedowncb method as an event handler for the mousedown event of the div HTML element with the

id HTML attribute value of mydiv as shown in the following code:
function mousedowncb(event)
{
event = event || window.event;
document.oldClientX = event.clientX;
document.oldClientY = event.clientY;
document.onmousemove = mousemovecb;
document.onmouseup = mouseupcb;
return false;
}
This method takes two steps. First, it accesses and stores the mouse position’s x and y coordinates from
the event object’s
clientX and clientY properties. Next, it registers the mousemovecb and mouseupcb
methods as callbacks for the document object’s
mousemove and mouseup events.
As Listing 6-15 shows, the

mousemovecb method first accesses the current x and y coordinates of the
mouse position from the
clientX and clientY properties of the event object and the old x and y coordi-
nates of the mouse. Next, it evaluates the number of pixels the mouse has moved:
var deltaClientX = event.clientX - document.oldClientX;
var deltaClientY = event.clientY - document.oldClientY;
The method then uses $get syntax to access a reference to the mydiv DOM element:
var sender = $get(“mydiv”);
Next, it calls the getLocation method, passing in the above reference to return the JavaScript object
literal that contains the current x and y coordinates of the
mydiv DOM element:
var senderLocation = Sys.UI.DomElement.getLocation(sender);
c06.indd 174c06.indd 174 8/20/07 7:58:13 PM8/20/07 7:58:13 PM
Chapter 6: DOM Extensions
175
Then, it calls the setLocation method to set the mydiv DOM element’s x and y coordinates to new
values. These new values basically increment the current values by the number of pixels that the mouse
has moved:
Sys.UI.DomElement.setLocation(sender, senderLocation.x+deltaClientX,
senderLocation.y+deltaClientY);
get Bounds
Because the getBounds method returns an object of type Bounds , first we need to study Bounds .
Listing 6-16 presents the internal implementation of the
Bounds type. As this code listing shows, Bounds
is a class with four properties:
x , y , height , and width . These properties contain the x and y coordinates
and the height and width of a specified DOM element.
Listing 6-16: The Bounds Type
Sys.UI.Bounds = function Sys$UI$Bounds(x, y, width, height) {
this.x = x;

this.y = y;
this.height = height;
this.width = width;
}
Sys.UI.Bounds.registerClass(‘Sys.UI.Bounds’);
As you can see, there is no sign of the DOM element in the definition of the Bounds type. This is where
the
getBounds method comes into play. As Listing 6-17 shows, this method returns a Bounds object that
contains the x and y coordinates and the width and height of the specified DOM element.
Listing 6-17: The Internal Implementation of the get Bounds Method
Sys.UI.DomElement.getBounds = function Sys$UI$DomElement$getBounds(element) {
var offset = Sys.UI.DomElement.getLocation(element);

return new Sys.UI.Bounds(offset.x, offset.y,
element.offsetWidth || 0,
element.offsetHeight || 0);
}
The ASP.NET page shown in Listing 6-18 uses the getBounds method to access the width of the span
DOM element called
myspan .
c06.indd 175c06.indd 175 8/20/07 7:58:14 PM8/20/07 7:58:14 PM
Chapter 6: DOM Extensions
176
Listing 6-18: An ASP.NET page that uses the get Bounds Method
<%@ Page Language=”C#” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ />
<html xmlns=” /> <head id=”Head1” runat=”server”>
<title>Untitled Page</title>

<script language=”javascript” type=”text/javascript”>
function pageLoad()
{
var bounds = Sys.UI.DomElement.getBounds($get(“myspan”));
alert(bounds.width);
}
</script>
</head>
<body>
<span id=”myspan” style=”font-weight:bold;”>Wrox Web Site</span>
<form id=”form1” runat=”server”>
<asp:ScriptManager ID=”ScriptManager1” runat=”server” />
</form>
</body>
</html>
MouseButton
One of the most common event sources is the mouse. The ASP.NET AJAX DOM extensions define an
enumeration named
MouseButton whose values represent different buttons of the mouse, as shown
in Listing 6-19 . As you can see, this enumeration has three enumeration values:
leftButton ,

middleButton , and rightButton .
Listing 6-19: The MouseButton Enumeration
Sys.UI.MouseButton = function Sys$UI$MouseButton() {}

Sys.UI.MouseButton.prototype = {
leftButton: 0,
middleButton: 1,
rightButton: 2

}
Sys.UI.MouseButton.registerEnum(“Sys.UI.MouseButton”);
Key
Another very common source of events is the keyboard. The ASP.NET AJAX DOM extensions define an
enumeration named
Key that features one enumeration value for each key, as shown in Listing 6-20 .
c06.indd 176c06.indd 176 8/20/07 7:58:14 PM8/20/07 7:58:14 PM
Chapter 6: DOM Extensions
177
Listing 6-20: The Key Enumeration
Sys.UI.Key = function Sys$UI$Key() { }

Sys.UI.Key.prototype = {
backspace: 8,
tab: 9,
enter: 13,
esc: 27,
space: 32,
pageUp: 33,
pageDown: 34,
end: 35,
home: 36,
left: 37,
up: 38,
right: 39,
down: 40,
del: 127
}
Sys.UI.Key.registerEnum(“Sys.UI.Key”);
Delegates

A method of a .NET class is characterized by the following:
❑ The name of the method
❑ The class to which the method belongs
❑ The number of its arguments
❑ The order of its arguments
❑ The types of its arguments
❑ The type of the value the method returns
❑ The body of the method — that is, its implementation
For the most part, the callers of a method are only interested in knowing what they need to pass into the
method and what the method returns. In other words, they’re only interested in the method’s argument
count, order, and types, and type of the value it returns. They don’t care what the name of the method is,
which class owns the method, or how the method is implemented (the body of the method).
As far as the callers are concerned, methods of different names and implementations belonging to differ-
ent classes are the same as long as they all have the same argument count, order, and types, and return
the same type. You can think of the argument count, order, and types and the return type of a method
as the type of the method.
Each method has the following two characteristics:
❑ Its type, which consists of its argument count, order, and types and return type
❑ Its method-specific aspects, which consists of its name, class, and body
c06.indd 177c06.indd 177 8/20/07 7:58:14 PM8/20/07 7:58:14 PM
Chapter 6: DOM Extensions
178
When the callers of a method call the method directly, they unnecessarily get coupled to its method-
specific aspects — that is, its name, class, and body. This will not allow these callers to invoke other
methods of the same type with different names and implementations belonging to different classes.
Therefore, you need a mechanism that will allow the caller of a method to indirectly call the method
without using its method-specific aspects (its name, class, and body). This will ensure that the caller of a
method is coupled only to its type, not its method-specific aspects.
The .NET Framework offers two approaches to decouple the callers of a method from its method-specific
aspects. The first approach requires the classes owning the methods to implement an interface that

exposes a method with the same argument count, order, and types and return value type. In other
words, the interface hides the method-specific aspects of a method — its class and body.
The second approach requires you to define a delegate with the same argument count, order, and types
and return value type. A delegate is an object that encapsulates and hides the name, class, and body of
the method that it represents. In other words, a delegate is just like an interface, but it exposes the
method’s argument count, order, and types and return-value type.
You may be wondering which approach is better because it seems that they both do the same thing —
they both hide the method-specific aspects of the method. The answer is, “It depends.” Because a dele-
gate represents a single type of method, it provides more granularity than an interface, which could
contain more than one type of method. As such, if you just want to hide the method-specific aspects of a
single method, you’re better off using a delegate, which only targets a single type of method.
There are two ways to define a .NET delegate. The most common approach is to use the
delegate key-
word to declare the delegate without actually implementing it. The
delegate keyword instructs the
compiler to generate the necessary code for the declared delegate at compile time. This saves you from
having to implement the delegate yourself. Another approach to defining a .NET delegate is to use the

CreateDelegate static method of the Delegate class. This method allows you to create a delegate to
represent a specified method of a specified .NET class.
The ASP.NET AJAX client-side framework extends the functionality of the JavaScript
Function type to add
support for a new static method named
createDelegate that emulates the CreateDelegate method of
the .NET
Delegate class. It allows you to create a delegate to represent a specified method of a specified
JavaScript object. Listing 6-21 presents the internal implementation of the
createDelegate method. Because
the
createDelegate method is a static method, you must call it directly on the Function class itself.

Listing 6-21: The create Delegate Method of the JavaScript Function Type
Function.createDelegate =
function Function$createDelegate(instance, method) {
return function() {
return method.apply(instance, arguments);
}
}
The createDelegate method takes two parameters. The first parameter references the JavaScript object
owning the method that the delegate represents. The second parameter references the
Function object
that represents the method the delegate represents. As you can see, the
createDelegate method
defines and returns a new JavaScript function that calls the
apply method on the Function object,
passing in the reference to the JavaScript object and the array that contains the values of the parameters
of the method that the
Function object represents.
c06.indd 178c06.indd 178 8/20/07 7:58:14 PM8/20/07 7:58:14 PM
Chapter 6: DOM Extensions
179
Strictly speaking, since the createDelegate method internally used the apply method, the JavaScript
function passed into the createDelegate method as its second argument doesn’t need to be a method of the
JavaScript object passed into the createDelegate method as its first argument. When the apply method is
invoked on the JavaScript function passed in the createDelegate method as its second argument, the
JavaScript keyword within the scope of the body of the JavaScript function is automatically set to refer-
ence the JavaScript object passed into the createDelegate method as its first argument. This allows the
JavaScript function to use the JavaScript keyword within the body of the function to access the JavaScript
object passed into the createDelegate method as its first argument. The same argument applies to all cases
in this book where the apply or call methods are used internally to implement those cases.
Listing 6-22 shows an example that uses the

createDelegate method. This example defines a new ASP
.NET AJAX client class named
Mover that belongs to a namespace named Delegates . This class encap-
sulates the logic that allows the end user to move a specified object (such as text or an image) around.
Each type of movable object comes with its own provider. A provider is an ASP.NET AJAX client class
that exposes a method that populates a specified container HTML element with the movable content. For
example, as you’ll see shortly, the
TextProvider client class is the provider associated with a text. This
client class exposes a method named
addText that populates the specified container HTML element
with the specified text.
Listing 6-22: An example that uses the create Delegate method
<%@ Page Language=”C#” %>

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ />
<html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
<script type=”text/javascript” language=”javascript”>
function pageLoad()
{
var mover = new Delegates.Mover();
var textProvider = new Delegates.TextProvider(“Wrox Web Site”);
var addTextDelegate = Function.createDelegate(textProvider,
textProvider.addText);
mover.invokeAddContentDelegate (addTextDelegate);
}
</script>
</head>
<body>


<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1”>
<Scripts>
<asp:ScriptReference Path=”Delegate.js” />
</Scripts>
</asp:ScriptManager>
</form>
</body>
</html>
c06.indd 179c06.indd 179 8/20/07 7:58:15 PM8/20/07 7:58:15 PM
Chapter 6: DOM Extensions
180
As you can see in this listing, the pageLoad method takes the following actions:
❑ It instantiates the Mover object:
var mover = new Delegates.Mover();
❑ It instantiates the TextProvider object, passing in the movable text:
var textProvider = new Delegates.TextProvider(“Wrox Web Site”);
❑ It calls the createDelegate method on the Function class to instantiate a delegate that repre-
sents the
addText method of the TextProvider object. The addText method is responsible for
providing the text that the end user can move.
var addTextDelegate = Function.createDelegate(textProvider, textProvider.addText);
❑ It calls the invokeAddContentDelegate method on the Mover object, passing in the delegate.
This method invokes the delegate to add the text that the end user can move around.
mover.invokeAddContentDelegate (addTextDelegate);
The delegate isolates the Mover from what the Mover is moving — that is, the movable content. Mover
has no idea that it is moving text. The sole responsibility of the
Mover is to enable the end user to move
the displayed content. The

Mover is not responsible for displaying and determining the movable content,
whether it’s text, an image, or something else. This responsibility is delegated to another object. In the
example in Listing 6-22 , this object is the
TextProvider object. Listing 6-22 wraps the addText method
of this
TextProvider object in a delegate and passes the delegate into the invokeAddContentDelegate
method of the
Mover object. As you’ll see shortly, the invokeAddContentDelegate method invokes
the delegate, which in turn invokes the
addText method of the TextProvider object. In other words, the
invocation of the
addText method of the TextProvider object has been assigned to the delegate.
Thanks to the delegate, the
Mover can indirectly invoke the addText method of the TextProvider
object without knowing the method-specific characteristics of the method. In addition, the
Mover can
execute any method of any class as long as the method takes a single argument and returns no value.
This means that you can replace the
TextProvider with another class to provide different type of
movable content. For example, Listing 6-23 uses an instance of a class named
ImageProvider to provide
an image as the movable content. Notice that in this case the
Mover executes a method with a different
name (
addImage instead of addText ) and a different implementation that belongs to a different class
(
ImageProvider instead of TextProvider ).
Listing 6-23: A page that uses different movable content
<%@ Page Language=”C#” %>


<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN”
“ />
<html xmlns=” /><head runat=”server”>
<title>Untitled Page</title>
<script type=”text/javascript” language=”javascript”>
c06.indd 180c06.indd 180 8/20/07 7:58:15 PM8/20/07 7:58:15 PM
Chapter 6: DOM Extensions
181
function pageLoad()
{
var mover = new Delegates.Mover();
var imageProvider = new Delegates.ImageProvider(“images.jpg”);
var addImageDelegate = Function.createDelegate(imageProvider,
imageProvider.addImage);
mover. invokeAddContentDelegate(addImageDelegate);
}
</script>
</head>
<body>

<form id=”form1” runat=”server”>
<asp:ScriptManager runat=”server” ID=”ScriptManager1”>
<scripts>
<asp:ScriptReference Path=”Delegate.js” />
</scripts>
</asp:ScriptManager>
</form>
</body>
</html>
Notice that Listings 6-22 and 6-23 use a <asp:ScriptReference> element to register the Delegate.js

JavaScript file. This file contains the entire application logic.
<asp:ScriptReference Path=”Delegate.js” />
T h e ScriptReference class is discussed later in this book. For now suffice it to say that the

ScriptManager server control exposes a collection property named Scripts that contains zero or
more instances of a class named
ScriptReference , where each instance registers a particular
JavaScript file. Notice that the
ScriptReference class exposes a property named Path . You must set
this to the path of the JavaScript file being registered.
Listing 6-24 presents the content of the
Delegate.js JavaScript file. As you can see, this file contains the
implementation of the
Mover , TextProvider , and ImageProvider ASP.NET AJAX client classes.
Listing 6-24: The Delegate.js JavaScript File
Type.registerNamespace(“Delegates”);

function Delegates$Mover$invokeAddContentDelegate(addContentDelegate)
{
addContentDelegate(“container1”);
}

function mousedowncb(event)
{
event = event || window.event;
document.oldClientX = event.clientX;
document.oldClientY = event.clientY;
(continued)
c06.indd 181c06.indd 181 8/20/07 7:58:15 PM8/20/07 7:58:15 PM
Chapter 6: DOM Extensions

182
Listing 6-24 (continued)
document.onmousemove = mousemovecb;
document.onmouseup = mouseupcb;
return false;
}

function mouseupcb(event)
{
event = event || window.event;
document.onmousemove = null;
document.onmouseup = null;
return false;
}

function mousemovecb(event)
{
event = event || window.event;
var deltaClientX = event.clientX - document.oldClientX;
var deltaClientY = event.clientY - document.oldClientY;

var container = document.getElementById(“container1”);

var containerLocation = Sys.UI.DomElement.getLocation(container);
Sys.UI.DomElement.setLocation(container,
containerLocation.x + deltaClientX,
containerLocation.y + deltaClientY);

document.oldClientX = event.clientX;
document.oldClientY = event.clientY;


return false;
}

function Delegates$TextProvider$addText(containerId)
{
var container = document.getElementById(containerId);
container.innerHTML =
‘<a href=”javascript:void(0)” id=”myspan”’ +
‘ style=”font-weight: bold”>’ + this.text + ‘</a>’;
}

function Delegates$ImageProvider$addImage(containerId)
{
var container = document.getElementById(containerId);
container.innerHTML = ”<img src=’” + this.imagePath + “’ alt=’img’ />”;
}

Delegates.TextProvider = function (text) {
this.text = text;
}

c06.indd 182c06.indd 182 8/20/07 7:58:15 PM8/20/07 7:58:15 PM
Chapter 6: DOM Extensions
183
Delegates.TextProvider.prototype = {
addText : Delegates$TextProvider$addText
}

Delegates.TextProvider.registerClass(“Delegates.TextProvider”);


Delegates.ImageProvider = function (imagePath) {
this.imagePath = imagePath;
}

Delegates.ImageProvider.prototype = {
addImage : Delegates$ImageProvider$addImage
}

Delegates.ImageProvider.registerClass(“Delegates.ImageProvider”);

Delegates.Mover = function () {
var container = document.getElementById(“container1”);
if (!container)
{
container = document.createElement(“div”);
container.id = ”container1”;
container.style.position = ”absolute”;
document.body.insertBefore(container, document.forms[0]);
container.onmousedown = mousedowncb;
}
}

Delegates.Mover.prototype = {
invokeAddContentDelegate : Delegates$Mover$invokeAddContentDelegate
}

Delegates.Mover.registerClass(“Delegates.Mover”);
The following sections walk you through this listing and describe the implementation of the Delegates
namespace and

Mover , TextProvider , and ImageProvider client classes.
Namespace
The Delegates.js file defines and registers a namespace named Delegates , which contains all the
classes defined for this application:
Type.registerNamespace(“Delegates”);
c06.indd 183c06.indd 183 8/20/07 7:58:16 PM8/20/07 7:58:16 PM
Chapter 6: DOM Extensions
184
Mover
The Delegates.js file defines and registers the Mover class. Note that the constructor of this class first
checks whether the
<body> HTML element of the current document contains a <div> HTML element
with an
id HTML attribute value of container1 . If not, it takes the following steps to create the element
and initialize its properties:
1. It calls the createElement method on the current document to create the container <div>
HTML element. This element will be used as a container for the movable content.
container = document.createElement(“div”);
2. It initializes the properties of the newly instantiated container <div> HTML element:
container.id = ”container1”;
container.style.position = ”absolute”;
3. It adds the container element before the <form> HTML element:
document.body.insertBefore(container, document.forms[0]);
4. It registers the mousedowncb global JavaScript function as the event handler for the mousedown
event of the container element. The implementation of this function is discussed later in this
chapter.
container.onmousedown = mousedowncb;
Note that the Mover is not responsible for specifying the content of the container <div> HTML element.
This responsibility is delegated to another class such as
TextProvider or ImageProvider . As you’ll see

in subsequent sections, the
TextProvider and ImageProvider classes populate the container <div>
HTML element with a text and an image.
The
Mover class exposes a method named invokeAddContentDelegate that takes a delegate as its
argument and invokes that delegate, passing in the value of the
id HTML attribute of the container

<div> HTML element, container1 :
function Delegates$Mover$invokeAddContentDelegate(addContentDelegate)
{
addContentDelegate(“container1”);
}
TextProvider
The Delegates.js file defines and registers the TextProvider class. The constructor of this class takes
some text and stores it in an internal field for future reference:
Delegates.TextProvider = function (text) {
this.text = text;
}
c06.indd 184c06.indd 184 8/20/07 7:58:16 PM8/20/07 7:58:16 PM
Chapter 6: DOM Extensions
185
Note that the TextProvider class exposes a method named addText that takes the value of the id
HTML attribute of the container
<div> HTML element as its argument:
function Delegates$TextProvider$addText(containerId)
{
var container = document.getElementById(containerId);
container.innerHTML = ’<a href=”javascript:void(0);” id=”myspan”’ +
‘style=”font-weight: bold”>’ + this.text + ‘</a>’;

}
The addText method first calls the getElementById method on the document object to access a
reference to the container
<div> HTML element:
var container = document.getElementById(containerId);
Next, it renders the specified text as a hyperlink within the opening and closing tags of the container

<div> HTML element:
container.innerHTML = ’<a href=”javascript:void(0);” id=”myspan”’ +
‘style=”font-weight:bold”>’ + this.text + ‘</a>’;
ImageProvider
The Delegates.js file defines the ImageProvider class. The constructor of this class takes a single
parameter, which contains the path to a specified image, and stores the image path in an internal field for
future reference:
Delegates.ImageProvider = function (imagePath) {
this.imagePath = imagePath;
}
Note that the ImageProvider class features a single method named addImage that takes the value of
the
id HTML attribute of the container <div> HTML element as its argument:
function Delegates$ImageProvider$addImage(containerId)
{
var container = document.getElementById(containerId);
container.innerHTML = ”<img src=’” + this.imagePath + “’ alt=’img’ />”;
}
This method first accesses the container <div> HTML element and then renders an <img> HTML ele-
ment with the specified
src HTML attribute value as the content of the container <div> HTML element.
DomEvent
DOM event programming is a complex task, mainly because different types of browsers use different

types of event models. As such, programmers spend most of their time adding custom code to make up
for the differences between these event models. The ASP.NET AJAX client-side framework comes with a
c06.indd 185c06.indd 185 8/20/07 7:58:16 PM8/20/07 7:58:16 PM

×