Jul 31, 2007

Sys.Component

Generally, the term component denotes an object that is reusable and can interact with other objects in the context of a framework. The term control, on the other hand, denotes an object that is sort of a specialized component. The main trait that differentiates components and controls is the user interface. Components are non-UI objects; controls are primarily UI-based objects. In the Microsoft AJAX library, the root component class is Sys.Component. The root class for controls is named Control and, guess what, lives in the Sys.UI namespace. In the Microsoft AJAX library, Sys.UI.Control derives from Sys.Component.

            

Sys.Application

The execution of each asp.net ajax page is controlled by an application object that is intstantiated in the body of the library. Whenever an asp.net ajax page is loaded in the browser, an instance of Sys._Application class is promptly created and assigned to the Sys.Application object: Sys.Application = new Sys._Application();

In additional, each asp.net ajax page is injected with the following script code:

            
            

This code is placed immediately after the closing tag of the of the page's form, and it commands the loading of any script files registered for loading with the page's script manager. Sys._Application class derives from Compnent and is the entry point point in the page hierachy to locate client-side components either bound to server controls or programmatically added to the application.

Member

Description

addComponent

Adds the specified Microsoft AJAX library component to the page hierarchy

beginCreateComponents

Starts adding new Microsoft AJAX library components to the page

endCreateComponents

Ends adding new Microsoft AJAX library components to the page

findComponent

Looks up the specified Microsoft AJAX library component in the page

getComponents

Gets the list of Microsoft AJAX library components found in the page

initialize

Ensures that all referenced script files are loaded

notifyScriptLoaded

Called by script files to notify the application object that the script has been successfully loaded

queueScriptReference

Queues a new script reference for loading

removeComponent

Removes the specified component from the page hierarchy

Vents in the page lifetime

Event

Description

Init

Occurs when the page is first initialized

Load

Occurs when the page is loaded

loadTimedOut

Occurs when the loading step takes too much time to complete

scriptLoadFailed

Occurs when one script fails to load for whatever reason

Unload

Occurs when the page is unloaded

Client Page Life-cycle Events

Create Custom ASP.NET AJAX Non-Visual Client Components

Components

  • Derive from the component base class
  • Typically have no UI representation, such as timer component that raise events at intervals but is not visible on the page.
  • Have no associated DOM elements
  • Encapsulate client code that is intended to be resuable across applications

Behavior

  • Derive from the behavior base class, wich extend the Component base class
  • Extend the behavior of DOM elements, such as a watermarking behavior of the DOM element that they are associated with.
  • Can create UI elements, although they do not typically modified the basic of DOM element that they are associated with.
  • If assigned an ID, can be accessed directly from the DOM element through a custom attribute( expando).
  • Do not require an association with another client object, such as a class derived from the Control or Behavior classes.
  • Can reference either a control or a non-control HTML element in their element property.

Controls

  • Derive from the Control base class, which extends the Component base class.
  • Represent a DOM element as a client object, typically changing the original DOM element's ordinary behavior to provide new functionality. For example, a menu control might read >li< items from a >ul< element as its source data, but not display a bulleted list.
  • Are accessed from the DOM element directly through the control expando

Please refer this artical, in summary you need to do the following thing

  • You need to create an component in js file
  • add reference to the js file
  • create the component instance in the application load event, the instance will be associated with other element in the page because the information is passed in the creation method.

            Type.registerNamespace("Demo");

            Demo.Timer = function() {
            Demo.Timer.initializeBase(this);
            this._interval = 1000;
            this._enabled = false;
            this._timer = null;}

            Demo.Timer.prototype = {

            // OK to declare value types in the prototype
            get_interval: function() {
            /// Interval in milliseconds
            return this._interval;    },

            set_interval: function(value) {
            if (this._interval !== value) {
            this._interval = value;
            this.raisePropertyChanged('interval');
            if (!this.get_isUpdating() && (this._timer !== null))
            {
            this._restartTimer();
            }        }    },

            get_enabled: function() {
            /// True if timer is enabled, false if disabled.
            return this._enabled;    },

            set_enabled: function(value) {
            if (value !== this.get_enabled()) {
            this._enabled = value;
            this.raisePropertyChanged('enabled');

            if (!this.get_isUpdating())
            {
            if (value) {
            this._startTimer();                }                else {
            this._stopTimer();                }            }        }
            },    // events    add_tick: function(handler) {
            /// Adds a event handler for the tick event.
            /// The handler to add to the event.
            this.get_events().addHandler("tick", handler);    },
            remove_tick: function(handler) {
            /// Removes a event handler for the tick event.
            /// The handler to remove from the event.
            this.get_events().removeHandler("tick", handler);    },
            dispose: function() {
            // call set_enabled so the property changed event fires, for potentially attached listeners.
            this.set_enabled(false);
            // make sure it stopped so we aren't called after disposal
            this._stopTimer();        // be sure to call base.dispose()
            Demo.Timer.callBaseMethod(this, 'dispose');    },
            updated: function() {        Demo.Timer.callBaseMethod(this, 'updated');
            // called after batch updates, this.beginUpdate(), this.endUpdate().
            if (this._enabled) {            this._restartTimer();        }    },
            _timerCallback: function() {
            var handler = this.get_events().getHandler("tick");
            if (handler) {            handler(this, Sys.EventArgs.Empty);        }
            },    _restartTimer: function() {        this._stopTimer();
            this._startTimer();    },    _startTimer: function() {
            // save timer cookie for removal later
            this._timer = window.setInterval(Function.createDelegate(this, this._timerCallback), this._interval);
            },    _stopTimer: function() {        if(this._timer) {
            window.clearInterval(this._timer);            this._timer = null;
            }    }}
            // JSON object that describes all properties, events, and methods of this component that should
            // be addressable through the Sys.TypeDescriptor methods, and addressable via xml-script.
            Demo.Timer.descriptor = {    properties: [   {name: 'interval', type: Number},
            {name: 'enabled', type: Boolean} ],
            events: [ {name: 'tick'} ]}
            Demo.Timer.registerClass('Demo.Timer', Sys.Component);
            // Since this script is not loaded by System.Web.Handlers.ScriptResourceHandler
            // invoke Sys.Application.notifyScriptLoaded to notify ScriptManager
            // that this is the end of the script.
            if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
            

ASP.NET AJAX Life Cycle

  1. Sys.Application.init(event handle need Sys.Application.add_init(MyInit);
  2. Sys.Application.load (event handler function pageLoad(sender, args){}
  3. Sys.Application.unload (event handler function pageUnLoad(sender, args){}
  4. Sys.Application.disposing( inherit from Component class)
  5. Sys.Application.propertyChanged( inherit from Component class)
  1. Sys.WebForms.PageRequestManager.initializeRequest
  2. Sys.WebForms.PageRequestManager.beginRequest
  3. Sys.WebForms.PageRequestManager.pageLoading
  4. Sys.WebForms.PageRequestManager.pageLoaded
  5. Sys.WebForms.PageRequestManager.endRequest

For detail see this

            Sys.Application.add_init(applicationInit);
            //Sys.Application.add_load(applicationLoad);
            Sys.Application.add_unload(applicationUnload);

            function applicationInit(sender, args)
            {
            alert("application init");
            }

            function pageLoad(sender, args) {
            //function applicationLoad(sender, args) {
            alert("application load");
            }

            function applicationUnload(sender, args)
            {
            alert("application unload");
            }

            Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(pageRequestInitRequestHandler);
            Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(pageRequestManagerBeginRequestHandler);
            Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(pageReqeustPageLoading);
            Sys.WebForms.PageRequestManager.getInstance().add_endRequest(pageRequestManagerEndRequestHandler);
            Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(pageRequestManagerLoadHandler);

            function pageRequestInitRequestHandler(sender, args)
            {
            alert("PageRequestManager initRequest");
            }

            function pageReqeustPageLoading(sender, args)
            {
            alert("PageRequestManager pageLoading");
            }

            function pageRequestManagerBeginRequestHandler(sender, args)
            {
            alert("PageRequestManager beginRequestr");
            }

            function pageRequestManagerLoadHandler(sender, args)
            {
            alert("PageRequestManager pageLoaded");
            }

            function pageRequestManagerEndRequestHandler(sender, args)
            {
            alert("PageRequestManager endRequest");
            }
            

For the initial load, the event sequence is PageRequestManager pageLoaded, application init, application load. For asynchronous call, is PageRequestManager initRequest, PageRequestManager beginRequest, PageRequestManager pageLoading, PageRequestManager pageLoaded, application load, PageRequestManager endRequest.

ASP.NET AJAX WebRequest

            // Instantiate a WebRequest.
            var wRequest = new Sys.Net.WebRequest();

            // Set the request URL.
            wRequest.set_url("getTarget.htm");

            // Set the request verb.
            wRequest.set_httpVerb("GET");

            // Set the request callback function.
            wRequest.add_completed(OnWebRequestCompleted);


            // Execute the request.
            wRequest.invoke();

            // This callback function processes the
            // request return values. It is called asynchronously
            // by the current executor.
            function OnWebRequestCompleted(executor, eventArgs)
            {
            if(executor.get_responseAvailable())
            {
            // Clear the previous results.

            resultElement.innerHTML = "";

            // Display Web request status.
            resultElement.innerHTML +=
            "Status: [" + executor.get_statusCode() + " " +
            executor.get_statusText() + "]" + "
"; // Display Web request headers. resultElement.innerHTML += "Headers: "; resultElement.innerHTML += executor.getAllResponseHeaders() + "
"; // Display Web request body. resultElement.innerHTML += "Body:"; if(document.all) resultElement.innerText += executor.get_responseData(); else resultElement.textContent += executor.get_responseData(); } }

Jul 29, 2007

UpdatePanel tricks

Trigger update from outside of the panel

            
            
            
            
            
            
            
            

            

Nesting UpdatePanel Controls

Nested panels are useful when you want to be able to refresh specific regions of the page separately and refresh multiple regions at the same time. You can accomplish this by setting the UpdateMode property of both the outer and nested controls to Conditional. This causes the outer panel not to refresh its page region if only the inner panel is refreshing. However, if the outer panel is refreshed, the nested panels are refreshed also. By default, the UpdateMode property of an UpdatePanel control is set to Always. This means that whenever a partial-page refresh is triggered, the UpdatePanel control will refresh the page region regardless of whether it was the UpdatePanel control that was triggered. To make sure that the UpdatePanel control will refresh only when it has been triggered, you can set the UpdateMode property of the UpdatePanel control to Conditional.

            
            
            
            
            
            
            
            
            
            
            
            

            

Sibling UpdatePanel

For sibling updatepanels, if their updatemode is "conditional", then by default they only update their own area. But you can use trigger in case when click one button in side of one panel to update the content in other panel.

Disabling Automatic Triggers

If you don't want your the control trigger update in update panel, (some time this is desireable when you want to trigger update by certain control, instead of all control in update panel), you need to set ChildrenAsTriggers to false

            <%@ Page Language="C#" %>

            

            

            
            
            Untitled Page
            
            
            

Managing Triggers and Refreshing an UpdatePanel Programmatically

Sometimes, if you want more granular control how to update the update panel, you can do that programatically. In this case, you don't need to specify statically trigger in aspx page, you do that in you .net code.

            protected void Page_Load()
            {
            ScriptManager1.RegisterAsyncPostBackControl(SurveyDataList);
            }

            protected void ChoicesRadioButtonList_SelectedIndexChanged(object sender, EventArgs e)
            {
            SortedList answers = this.AnsweredQuestions;
            RadioButtonList r = (RadioButtonList)sender;
            answers[r.ToolTip] = r.SelectedValue;
            this.AnsweredQuestions = answers;

            ResultsList.DataSource = this.AnsweredQuestions;
            ResultsList.DataBind();

            if (this.AnsweredQuestions.Count == SurveyDataList.Items.Count)
            SubmitButton.Visible = true;

            UpdatePanel1.Update();
            }
            

Adding script to client using ScriptManager

You can add script to page either statically or dynamically, for example

            
            
            
            
            
            
            void Page_Load(object sender, EventArgs e)
            {
            ScriptManager smgr = ScriptManager.GetCurrent(Page);
            if (smgr == null)
            {
            throw new Exception("ScriptManager not found");
            }

            ScriptReference sref = new ScriptReference();
            sref.Path = "~/scripts/test.js";
            smgr.Scripts.Add(sref);

            }
            

Utiltity to convert htm



escapeHTML

            function escapeHTML (str)
            {
            var div = document.createElement('div');
            var text = document.createTextNode(str);
            div.appendChild(text);
            return div.innerHTML;
            };

            

Jul 24, 2007

Relational vs. Object Modeling

This is an excerpt of Expert C# Business Object.

Before going any further, let’s make sure we’re in agreement that object models aren’t the same as relational models. Relational models are primarily concerned with the efficient storage of data, so that replication is minimized. Relational modeling is governed by the rules of normalization, and almost all databases are designed to meet at least the third normal form. In this form, it’s quite likely that the data for any given business concept or entity is split between multiple tables in the database in order to avoid any duplication of data.

Object models, on the other hand, are primarily concerned with modeling behavior, not data. It’s not the data that defines the object, but the role the object plays within your business domain. Every object should have one clear responsibility and a limited number of behaviors focused on fulfilling that responsibility.

For instance, a Customer object may be responsible for adding and editing customer data. A CustomerInfo object in the same application may be responsible for providing read-only access to customer data. Both objects will use the same data from the same database and table, but they provide different behaviors.

Similarly, an Invoice object may be responsible for adding and editing invoice data. But invoices include some customer data. A na├»ve solution is to have the Invoice object make use of the aforementioned Customer object, but that’s not a good answer. That Customer object should only be used in the case where the application is adding or editing customer data—something that isn’t occurring while working with invoices. Instead, the Invoice object should directly interact with the customer data it needs to do its job.

Through these two examples, it should be clear that sometimes multiple objects will use the same relational data. In other cases, a single object will use relational data from different data entities. In the end, the same customer data is being used by three different objects. The point, though, is that each one of these objects has a clearly defined responsibility that defines the object’s behavior. Data is merely a resource the object needs to implement that behavior.

Behavioral Object-Oriented Design

It is a common trap to think that data in objects needs to be normalized like it is in a database. A better way to think about objects is to say that behavior should be normalized. The goal of object oriented design is to avoid replication of behavior, not data. In object-oriented design, behavior should be normalized, not data.

At this point, most people are struggling. Most developers have spent years programming their brains to think relationally, and this view of object-oriented design flies directly in the face of that conditioning. Yet the key to the successful application of object-oriented design is to divorce object thinking from relational or data thinking.

Perhaps the most common objection at this point is this: if two objects (say, Customer and Invoice) both use the same data (say, the customer’s name), how do you make sure that consistent business rules are applied to that data? And this is a good question.

The answer is that the behavior must be normalized. Business rules are merely a form of behavior. The business rule specifying that the customer name value is required, for instance, is just a behavior associated with that particular value. Earlier in the chapter, I discussed the idea that a validation rule can be reduced to a method defined by a delegate. A delegate is just an object that points to a method, so it is quite possible to view the delegate itself as the rule. Following this train of thought, every rule then becomes an object.

Behavioral object-oriented design relies heavily on the concept of collaboration. Collaboration is the idea that an object should collaborate with other objects to do its work. If an object starts to become complex, you can break the problem into smaller, more digestible parts by moving some of the sub-behaviors into other objects that collaborate with the original object to accomplish the overall goal.

In the case of a required customer name value, there’s a Rule object that defines that behavior. Both the Customer and Invoice objects can collaborate with that Rule object to ensure that the rule is consistently applied. As you can see in Figure 2-6, the actual rule is only implemented once, but is used as appropriate—effectively normalizing that behavior.

It could be argued that the CustomerName concept should become an object of its own, and that this object would implement the behaviors common to the field. While this sounds good in an idealistic sense, it has serious performance and complexity drawbacks when implemented on development platforms such as .NET. Creating a custom object for every field in your application can rapidly become overwhelming, and such an approach makes the use of technologies like data binding very complex. My approach of normalizing the rules themselves provides a workable compromise; providing a high level of code reuse while still offering good performance and allowing the application to take advantage of all the features of the .NET platform.

Object-Relational Mapping

If object models aren’t the same as relational models (or some other data models that we might be using), some mechanism is needed by which data can be translated from the Data Storage and Management layer up into the object-oriented Business Logic layer.

This is a well-known issue within the object-oriented community. It is commonly referred to as the impedance mismatch problem, and one of the best discussions of it can be found in David Taylor’s book, Object-Oriented Technology: A Manager's Guide (Addison-Wesley, 1991).

Several object-relational mapping (ORM) products exist for the .NET platform from various vendors. In truth, however, most ORM tools have difficulty working against object models defined using behavioral object-oriented design. Unfortunately, most of the ORM tools tend to create "superpowered" DataSet equivalents, rather than true behavioral business objects. In other words, they create a data-centric representation of the business data and wrap it with business logic. The differences between such a data-centric object model and what I am proposing in this book are subtle but important. Behavioral object modeling creates objects that are focused on the object’s behavior, not on the data it contains. The fact that objects contain data is merely a side effect of implementing behavior; the data is not the identity of the object. Most ORM tools, by contrast, create objects based around the data, with the behavior being a side effect of the data in the object. Beyond the philosophical differences, the wide variety of mappings that you might need, and the potential for business logic driving variations in the mapping from object to object, make it virtually impossible to create a generic ORM product that can meet everyone’s needs. Consider the Customer object example discussed earlier. While the customer data may come from one database, it is totally realistic to consider that some data may come from SQL Server while other data comes through screen-scraping a mainframe screen. It’s also quite possible that the business logic will dictate that some of the data is updated in some cases, but not in others. Issues like these are virtually impossible to solve in a generic sense, and so solutions almost always revolve around custom code. The most a typical ORM tool can do is provide support for simple cases, in which objects are updated to and from standard, supported, relational data stores. At most, they’ll provide hooks by which you can customize their behavior. Rather than trying to build a generic ORM product as part of this book, I’ll aim for a much more attainable goal. The framework in this book will define a standard set of four methods for creating, retrieving, updating, and deleting objects. Business developers will implement these four methods to work with the underlying data management tier by using ADO.NET, the XML support in .NET, Web Services, or any other technology required to accomplish the task. In fact, if you have an ORM (or some other generic data access) product, you’ll often be able to invoke that tool from these four methods just as easily as using ADO.NET directly.

The approach taken in this book and the associated framework is very conducive to code generation. Many people use code generators to automate the process of building common data access logic for their objects—thus achieving high levels of productivity while retaining the ability to create a behavioral object-oriented model.

The point is that the framework will simplify object persistence to the point at which all developers need to do is implement these four methods in order to retrieve or update data. This places no restrictions on the object’s ability to work with data, and provides a standardized persistence and mapping mechanism for all objects.

Jul 9, 2007

Mastering Text

Mastering Text
            

Table basic

I found an artical about how to tag in table works. Please see this

            

Jul 8, 2007

Use Jquery to strip your table

check the result. Or check the tutorial
            

Namespace

            var MSDNMagNS = {};
            // nested namespace "Examples"
            MSDNMagNS.Examples = {};

            MSDNMagNS.Examples.Pet = function(name) { // code };
            MSDNMagNS.Examples.Pet.prototype.toString = function() { // code };

            var pet = new MSDNMagNS.Examples.Pet("Yammer");
            

javascript inheritance

Javascript has not class, it has only function. So its inheritance is somewhat confusing. Before we talk about inheritance, let's make sure we understanding the basic. Here is the fact.
  1. a function has property "prototype", the prototype object's points back to the function itself.
                function Dog(name)
                {
                }
                //the following are true
                alert(Dog.prototype.constructor == Dog);
                alert(Dog.constructor == Function);
                alert(Object.constructor == Function);
                
  2.             var spot = new Dog("fred");
                
    What does this mean? It means the above picture.
                var spot = new Dog("Spot");
    
                // Dog.prototype is the prototype of spot
                alert(Dog.prototype.isPrototypeOf(spot));
    
                // spot inherits the constructor property
                // from Dog.prototype
                alert(spot.constructor == Dog.prototype.constructor);
                alert(spot.constructor == Dog);
    
                // But constructor property doesn’t belong
                // to spot. The line below displays "false"
                //because a spot is not a function, only fucntion has constructor
                alert(spot.hasOwnProperty("constructor"));
    
                //but why spot.constructor is not null, it turns up that if the runtime find
                //a property is null, it will check its prototype's property
    
                // The constructor property belongs to Dog.prototype
                // The line below displays “true”
                alert(Dog.prototype.hasOwnProperty("constructor"));
    
                alert(Dog.prototype) //alert [object Object]
                
    The following change depict the property sovling process.

Now let's look how prototype inheritance work

            function Pet()
            {
            this.walk = function()
            { alert("pet walk"); }
            }

            var p = new Pet();
            p.walk();

            function Dog(){}

            //prototype is an instance(object)
            Dog.prototype = p;

            var d = new Dog();

            d.walk();

            

Jul 5, 2007

page event beforeunload vs unload

beforeunload happend before unload. beforeunload can give user a message, asking user if want to contine or not, if yes, unload event will trigger and unload can show user a message, but can not block to unload the page. Below is the sample.

            // Attach to the beforeunload handler
            window.onbeforeunload = function(){
            // Return an explanation for why the user should not leave the page.
            return 'Your data will not be saved.';
            };

            // Watch for the user leaving the web site
            window.onunload = function(){
            // Display a message to the user, thanking them for visiting
            alert( 'Thanks for visiting!' );
            };

            

Jul 4, 2007

Animation

Slide In

A Function for Slowly Revealing a Hidden Element by Increasing Its Height Over a Matter of One Second.

            function slideDown( elem ) {
            // Start the slide down at  0
            elem.style.height = "0px";

            // Show the element (but you can see it, since the height is 0)
            show( elem );

            // Find the full, potential, height of the element
            var h = fullHeight( elem );

            // Were going to do a 20 frame animation that takes
            // place over one second
            for ( var i = 0; i <= 100; i += 5 ) {
            // A closure to make sure that we have the right i
            (function(){
            var pos = i;

            // Set the timeout to occur at the specified time in the future
            setTimeout(function(){
            // Set the new height of the element
            elem.style.height = (pos/100)*h + "px";
            //elem.style.height = (pos/100)*h) + "px";
            },
            ( pos + 1 ) * 10 );
            }
            )();

            }
            }

            // A function for hiding (using display) an element
            function hide( elem ) {
            // Find out what its current display state is
            var curDisplay = getStyle( elem, 'display' );
            // Remember its display state for later
            if ( curDisplay != 'none' )
            elem.$oldDisplay = curDisplay;
            // Set the display to none (hiding the element)
            elem.style.display = 'none';
            }

            // A function for showing (using display) an element
            function show( elem ) {
            // Set the display property back to what it use to be, or use
            // 'block', if no previous display had been saved
            elem.style.display = elem.$oldDisplay || '';
            }

            

Fade in

A Function for Slowly Revealing a Hidden Element by Increasing Its Opacity Over a Matter of One Second

            function fadeIn( elem ) {
            // Start the opacity at  0
            setOpacity( elem, 0 );

            // Show the element (but you can see it, since the opacity is 0)
            show( elem );

            // Were going to do a 20 frame animation that takes
            // place over one second
            for ( var i = 0; i <= 100; i += 5 ) {
            // A closure to make sure that we have the right i
            (function(){
            var pos = i;

            // Set the timeout to occur at the specified time in the future
            setTimeout(function(){

            // Set the new opacity of the element
            setOpacity( elem, pos );

            }, ( pos + 1 ) * 10 );
            })();
            }
            }

            // Set an opacity level for an element
            // (where level is a number 0-100)
            function setOpacity( elem, level ) {
            // If filters exist, then this is IE, so set the Alpha filter
            if ( elem.filters )
            elem.style.filters = 'alpha(opacity=' + level + ')';
            // Otherwise use the W3C opacity property
            else
            elem.style.opacity = level / 100;
            }

            

Jul 3, 2007

Javascript and Postion

In CSS, elements are positioned using offsets. The measurement used is the amount of offset from the top-left corner of an element’s parent.

offsetParent: Theoretically, this is the parent that an element is positioned within. However, in practice, the element that offsetParent refers to depends on the browser (for example, in Firefox it refers to the root node, and in Opera, the immediate parent).

offsetLeft and offsetTop: These parameters are the horizontal and vertical offsets of the element within the context of the offsetParent. Thankfully, this is always accurate in modern browsers.

Two Helper Functions for Determining the x and y Locations of an Element Relative to the Entire Document

            // Find the X (Horizontal, Left) position of an element
            function pageX(elem)
            {
            // See if we're at the root element, or not
            return elem.offsetParent ?
            // If we can still go up, add the current offset and recurse upwards
            elem.offsetLeft + pageX( elem.offsetParent ) :
            // Otherwise, just get the current offset
            elem.offsetLeft;
            }

            // Find the Y (Vertical, Top) position of an element
            function pageY(elem)
            {
            // See if we're at the root element, or not
            return elem.offsetParent ?
            // If we can still go up, add the current offset and recurse upwards
            elem.offsetTop + pageY( elem.offsetParent ) :
            // Otherwise, just get the current offset
            elem.offsetTop;
            }
            

Two Functions for Determining the Position of an Element Relative to Its Parent Element. (parent element is not necessary its offsetParent)

            // Find the horizontal positioing of an element within its parent
            function parentX(elem) {
            // If the offsetParent is the element's parent, break early
            return elem.parentNode == elem.offsetParent ?
            elem.offsetLeft :
            // Otherwise, we need to find the position relative to the entire
            // page for both elements, and find the difference
            pageX( elem ) - pageX( elem.parentNode );
            }

            // Find the vertical positioning of an element within its parent
            function parentY(elem) {
            // If the offsetParent is the element's parent, break early
            return elem.parentNode == elem.offsetParent ?
            elem.offsetTop :
            // Otherwise, we need to find the position relative to the entire
            // page for both elements, and find the difference
            pageY( elem ) - pageY( elem.parentNode );
            }
            

Helper Functions for Finding the CSS Positioning of an Element

            // Find the left position of an element
            function posX(elem) {
            // Get the computed style and get the number out of the value
            return parseInt( getStyle( elem, "left" ) );
            }

            // Find the top position of an element
            function posY(elem) {
            // Get the computed style and get the number out of the value
            return parseInt( getStyle( elem, "top" ) );
            }
            

A Pair of Functions for Setting the x and y Positions of an Element, Regardless of Its Current Position

            // A function for setting the horizontal position of an element
            function setX(elem, pos) {
            // Set the 'left' CSS property, using pixel units
            elem.style.left = pos + "px";
            }

            // A function for setting the vertical position of an element
            function setY(elem, pos) {
            // Set the 'left' CSS property, using pixel units
            elem.style.top = pos + "px";
            }
            

A Pair of Functions for Adjusting the Position of an Element Relative to Its Current Position

            // A function for adding a number of pixels to the horizontal
            // position of an element
            function addX(elem,pos) {
            // Get the current horz. position and add the offset to it.
            setX( posX(elem) + pos );
            }

            // A function that can be used to add a number of pixels to the
            // vertical position of an element
            function addY(elem,pos) {
            // Get the current vertical position and add the offset to it
            setY( posY(elem) + pos );
            }
            

Two Functions for Retreiving the Current Height or Width of a DOM Element

            // Get the actual height (using the computed CSS) of an element
            function getHeight( elem ) {
            // Gets the computed CSS value and parses out a usable number
            return parseInt( getStyle( elem, 'height' ) );
            }

            // Get the actual width (using the computed CSS) of an element
            function getWidth( elem ) {
            // Gets the computed CSS value and parses out a usable number
            return parseInt( getStyle( elem, 'width' ) );
            }
            

Two Functions for Finding the Full Potential Height or Width of an Element, Even If the Element Is Hidden

            // Find the full, possible, height of an element (not the actual,
            // current, height)
            function fullHeight( elem ) {
            // If the element is being displayed, then offsetHeight
            // should do the trick, barring that, getHeight() will work
            if ( getStyle( elem, "display" ) != "none" )
            return elem.offsetHeight || getHeight( elem );

            // Otherwise, we have to deal with an element with a display
            // of none, so we need to reset its CSS properties to get a more
            // accurate reading
            var old = resetCSS( elem, {
            display: "",
            visibility: "hidden",
            position: "absolute"
            });

            // Figure out what the full height of the element is, using clientHeight
            // and if that doesn"t work, use getHeight
            var h = elem.clientHeight || getHeight( elem );

            // Finally, restore the CSS properties back to what they were
            restoreCSS( elem, old );

            // and return the full height of the element
            return h;
            }

            // Find the full, possible, width of an element (not the actual,
            // current, width)
            function fullWidth( elem ) {
            // If the element is being displayed, then offsetWidth
            // should do the trick, barring that, getWidth() will work
            if ( getStyle( elem, "display" ) != "none" )
            return elem.offsetWidth || getWidth( elem );

            // Otherwise, we have to deal with an element with a display
            // of none, so we need to reset its CSS properties to get a more
            // accurate reading
            var old = resetCSS( elem, {
            display: "",
            visibility: "hidden",
            position: "absolute"
            });

            // Figure out what the full width of the element is, using clientWidth
            // and if that doesn"t work, use getWidth
            var w = elem.clientWidth || getWidth( elem );

            // Finally, restore the CSS properties back to what they were
            restoreCSS( elem, old );

            // and return the full width of the element
            return w;
            }

            // A function used for setting a set of CSS properties, which
            // can then be restored back again later
            function resetCSS( elem, prop ) {
            var old = {};

            // Go through each of the properties
            for ( var i in prop ) {
            // Remember the old property value
            old[ i ] = elem.style[ i ];

            // And set the new value
            elem.style[ i ] = prop[i];
            }

            // Retun the set of changed values, to be used by restoreCSS
            return old;
            }

            // A function for restoring the side effects of the resetCSS function
            function restoreCSS( elem, prop ) {
            // Reset all the properties back to their original values
            for ( var i in prop )
            elem.style[ i ] = prop[ i ];
            }

            

A Set of Functions for Toggling the Visibility of an Element Using Its CSS Display Property.

            // A function for hiding (using display) an element
            function hide( elem ) {
            // Find out what it"s current display state is
            var curDisplay = getStyle( elem, "display" );

            //  Remember its display state for later
            if ( curDisplay != "none" )
            elem.$oldDisplay = curDisplay;

            // Set the display to none (hiding the element)
            elem.style.display = "none";
            }

            // A function for showing (using display) an element
            function show( elem ) {
            // Set the display property back to what it use to be, or use
            // "block", if no previous display had been saved
            elem.style.display = elem.$oldDisplay || "block";
            }
            

A Function for Adjusting the Opacity Level of an Element.

            // Set an opacity level for an element
            // (where level is a number 0-100)
            function setOpacity( elem, level ) {
            // If filters exist, then this is IE, so set the Alpha filter
            if ( elem.filters )
            elem.filters.alpha.opacity = level;

            // Otherwise use the W3C opacity property
            else
            elem.style.opacity = level / 100;
            }



            

Finding the actual computed value of a CSS Style Property on an Element

            // Get a style property (name) of a specific element (elem)
            function getStyle( elem, name )
            {
            // If the property exists in style[], then it's been set
            // recently (and is current)
            if (elem.style[name])
            {
            return elem.style[name];
            }
            // Otherwise, try to use IE's method
            else if (elem.currentStyle)
            {
            return elem.currentStyle[name];
            }
            // Or the W3C's method, if it exists
            else if (document.defaultView && document.defaultView.getComputedStyle)
            {
            // It uses the traditional 'text-align' style of rule writing,
            // instead of textAlign
            name = name.replace(/([A-Z])/g,"-$1");
            name = name.toLowerCase();
            // Get the style object and get the value of the property (if it exists)
            var s = document.defaultView.getComputedStyle(elem,"");
            return s && s.getPropertyValue(name);
            // Otherwise, we're using some other browser
            } else
            return null;
            }
            

Unobtrusive DOM Scripting

Everything that you’ve learned up to this point comes to one incredibly important goal: writing your JavaScript so that it interacts with your users unobtrusively and naturally. The driving force behind this style of scripting is that you can now focus your energy on writing good code that will work in modern browsers while failing gracefully for older (unsupported) browsers.To achieve this, you could combine three techniques that you’ve learned to make an application unobtrusively scripted:

  1. All functionality in your application should be verified. For example, if you wish to access the HTML DOM you need to verify that it exists and has all the functions that you need to use it (e.g., if ( document && document.getElementById ) ). This technique is discussed in Chapter 2.
  2. Use the DOM to quickly and uniformly access elements in your document. Since you already know that the browser supports DOM functions, you can feel free to write your code simply and without hacks or kludges.
  3. Finally, you dynamically bind all events to the document using the DOM and your addEvent function. Nowhere must you have something such as this: . This is very bad in the eyes of coding unobtrusively, as that code will effectively do nothing if JavaScript is turned off or if the user has an old version of a browser that you don’t support. Since you’re just pointing the user to a nonsensical URL, it will give no interaction to users who are unable to support your scripting functionality.

If it isn’t apparent already, you need to pretend that the user does not have JavaScript installed at all, or that his browser may be inferior in some way. Go ahead, open your browser, visit your favorite web page, and turn off JavaScript; does it still work? How about all CSS; can you still navigate to where you need to go? Finally, is it possible to use your site without a mouse? All of these should be part of the ultimate goal for your web site. Thankfully, since you’ve built up an excellent understanding of how to code really efficient JavaScript code, the cost of this transition is negligible and can be done with minimal effort.

            

Binding Event Listeners

Traditional Binding The traditional way of binding events is the one that I’ve been using up until now in this chapter. It is by far the simplest, most compatible way of binding event handlers. To use this particular method, you attach a function as a property to the DOM element that you wish to watch. Some samples of attaching events using the traditional method are shown in Listing 6-10.

            // Find the first form element and attach a 'submit' event handler to it
            document.getElementsByTagName("form")[0].onsubmit = function(e){
            // Stop all form submission attempts
            return stopDefault( e );
            };
            // Attach a keypress event handler to the body element of the document
            document.body.onkeypress = myKeyPressHandler;
            // Attach an load event hanlder to the page
            window.onload = function(){ … };
            

The disadvantages of the traditional method are as follows:

  • The traditional method only works with event bubbling, not capturing and bubbling.
  • It’s only possible to bind one event handler to an element at a time. This has the potential to cause confusing results when working with the popular window.onload property (effectively overwriting other pieces of code that have used the same method of binding events). An example of this problem is shown in Listing 6-11, where an event handler overwrites an old event handler.
  • The event object argument is only available in non-Internet Explorer browsers.

            // Bind your initial load handler
            window.onload = myFirstHandler;
            // somewhere, in another library that you've included,
            // your first handler is overwritten
            // only 'mySecondHandler' is called when the page finishes loading
            window.onload = mySecondHandler;
            

Knowing that it’s possible to blindly override other events, you should probably opt to only use the traditional means of event binding in simple situations, where you can trust all the other code that is running alongside yours. One way to get around this troublesome mess, however, is to use the modern event binding methods provided by browsers.

DOM Binding:W3C

            // Find the first form element and attach a 'submit' event handler to it
            document.getElementsByTagName("form")[0].addEventListener('submit',function(e){
            // Stop all form submission attempts
            return stopDefault( e );
            }, false);
            // Attach a keypress event handler to the body element of the document
            document.body.addEventListener('keypress', myKeyPressHandler, false);
            // Attach an load event hanlder to the page
            window.addEventListener('load', function(){ … }, false);
            

Advantages of W3C Binding

The advantages to the W3C event-binding method are the following:
  • This method supports both the capturing and bubbling phases of event handling. The event phase is toggled by setting the last parameter of addEventListener to false (for bubbling) or true (for capturing).
  • Inside of the event handler function, the this keyword refers to the current element.
  • The event object is always available in the first argument of the handling function.
  • You can bind as many events to an element as you wish, with none overwriting previously bound handlers.

Disadvantage of W3C Binding

The disadvantage to the W3C event-binding method is the following:
  • It does not work in Internet Explorer; you must use IE’s attachEvent function instead.

DOM Binding: IE

In a lot of ways, the Internet Explorer way of binding events appears to be very similar to the W3C’s. However, when you get down to the details, it begins to differ in some very significant ways. Some examples of attaching event handlers in Internet Explorer can be found in Listing 6-13.

Listing 6-13. Samples of Attaching Event Handlers to Elements Using the Internet Explorer Way of Binding Events

            // Find the first form element and attach a 'submit' event handler to it
            document.getElementsByTagName("form")[0].attachEvent('onsubmit',function(){
            // Stop all form submission attempts
            return stopDefault();
            },);
            // Attach a keypress event handler to the body element of the document
            document.body.attachEvent('onkeypress', myKeyPressHandler);
            // Attach an load event hanlder to the page
            window.attachEvent('onload', function(){ … });
            

Advantage of IE Binding

  • You can bind as many events to an element as you desire, with none overwriting previously bound handlers.

Disadvantages of IE Binding

  • Internet Explorer only supports the bubbling phase of event capturing.
  • The this keyword inside of event listener functions points to the window object, not the current element (a huge drawback of IE).
  • The event object is only available in the window.event parameter.
  • The name of the event must be named as ontype—for example, onclick instead of just requiring click.
  • It only works in Internet Explorer. You must use the W3C’s addEventListener for non-IE browsers.

DOM Binding:Dean Edwards way

            // Wait for the page to finish loading
            addEvent(window, "load", function(){
            // Watch for any keypresses done by the user
            addEvent( document.body, "keypress", function(e){
            // If the user hits the Spacebar + Ctrl key
            if ( e.keyCode == 32 && e.ctrlKey )
            {
            // Display our special form
            this.getElementsByTagName("form")[0].style.display = 'block';
            // Make sure that nothing strange happens
            //this works in IE and also firfox
            //because the library already fix the event
            e.preventDefault();
            }
            });
            });
            

Advantages of addEvent

  • It works in all browsers, even older unsupported browsers.
  • The this keyword is available in all bound functions, pointing to the current element.
  • All browser-specific functions for preventing the default browser action and for stopping event bubbling are neutralized.
  • The event object is always passed in as the first argument, regardless of the browser type.

Disadvantage of addEvent

  • It only works during the bubbling phase (since it uses the traditional method of event binding under the hood).

Event Object, this, stopbuble, canceldefalt,

The tricky part of the event object, however, is that Internet Explorer’s implementation is different from the W3C’s specification. Internet Explorer has a single global event object (which can be reliably found in the global variable property window.event), whereas every other browser has a single argument passed to it, containing the event object.


            // Find the first textarea on the page and bind a keypress listener
            document.getElementsByTagName("textarea")[0].onkeypress = function(e){

            // If no event object exists, then grab the global (IE-only) one
            e = e || window.event;
            // If the Enter key is pressed, return false (causing it to do nothing)
            return e.keyCode != 13;
            };

            

The this Keyword

            // Find all 
  • elements and bind the click handler to each of them //this is supported in both IE and firefox, if attache event this way var li = document.getElementsByTagName("li"); for ( var i = 0; i < li.length; i++ ) { li[i].onclick = handleClick; } // The click handler – when called it changes the background and // foreground color of the specified element function handleClick() { this.style.backgroundColor = "blue"; this.style.color = "white"; }
  • Stopping Event Bubbling

    This function can be called in side a event handler

                function stopBubble(e)
                {
                // If an event object is provided, then this is a non-IE browser
                if ( e && e.stopPropagation )
                {
                // and therefore it supports the W3C stopPropagation() method
                e.stopPropagation();
                }
                else
                {
                // Otherwise, we need to use the Internet Explorer
                // way of cancelling event bubbling
                window.event.cancelBubble = true;
                }
                }
    
                

    When do we need to stop the bubble of events? The majority of the time, you will probably never have to worry about it. The need for it begins to arise when you start developing dynamic application (especially ones that deeal with the keyboard or mouse). With the ability to stop the event bubbling, you now have complete control over which elements get to see and handle an event. This is a fundamental tool necessary for exploring the development of dynamic web applications. The final aspect is to cancel the default action of the browser, allowing you to completely override what the browser does and implement new functionality instead.

    Event Default action

    Default actions can be summarized as anything that the browser does that you do not explicitly tell it to do. For example

    All of the previous actions are executed by the browser even if you stop the event bubbling or if you have no event handler bound at all. This can lead to significant problems in your scripts. What if you want your submitted forms to behave differently? Or what if you want elements to behave differently than their intended purpose? Since canceling event bubbling isn’t enough to prevent the default action, you need some specific code to handle that directly. As with canceling event bubbling, there are two ways of stopping the default action from occurring: the IE-specific way and the W3C way. Both ways are shown in Listing 6-8. The function shown takes a single argument: the event object that’s passed in to the event handler. This function should be used at the very end of your event handler, like so: return stopDefault( e );—as your handler needs to also return false (which is, itself, returned from stopDefault for you).

                function stopDefault( e )
                {
                // Prevent the default browser action (W3C)
                if ( e && e.preventDefault )
                {
                e.preventDefault();
                }
                // A shortcut for stoping the browser action in IE
                else
                {
                window.event.returnValue = false;
                }
                }
    
                //or
                function stopDefault( e )
                {
                return false; //this is supported by both ie and firefox
                }
    
                

    The code makes all the links on a page load in a self-contained