Posted on March 5, 2008 05:57 by mcollins

While I am a fan of using Microsoft's AJAX framework in my ASP.NET applications, I am realistic and admit that it does some things well, but for other things, sometimes there's a better way.  Take the following web page for example:

   1: <%@ Page Language="C#" AutoEventWireup="true" ... %>
   2:  
   3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   4:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   5: <html xmlns="http://www.w3.org/1999/xhtml">
   6: <head runat="server">
   7:     <title>Untitled Page</title>
   8: </head>
   9: <body>
  10:     <form id="form1" runat="server">
  11:     <asp:ScriptManager ID="ScriptManager1" runat="server">
  12:     </asp:ScriptManager>
  13:     <div id="faq">
  14:         <h2><a href="#">Question 1</a></h2>
  15:         <div>
  16:             <p>Answer 1</p>
  17:         </div>
  18:         <h2><a href="#">Question 2</a></h2>
  19:         <div>
  20:             <p>Answer 2</p>
  21:         </div>
  22:     </div>
  23:     <asp:Panel ID="DebugPanel" runat="server" Visible="false">
  24:         <textarea id="TraceConsole" rows="30" cols="60"></textarea>
  25:     </asp:Panel>
  26:     </form>
  27:  
  28:     <script type="text/javascript">
   1:  
   2:     <!--
   3:         Sys.Application.add_init(function() {
   4:             Sys.Debug.trace('Initializing page.');
   5:  
   6:             // Create client components. Initialize the page.
   7:             
   8:             Sys.Debug.trace('Page initialized.');
   9:         });
  10:         
  11:         function pageLoad() {
  12:             Sys.Debug.trace('pageLoad called.');
  13:             
  14:             // Begin executing the business logic for the page. Attach
  15:             // event handlers to elements, etc.
  16:             
  17:             Sys.Debug.trace('pageLoad completed.');
  18:         }
  19:         
  20:         function pageUnload() {
  21:             Sys.Debug.trace('pageUnload called.');
  22:             
  23:             // The page is being unloaded. If you attached event
  24:             // handlers in pageLoad, detach them here to prevent
  25:             // memory leaks.
  26:             
  27:             Sys.Debug.trace('pageUnload completed.');
  28:         }
  29:     //-->
  30:     
</script>
  29:  
  30: </body>
  31: </html>

This code template is a standard FAQ that has two questions and two answers.  Typically, when you see a page like this in a browser, you may come across the FAQ hiding the answers and showing you a list of the questions.  When you click on a question, then the answer will display itself.  To accomplish this feat, we'll use a little JavaScript, DOM, and CSS manipulation to show and hide the answers.  Here's the code with JavaScript using the Microsoft AJAX framework:

   1: <%@ Page Language="C#" AutoEventWireup="true" ... %>
   2:  
   3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   4:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   5: <html xmlns="http://www.w3.org/1999/xhtml">
   6: <head runat="server">
   7:     <title>Untitled Page</title>
   8: </head>
   9: <body>
  10:     <form id="form1" runat="server">
  11:     <asp:ScriptManager ID="ScriptManager1" runat="server">
  12:     </asp:ScriptManager>
  13:     <div id="faq">
  14:         <h2><a href="#">Question 1</a></h2>
  15:         <div>
  16:             <p>Answer 1</p>
  17:         </div>
  18:         <h2><a href="#">Question 2</a></h2>
  19:         <div>
  20:             <p>Answer 2</p>
  21:         </div>
  22:     </div>
  23:     <asp:Panel ID="DebugPanel" runat="server" Visible="false">
  24:         <textarea id="TraceConsole" rows="30" cols="60"></textarea>
  25:     </asp:Panel>
  26:     </form>
  27:  
  28:     <script type="text/javascript">
   1:  
   2:     <!--
   3:         Sys.Application.add_init(function() {
   4:             Sys.Debug.trace('Initializing page.');
   5:  
   6:             // Create client components. Initialize the page.
   7:             
   8:             Sys.Debug.trace('Page initialized.');
   9:         });
  10:         
  11:         function pageLoad() {
  12:             Sys.Debug.trace('pageLoad called.');
  13:             
  14:             var faq = $get('faq');
  15:             var questions = faq.getElementsByTagName('H2');
  16:             for (var i = 0; i < questions.length; i++) {
  17:                 questions[i].nextSibling.style.display = 'none';
  18:                 $addHandler(questions[i].firstChild, 'click',
  19:                     toggleAnswer);
  20:             }
  21:             
  22:             Sys.Debug.trace('pageLoad completed.');
  23:         }
  24:         
  25:         function pageUnload() {
  26:             Sys.Debug.trace('pageUnload called.');
  27:             
  28:             var faq = $get('faq');
  29:             var questions = faq.getElementsByTagName('H2');
  30:             for (var i = 0; i < questions.length; i++) {
  31:                 $removeHandler(questions[i].firstChild, 'click',
  32:                     toggleAnswer);
  33:             }
  34:             
  35:             Sys.Debug.trace('pageUnload completed.');
  36:         }
  37:         
  38:         function toggleAnswer() {
  39:             Sys.Debug.trace('toggleAnswer called.');
  40:             
  41:             var answer = this.parentNode.nextSibling;
  42:             if (answer.style.display == 'none') {
  43:                 answer.style.display = 'block';
  44:             } else {
  45:                 answer.style.display = 'none';
  46:             }
  47:             
  48:             Sys.Debug.trace('toggleAnswer completed.');
  49:             return false;
  50:         }
  51:     //-->
  52:     
</script>
  29:  
  30: </body>
  31: </html>

In the pageLoad function, I begin by using the Microsoft AJAX framework's $get function to get the element with an id of faq.  From there, I get all of the child H2 elements of the FAQ.  Next, I do two things.  First, I hide the block containing the answer to the question.  Second, I attach an event handler to the click event of the A element inside of the H2 element.

Inside of the toggleAnswer function, I look at the current display state of the block containing the answer.  If the block is visible, I hide the block.  If the block is visible, I show the block.

Overall, this code works, but it seems like a lot of code.  Is there an easier way to build this page?

   1: <%@ Page Language="C#" AutoEventWireup="true" ... %>
   2:  
   3: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   4:     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
   5: <html xmlns="http://www.w3.org/1999/xhtml">
   6: <head runat="server">
   7:     <title>Untitled Page</title>
   8: </head>
   9: <body>
  10:     <form id="form1" runat="server">
  11:     <asp:ScriptManager ID="ScriptManager1" runat="server">
  12:         <Scripts>
  13:             <asp:ScriptReference Path="~/jquery-1.2.3.min.js" />
  14:         </Scripts>
  15:     </asp:ScriptManager>
  16:     <div id="faq">
  17:         <h2><a href="#">Question 1</a></h2>
  18:         <div>
  19:             <p>Answer 1</p>
  20:         </div>
  21:         <h2><a href="#">Question 2</a></h2>
  22:         <div>
  23:             <p>Answer 2</p>
  24:         </div>
  25:     </div>
  26:     <asp:Panel ID="DebugPanel" runat="server" Visible="false">
  27:         <textarea id="TraceConsole" rows="30" cols="60"></textarea>
  28:     </asp:Panel>
  29:     </form>
  30:  
  31:     <script type="text/javascript">
   1:  
   2:     <!--
   3:         var $j = jQuery.noConflict();
   4:         
   5:         Sys.Application.add_init(function() {
   6:             Sys.Debug.trace('Initializing page.');
   7:  
   8:             // Create client components. Initialize the page.
   9:             
  10:             Sys.Debug.trace('Page initialized.');
  11:         });
  12:         
  13:         function pageLoad() {
  14:             Sys.Debug.trace('pageLoad called.');
  15:             
  16:             $j('#faq > div').hide();
  17:             $j('#faq > h2 a').click(function() {