My Feedback: ASP.NET AJAX Roadmap

Posted on July 17, 2008 by matt@mattberseth.com.
Categories: ASP.NET, Contributors.

Dave Ward had another great post this past week. He took the time to not only read the ASP.NET Ajax Road Map document, but he also wrote up a post outlining what he thought about it. So I thought I would join Dave's efforts and throw in my 2 cents as well. Of course, most of this post will not make too much sense if you aren't familiar with the Road Map document. So if that's you, here is what I recommend reading (in order) ...

My Comments ...

My goal is to be direct without being rude, lets see if I can do it.

$query

  • Any plans on providing an extensible selector engine? If you have to go through all of the work to implement CSS 2.1 and 3.0, it would be cool to let developers hook into this engine and extend it.
  • Do you have any idea on what operations will be available on the collection returned from $query? From the example provided, I can see addHandler, setStyle and create will be there, but I would really love to what else is available. Also …
    • I would like to see helper functions for each events. I find it more readable to replace addHandler with $query(‘input’).click(function(){ //do something }); I know this might seem nitpicky, but I think it is this type of thing that makes the jQuery API so easy to work with.
    1. //    I like this ...
    2. $query('input').click(function(){
    3.     // do something
    4. });
    5.  
    6. //    better than this
    7. $query('input').addHandler('click', function(){
    8.     // do something
    9. });
    • I like the terseness of the jQuery API. You don’t see stuff like setStyle(). It would just be style() and all of the following would be possible. Again, probably trivial, but IMO the stuff that keeps the JS files nice and small and the API a breeze to work with
      1. //     get the width of the first matched input element
      2. var width = $query('input').style('width');
      3.  
      4. //     set the width to zero
      5. $query('input').style('width', 0);               
      6.  
      7. //     set the width and height
      8. $query('input').style({width:0, height:0});
      9.  
      10. //     set  the width and height
      11. $query('input').width(0).height(0);

$listen

  • Can we get the same stuff for attaching behaviors to elements? For example, any TEXTAREA that gets added to the DOM with a class of ‘rich’ should always get the RichTextBox behavior applied to it. Or do I have to manage this stuff myself? I would like to attach these behaviors the first time the page loads on the client and then not have to worry about it again.
  • I like how there is a single entry point to the jQuery features -> $. Any chance $listen could hang off of the wrapped set? So I could do something like this …
    1. //    again, I would prefer this
    2. $query('input').listen('hover', function(){
    3.     // do something
    4. });
    5.  
    6. //    over this
    7. $listen('hover', 'input', function(){
    8.     //    do something
    9. });
  • Is the parameter order for $listen correct? Seems like $listen(query, event, fx) would be more consistent

Templates and Client Data Sources

  • The templates look interesting. And the complex template example really does look complex ;) But I found it hard to provide any real meaningful feedback here because of the complexity of the features. This stuff seems really promising, any idea when we would be able to see examples of how a sample GridView/ListView could be replaced with a client side template/data source (sorting, paging, editing, etc...).
  • Will client side templates be able to fire server side events? Could an input button contained in a client side template trigger a server side event handler?
  • Will the 2 way databinding require the bound JS objects to implement the INotifyPropertyChanged stuff?

Animation

  • Will we have access to the animation queue?
  • This still seems too verbose ....
    1. // isn't this better ...
    2. $query('.sprite')
    3.     .fadeIn(300)
    4.     .animate({backgroundColor:'ff0000', fontSize:'2em'}, 500)
    5.     .fadeOut(300);
    6.  
    7. // than this?
    8. $query('.sprite')
    9.     .animate([
    10.         new Sys.Animation.FadeIn(300),
    11.         {'style.backgroundColor':'#ff0000', 'style.fontSize':'2em'), duration: 500 },
    12.         new Sys.Animation.FadeOut(300)
    13.     ]));

Other Stuff …

  • In my opinion, the thing jQuery has over ASP.NET AJAX and the Toolkit is its programming model. It so simple that newcomers can get up and running and author there first plug-ins in nearly no time. But, the interfaces provide the ultimate in flexibility (hooks for extending the selection engine, access to the animation queue, etc...) for the more experienced developers. It truly is elegant, simple AND powerful. Now compare this to the tutorial for creating an ASP.NET AJAX extender control. This tutorial has me writing server side code, a JavaScript behavior that is nearly 100 LOC, plus all of the plumbing that glues the two together. That's just too complex ...
  • I think the TargetControlID could use some rethinking. Again, the extender control tutorial provides a sample for adding highlighting to a control that has focus. Here is how you can tie the behavior to the TextBox ...

    1. <asp:TextBox ID="TextBox1" runat="server" />
    2. <sample:FocusBehavior runat="server"
    3.     ID="FocusBehavior1"
    4.     HighlightCssClass="MyHighLight"
    5.     NoHighlightCssClass="MyLowLight"
    6.     TargetControlID="TextBox1" />

I know I can now access the HighlighCssClass and NoHighlightCssClass properties from the codebehind, but this just isn't something I find myself doing very often. Plus, I often wanted to apply an extender to multiple elements. Imagine a form where I have 7 textboxes and I want every one to get this highlighting. Binding the extender control to a class or tag based selector would have been useful. I would have traded the server side access for that without giving it a second thought.

Also, I prefer to use the DataControlFields of the GridView and DetailsViews. It would be pretty cool if I could have attached the extender controls to these fields too ... Seems like class/tag based selection would help here too.

  • I really think the ASP.NET AJAX needs a plug-in programming model. A good part of the JavaScript code I write does not require a full blown Sys.UI.Behavior. I just want to run a bit of code when some DOM event occurs.
    • I know this is just meant as an example, but a good illustration of this is the FocusBehavior you can find in the asp.net ajax documentation. Here is the source for this behavior ...
      1. // Register the namespace for the control.
      2. Type.registerNamespace('Samples');
      3.  
      4. //
      5. // Define the behavior properties.
      6. //
      7. Samples.FocusBehavior = function(element) {
      8.     Samples.FocusBehavior.initializeBase(this, [element]);
      9.  
      10.     this._highlightCssClass = null;
      11.     this._nohighlightCssClass = null;
      12. }
      13.  
      14. //
      15. // Create the prototype for the behavior.
      16. //
      17.  
      18. Samples.FocusBehavior.prototype = {
      19.  
      20.     initialize : function() {
      21.         Samples.FocusBehavior.callBaseMethod(this, 'initialize');
      22.        
      23.         $addHandlers(this.get_element(),
      24.                      { 'focus' : this._onFocus,
      25.                        'blur' : this._onBlur },
      26.                      this);
      27.        
      28.         this.get_element().className = this._nohighlightCssClass;
      29.     },
      30.    
      31.     dispose : function() {
      32.         $clearHandlers(this.get_element());
      33.        
      34.         Samples.FocusBehavior.callBaseMethod(this, 'dispose');
      35.     },
      36.  
      37.     //
      38.     // Event delegates
      39.     //
      40.    
      41.     _onFocus : function(e) {
      42.         if (this.get_element() && !this.get_element().disabled) {
      43.             this.get_element().className = this._highlightCssClass;         
      44.         }
      45.     },
      46.    
      47.     _onBlur : function(e) {
      48.         if (this.get_element() && !this.get_element().disabled) {
      49.             this.get_element().className = this._nohighlightCssClass;         
      50.         }
      51.     },
      52.  
      53.  
      54.     //
      55.     // Behavior properties
      56.     //
      57.    
      58.     get_highlightCssClass : function() {
      59.         return this._highlightCssClass;
      60.     },
      61.  
      62.     set_highlightCssClass : function(value) {
      63.         if (this._highlightCssClass !== value) {
      64.             this._highlightCssClass = value;
      65.             this.raisePropertyChanged('highlightCssClass');
      66.         }
      67.     },
      68.    
      69.     get_nohighlightCssClass : function() {
      70.         return this._nohighlightCssClass;
      71.     },
      72.  
      73.     set_nohighlightCssClass : function(value) {
      74.         if (this._nohighlightCssClass !== value) {
      75.             this._nohighlightCssClass = value;
      76.             this.raisePropertyChanged('nohighlightCssClass');
      77.         }
      78.     }
      79. }
      80.  
      81. // Optional descriptor for JSON serialization.
      82. Samples.FocusBehavior.descriptor = {
      83.     properties: [   {name: 'highlightCssClass', type: String},
      84.                     {name: 'nohighlightCssClass', type: String} ]
      85. }
      86.  
      87. // Register the class as a type that inherits from Sys.UI.Control.
      88. Samples.FocusBehavior.registerClass('Samples.FocusBehavior', Sys.UI.Behavior);
      89.  
      90. if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

And the same functionality can easily be written as a jQuery plug-in in a fraction of the lines.

      1. (function($) {
      2.   $.fn.focus = function(options) {
      3.     // build main options before element iteration
      4.     var opts = $.extend({}, $.fn.focus.defaults, options);
      5.    
      6.     return this.each(function() {
      7.        
      8.         //    add/remove the class as the element comes into and out of focus
      9.         $(this).focus(function(){
      10.             if(!this.disabled){
      11.                 $(this).addClass(opts.highlight);
      12.             }
      13.         }).blur(function(){
      14.             if(!this.disabled){
      15.                 $(this).removeClass(opts.highlight);
      16.             }       
      17.         });
      18.     });
      19.   };
      20.  
      21.   $.fn.focus.defaults = {
      22.     highlight: 'highlight'
      23.   };
      24.  
      25. })(jQuery);

The example above really just ties a very small bit of JavaScript to a couple of event handlers. It would be great to have plug-in plumbing in the next rev to make doing stuff like this easier.

  • On sort of a related note, I think the existing existing ASP.NET AJAX and Toolkit components are too verbose and too OO (yes it feels weird saying that). Some of the things that bother me are ...
    • The uber long namespaces. Stuff like Sys.UI.DomElement.containsCssClass is just too long. Is requiring so many namespaces to organize the library a sign that it has become too large?
    • Having multiple entry points. I like how everything in jQuery starts with $(). Its simple.
    • All of the code required for the property getters and setters. Where am I seeing the value from calling raisePropertyChanged?

And I just have to wonder if having such a rigid and verbose programming model is the right choice for a client side AJAX library.

  • I don’t want to pay for features I don’t plan on using
    • I have never use the RoundedCorners and DropShadow features of the ModalPopup control, but there is no way (that I know of) for me not to pull these scripts down to the client. I like how jQuery is progressive. If the metadata plug-in is available, it will go ahead and use it. But if you decide not to pull it down, nothing will break and the widget/plugin is still usable. And for animations, just the real low-level plumbing and most often used parts of animation components are bundled in with jQuery's core. If you choose to animate colors, you can add the color animation extensions.
    • I like how jQuery doesn't pull down any extensions to the String/Number/Date prototypes.
  • The Toolkit got stale. It seemed like developing new controls was too difficult for the community to do, but the ASP.NET team was moving on to other things (possibly ASP.NET MVC and Silverlight?). So it seemed like nothing was really happening. jQuery plug-ins on the other hand are spreading like wildfire. They are simple to write AND distribute (its just a text file). The toolkit is the opposite. The controls are challenging to write and you end up distributing a DLL which may or may not correspond to the version of .Net the consumer is running with in production (I know that first hand because I received plenty of emails about people wanting to use the 3.5 version of the toolkit control with .Net 2.0).
  • I am curious if the ASP.NET AJAX team has written an application using jQuery. And I do not mean this as an insult or anything, I am really serious. In my mind that is the bar ASP.NET AJAX vNext needs to get to. I have no idea when ASP.NET AJAX vNext is coming out, but from now until then I am using jQuery. And it will be tough to persuade me to go back to ASP.NET AJAX if it is missing some of the stuff I mentioned here.

Conclusion

Well, I tried to be blunt without being rude - I truly hope that I succeeded. I hope I don't come off as angry or anything, I just have spent a lot of time with ASP.NET AJAX and the Toolkit and think that these are the things that I would want changed for vNext. And just like Dave said, I am not an MVP, or an Insider, I am just a regular dev that really wants Microsoft to nail the next version of ASP.NET AJAX. Good luck guys!

That's it. Enjoy!

Using jQuery Plugins with ASP.NET

Posted on June 29, 2008 by matt@mattberseth.com.
Categories: ASP.NET, Contributors.

The controls found in the AjaxControlToolkit fit perfectly into the ASP.NET programming model (as to be expected). You can configure the controls properties through the markup which in turn affects how the control's display and behavior on the client. Many of the control's properties support databinding so using the controls within a databound control like a ListView or a GridView is no big deal. Take for example the ProgressBar Toolkit control I blogged about (here and here). One of the places that I use this control is in a GridView's TemplateField. Instead of displaying the percentage as a text value, I use the ProgressBar to display the percentage in a more visual manner. It looks something like this ...

Live Demo | Download

image

With the Toolkit, setting this up is really simple. Just use a databinding expression to set the Value property of the ProgressBar control to the value of the PercentComplete property of the databound item. Done.

  1. <mb:ProgressControl
  2.     ID="ProgressControl17" runat="server"
  3.     Value='<%# Eval("PercentComplete") %>'
  4.     Animate="true" Mode="Manual" ShowStatusText="false"
  5. />

And if I start using and building jQuery plugins I am thinking there will be cases where I am going to miss how simple this is. So after learning about jQuery's metadata plugin, I thought it would be interesting to see explore it a bit more to see if it could be used to enable some of the common scenarios. There are 2 things the metadata plugin has going for it that I think will help it fit into the ASP.NET programming model ...

  • If a jQuery plugin supports the metadata plugin in, you can specify the plugin options on a per element basis by using some JSON within the elements class attribute to specify the option properties you would like to use. To me this really doesn't feel too different than pointing a Toolkit control (using TargetControlID) at an existing web control.
  • It can all be done via markup - no need to emit any extra javascript

Creating the Plugin

Before I show how this can be done using the ListView, I thought I would show what the jQuery plugin looks like (granted it doesn't have nearly as many features as the Toolkit version, but it still very useful for the data grid scenario). Here is how the plugin works ...

  1. Coalesce the default options together with any options that are explicitly provided when the progressbar plugin is applied (Line #5)
  2. Inject the DIV elements that are used for styling the progress bar (Line #9)
  3. Check to see if the metedata plugin is available. If it is override any of the element specific options (Line 12)
  4. Finally, use find to locate the progress_indicator DIV whose background image is set to the progress image (this is applied via the stylesheet). Set the title attribute of this element and animate the width to the specified value. (Lines #16-#20)

Could it get any simpler?

  1. (function($) {
  2.  
  3.     $.fn.progressbar = function(options) {
  4.         // build main options before element iteration
  5.         var opts = $.extend({}, {value:0, tooltip:''}, options);
  6.  
  7.         return this.each(function() {
  8.             //  add the progress DOM structure
  9.             $(this).html('<div class="progress_outer"><div class="progress_inner"><div class="progress_indicator"></div></div></div>');
  10.            
  11.             //  if the metadata plug-in is installed, use it to build the options
  12.             var o = $.metadata ? $.extend({}, opts, $(this).metadata()) : opts;
  13.            
  14.             //  locate the DOM element that contains
  15.             //  the progress image       
  16.             $(this).find('.progress_indicator')
  17.                 //  add the tooltip
  18.                 .attr('title', o.tooltip)           
  19.                 //  and animate the width
  20.                 .animate({width: o.value + '%'}, 'slow');
  21.         });
  22.     };
  23.  
  24. })(jQuery);

Using the jQuery progressbar Plugin with the ListView

Then, I can use databinding expressions to encode the tooltip and value options using the databinding expression. It isn't as pretty, but it defiantly works. All of the magic happens in line #16. And if you set the control to runat server, you could populate this value from the codebehind as well.

  1. <asp:ListView ID="lvWorkItems" runat="server" DataSourceID="ldsWorkItems">
  2.     <LayoutTemplate>
  3.         <table class="yui-grid" cellspacing="0" cellpadding="0">
  4.             <tr class="hdr">
  5.                 <th><asp:LinkButton ID="btnIDSort" runat="server" Text="ID" CommandName="Sort" CommandArgument="ID" /></th>
  6.                 <th><asp:LinkButton ID="LinkButton1" runat="server" Text="Name" CommandName="Sort" CommandArgument="Name" /></th>
  7.                 <th><asp:LinkButton ID="LinkButton2" runat="server" Text="Percent Complete" CommandName="Sort" CommandArgument="PercentComplete" /></th>
  8.             </tr>
  9.             <tr id="itemPlaceholder" runat="server" />
  10.         </table>
  11.     </LayoutTemplate>
  12.     <ItemTemplate>
  13.         <tr class='<%# Container.DataItemIndex % 2 == 0 ? "row" : "altrow" %>'>
  14.             <td><%# Eval("ID") %></td>
  15.             <td><%# Eval("Name") %></td>
  16.             <td><div class='progressBar {value: "<%# Eval("PercentComplete") %>", tooltip:"<%# string.Format("Task {0} is {1}% complete!", Eval("ID"), Eval("PercentComplete")) %>"}'></div></td>
  17.         </tr>
  18.     </ItemTemplate>
  19. </asp:ListView>

Applying the Plugin

And the only remaining bit of awkwardness is that the progressbar plugin needs to be applied twice. Once when the page first loads.

  1. function pageLoad(sender, args){
  2.     if(!args.get_isPartialLoad()){
  3.         //  apply the
  4.         $('.progressBar').progressbar();
  5.     }
  6. }

And then again just after an UpdatePanel is refreshed (I apply it to the panels new contents) ...

  1. Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function(sender, args){
  2.     var updatedPanels = args.get_panelsUpdated();
  3.     if(updatedPanels &amp;&amp; updatedPanels.length > 0){
  4.         for(var i = 0; i < updatedPanels.length; i++) {
  5.              $('.progressBar', updatedPanels[i]).progressbar();
  6.         }
  7.     }                   
  8. });

Footprint

And as promised, here is the JavaScript and CSS footprint for this example. I am using ASP.NET AJAX's ScriptManager and UpdatePanel controls - so those are the axd JavaScript references. The JavaScript for my plugin is ~1 KB unminified.

JavaScript: 113 KB

image

CSS: 2 KB

image

That's it. Enjoy!

glowbuttons - Writing My First jQuery Plugin

Posted on June 26, 2008 by matt@mattberseth.com.
Categories: Contributors.

So I figure what better way to learn more about jQuery than to dig right in and create a plugin or two? At first I thought this sounded pretty intimidating, but after browsing through some of the existing jQuery plugins I figured it might be easy enough to try and 'rearrange' one of the Toolkit controls that I created. So that is what I did with my GlowButtonExtender control I created a few months back (and I plan on doing it with the ProgressBar one as well very soon). I kind of liked this because I thought creating the same UI widget using both the Toolkit and jQuery might give a little more insight into what the relative advantages are between the two frameworks.

In case you didn't see my original post on creating the GlowButtonExtender control, here is how it works: When you mouse over the button, it glows as it comes into focus and of course when the mouse leaves the glow slowly fades away. These are just screen shots so you will have to check out the demo page to see it in action.

Live Demo (IE7, FF and Opera)* | Download

* IETester seems to be on the fritz - so I wasn't able to test in IE6.

image imageimageimageimage

Footprint

Before I jump into the details, I just thought that I would mention that from now on, all of my posts (at least the ones that include a live demo) will include a section titled Footprint that has a brief discussion on the size of JavaScript and CSS resources my post is making use of. I really should have been doing this all along, but better late than never, right? So here it is for this post. I am using minified versions of jQuery and the metadata plugin (58 KB total combined) plus the color plugin (another 4 KB). And finally, the JavaScript for my glowbuttons plugin is a whopping 3 KB un-minified plus another 2 KB for the CSS.

JavaScript: Total 64 KB

image

CSS: Total 2 KB

image

Basic jQuery Plugin Template

So to get started creating my glowbutton plugin I first did some googling and came across a great resource that goes over some of the basics about creating jQuery plugins. I recommend reading (and understanding) the code samples. Below is the shell that I started with for my plugin. I would pay particular attention to the following ...

  • There is a decent amount of plumbing that really needs to be included in your plugin to make sure it can play together nicely with other jQuery plugins as well as other JavaScript libraries
    • Notice the first and last lines. The plugin is wrapped in a self executing function that takes a single parameter '$'. And the last line of code invokes the function passing in the global jQuery object. If you follow this pattern you can safely use the $ shortcut to refer to the jQuery object without worrying about colliding with other bits of JavaScript code that might also be running on the client. If you don't write your plugin using this technique I believe you should not be using the $ shortcut at all (or do at your own peril).
    • Line 5 I am creating the entry point to my glowbuttons plugin and adding it to the $.fn object
    • If you notice in line 5, my plugin accepts a single parameter called options. I am using lines 19 through 24 to define the options my plugin supports as well as the default values for each of the properties. Line 8 examines the options my plugin is provided along with the default option values and fills in any missing options with the defaults. If no options are supplied all of the default values will be used.
    • Line 10 is where we actually start doing something. Here we iterate over each of the elements in the wrapped set and do something with them. Two things are important here. The first is that I am returning this which is a reference to the wrapped set. This allows users to chain calls together creating the nice compact, fluent interface that jQuery is know for. And the second is that where I have the comment 'do something with $(this)' is where the actual plug-in logic goes. We will see that soon.
  1. (function($) {
  2.   //  add our glowbuttons function.  it accepts
  3.   //  a single parameter that specifies any parameters
  4.   //  our plugin supports
  5.   $.fn.glowbuttons = function(options) {
  6.    
  7.     // build main options before element iteration
  8.     var opts = $.extend({}, $.fn.glowbuttons.defaults, options);
  9.    
  10.     return this.each(function() {
  11.  
  12.         //  do something with $(this).
  13.         //  this is where all of the core plug in code belongs
  14.  
  15.     });
  16.   };
  17.  
  18.   // default options - these are used of none others are specified
  19.   $.fn.glowbuttons.defaults = {
  20.     from: '#016bbd', '#b1ddff',
  21.     className: 'blue',
  22.     speed: 1000
  23.   };
  24.  
  25. //  invoke the function we just created passing it
  26. //  the jQuery object
  27. })(jQuery);

Adding the GlowButton Plugin Logic

So after creating the template, I went back to my existing GlowButtonsExtender logic and started moving it over into my plugin. My existing plugin did basically 3 things

  • Injected 2 SPAN's surrounding the button that I am using for styling (border and background)
  • Injected some browser-specific style workarounds
  • Setup 2 Toolkit animations for animating the background color as the mouse enters and leaves the button

And adding this logic into the plugin was really easy. Below I am just showing the part where I iterate over the wrapped set, but you can see that on line 4 I am wrapping the button in 2 spans using the wrap function. Then I navigate up to the immediate parent nodes and apply some browser hacks (I left them out of the code snippet here because they don't add much value). And finally, I attach to the outer most SPAN's hover events and run a simple color animation when the button is hovered over.

  1. return this.each(function() {
  2.     var button = $(this);
  3.     //  inject the parent nodes           
  4.     button.wrap('<span class="glow-button"><span class="inner">');
  5.    
  6.     //  ** do some browser specific style workarounds
  7.    
  8.     button.parent().each(function(){
  9.         var innerWrapper = $(this);
  10.        
  11.         //  ** do some browser specific style workarounds
  12.        
  13.         innerWrapper.parent().each(function(){
  14.        
  15.             //  ** do some browser specific style workarounds
  16.        
  17.         })           
  18.         //  add a class to the outer most node - this helps with theming
  19.         .addClass(o.className)
  20.         //  finally attach to the hover events to run the animation
  21.         .hover(
  22.             function(){
  23.                 $(this).stop();
  24.                 $(this).animate({ backgroundColor: o.to }, o.speed);
  25.             },
  26.             function(){
  27.                 $(this).stop();
  28.                 $(this).animate({ backgroundColor: o.from }, o.speed);
  29.             }
  30.         );                     
  31.     });
  32. });

And now I can do things like this ...

  1. $(document).ready(function(){   
  2.     $('.glow').glowbuttons();
  3. });

... and all of the INPUTs on my page with the glow CSS class will start glowing.

Overriding Options with the Metadata Plugin

And you could stop there if you want and have a pretty useful plugin. But, you can make your plugin even more flexible by adding support for the Metadata plugin. The Metadata plugin allows you to override the option values on a per element basis. So in the code snippet immediately above this I am applying the default options to all of the glow elements on the page. But that isn't always what you want - and that is where the Metadata plugin becomes useful. With this plugin users can override the default options by specifying option values using the class attribute as follows ...

  1. <asp:Button ID="Button1" runat="server" CssClass="glow" Text="Sign Up Now!" />       
  2. <br />
  3. <br />
  4. <asp:Button ID="Button2" runat="server" CssClass="glow {from: '#111111', to: '#555555', className: 'dark'}" Text="Sign Up Now!" />       
  5. <br />
  6. <br />
  7. <asp:Button ID="Button3" runat="server" CssClass="glow {from: '#79B837', to: '#C7EB6E', className: 'green', speed: 500}" Text="Sign Up Now!" />       
  8. <br />
  9. <br />   
  10. <asp:Button ID="Button4" runat="server" CssClass="glow {from: '#9C0063', to: '#D693BD', className: 'purple'}" Text="Sign Up Now!" />

And now we can still fire the same logic on document ready, but now because we are specifying different parameter values on a per element basis, we end up rendering the following buttons.

image

And best of all, to support this all we have to do us update our plugin and add a single line of code. In the snippet below I updated the template to check first check the current element (i.e. $(this)) to see if it has any metadata properties defined. If it does we will honor these values. And if the metadata plugin isn't available, we just fall back and use what ever options are available.

  1. (function($) {
  2.   //  add our glowbuttons function.  it accepts
  3.   //  a single parameter that specifies any parameters
  4.   //  our plugin supports
  5.   $.fn.glowbuttons = function(options) {
  6.    
  7.     // build main options before element iteration
  8.     var opts = $.extend({}, $.fn.glowbuttons.defaults, options);
  9.    
  10.     return this.each(function() {
  11.         //  if the metadata plug-in is installed, use it to build the options
  12.         var o = $.metadata ? $.extend({}, opts, $(this).metadata()) : opts;
  13.        
  14.         //  ... plugin logic
  15.        
  16.     });
  17.   };
  18.  
  19.   // default options - these are used of none others are specified
  20.   $.fn.glowbuttons.defaults = {
  21.     from: '#016bbd',
  22.   '#b1ddff',
  23.     className: 'blue',
  24.     speed: 1000
  25.   };
  26.  
  27. //  invoke the function we just created passing it
  28. //  the jQuery object
  29. })(jQuery);

And finally, here is the complete source code for the plugin.

  1. (function($) {
  2.   $.fn.glowbuttons = function(options) {
  3.     // build main options before element iteration
  4.     var opts = $.extend({}, $.fn.glowbuttons.defaults, options);
  5.    
  6.     return this.each(function() {
  7.         var button = $(this);
  8.         //  if the metadata plug-in is installed, use it to build the options
  9.         var o = $.metadata ? $.extend({}, opts, button.metadata()) : opts;
  10.         //  inject the parent nodes           
  11.         button.wrap('<span class="glow-button"><span class="inner">');
  12.         //  ie display workaround
  13.         button.css('display', $.browser.msie ? 'inline-block' : 'block');
  14.        
  15.         button.parent().each(function(){
  16.             var innerWrapper = $(this);
  17.            
  18.             //  more browser specific workarounds   
  19.             innerWrapper.css('display', $.browser.msie ? 'inline-block' : 'block');
  20.             if($.browser.msie) {
  21.                 innerWrapper.css({ 'position':'relative', 'left':'-1px' });
  22.             }
  23.            
  24.             innerWrapper.parent().each(function(){
  25.                 var outerWrapper = $(this);
  26.                 outerWrapper.css('display', $.browser.mozilla ? '-moz-inline-box' : 'inline-block');
  27.                 outerWrapper.css('backgroundColor', o.from);
  28.                
  29.                 //  our glossy image is a transparent PNG so
  30.                 //  we have a special class that uses an image filter
  31.                 if($.browser.msie &amp;&amp; $.browser.version < 7) {
  32.                     outerWrapper.addClass('ie6');   
  33.                 }                     
  34.             })
  35.             //  add a class to the outer most node - this helps with theming
  36.             .addClass(o.className)
  37.             //  finally attach to the hover events to run the animation
  38.             .hover(
  39.                 function(){
  40.                     $(this).stop();
  41.                     $(this).animate({ backgroundColor: o.to }, o.speed);
  42.                 },
  43.                 function(){
  44.                     $(this).stop();
  45.                     $(this).animate({ backgroundColor: o.from }, o.speed);
  46.                 }
  47.            );                     
  48.         });
  49.     });
  50.   };
  51.  
  52.   $.fn.glowbuttons.defaults = {
  53.     from: '#016bbd',
  54.   '#b1ddff',
  55.     className: 'blue',
  56.     speed: 1000
  57.   };
  58.  
  59. })(jQuery);

What am I doing to learn more about jQuery?

So far I have found jQuery pretty easy to learn. But I have also only just started so I feel like I have a lot of catching up to do. Here is what I am doing to learn more about jQuery ...

  • Take Scott Hanselman's advice and become a better dev by reading more code. The existing jQuery plugins are pretty readable. I think once you get past the seemingly goofy JavaScript syntax for controlling scoping the rest is pretty straight forward. Browse the plugins and see how they work. I think you might be surprised at how simple they are (I know I was)
  • Subscribe to jQuery's discussion board over on Google Groups.
  • Pick up a JavaScript reference book
  • Check out learningjquery.com it has some great information. Plus I was over there today and stumbled onto a comment that Martin Fowler made (obviously I don't know that it was him for sure, but you never know).
  • Both Dave Ward and Rick Strahl blog fairly regularly on using jQuery with ASP.NET. So if you haven't yet, subscribe to their feeds. (here and here)

That;s it. Enjoy!

AjaxControlToolkit Script Browser

Posted on June 23, 2008 by matt@mattberseth.com.
Categories: Contributors.

When creating my last post I tracked down all of the JavaScript files the ModalPopup Toolkit control loads.  I did this manually by using FireBug to see what script files were pushed out to the client and then I used Reflector to find out the size of the file (the Toolkit embeds the JavaScript files as resources).  That was tedious.  But I found the information pretty useful (I wouldn't have guessed the ModalPopup required the RoundedCorners script) and so I thought it might be useful to view this information for all of the Toolkit controls.

So I cooked up a simple console application that rips through the Toolkit's assembly (I used version 1.0.20229.0) and tracks down all of the ClientScriptResources a particular control is using.  As I identified the scripts I ran then each through the minify -> gzip process so I could better gauge how large these scripts are when compared to jQuery and some of its plug-ins.  A few things to note ...

  • Unless I am mistaken, the Toolkit scripts were minified before they were embedded into the Toolkit's assembly.  I don't see any comments in the source code or anything so I am assuming this is correct.  Does anyone know what tool was used here?
  • I also downloaded the YUI Compressor (another minifier) to see how much better it could do than what comes by default
  • I did the same with JSMin
  • Finally to simulate HTTP compression, I gzipped all of the minified files to get a closer approximation to the size of the file that is actually transmitted

Below is what it looks like (I fit the output into one of my grouping grid's).  So you can browse the Toolkit controls and check out both how many scripts the control depends on and what the overall compressed size of the script is (i.e. the ModalPopup compressed is only 22 KB - much smaller than the original 102 KB).

Live Demo | Download

image

That's it.  Enjoy!

Master-Detail with the GridView, DetailsView and jQuery's ThickBox

Posted on June 22, 2008 by matt@mattberseth.com.
Categories: ASP.NET, Contributors.

And so I continue on messing around with jQuery and looking into ways I can use it in my WebForms applications.  Something I sheepishly admitted to in my last post was that I don't want to use 3 client side libraries {ASP.NET AJAX, AjaxControlToolkit, jQuery}, yet I never really took the time to see what the size of the core jQuery library is.  And for that matter I haven't really looked at what scripts some of my favorite AjaxControlToolkit controls are pulling down either.  I also assumed there is a bunch of overlap between jQuery and ASP.NET AJAX, but I haven't looked into that either. 

So I thought I would check some of this out and along the way rebuild my Master-Detail with the GridView, DetailView and ModalPopup Controls and replace the ModalPopup with jQuery's ThickBox.  I found it pretty interesting - read on to see how it went.

Live Demo | Download

ThickBox Demo

What I Was Looking For

While building this sample I decided I would keep my eye out for the following things ...

  1. Size Comparison (minified, but not gzipped) of AjaxControlToolkit's ModalPopup versus jQuery's ThickBox
  2. Installation / Configuration 
  3. Programming Model / API 
  4. ASP.NET AJAX Integration 

 

jQuery's ThickBox

If you are not familiar with jQuery's ThickBox = here the 2 sentence description taken from the ThickBox home page ...

ThickBox is a webpage UI dialog widget written in JavaScript on top of the jQuery library.  Its function is to show a single image, multiple images, inline content, iframed content, or a content server through AJAX in a hybrid model

Size Comparison

This is what I was most interested in.  I get a lot of comments to my AjaxControlToolkit posts that comment on how large the client side footprint of the Toolkit is.  And I honestly don't really know how to respond - mostly because I don't have a baseline that I can compare it to.  And jQuery's ThickBox and the Toolkit's ModalPopup is not exactly like comparing apples to apples, but I think it is close enough to get a feeling for things.

So here are the numbers ...

 FileSize (Compressed)
ThickBox  
 jQuery.js55KB
 ThickBox.js/ThickBox.css10KB
Total 65KB
ModalPopup  
 Common.js28KB
 BaseScripts.js11KB
 RoundedCornersBehavior.js27KB
 Timer.js1KB
 DropShadowBehavior.js7KB
 DynamicPopulateBehavior.js6KB
 DragDropScripts.js23KB
 DynamicPopulateBehavior.js6KB
 Floatingehavior.js4KB
 ModalPopupBehavior.js18KB
Total 131KB

 

The ModalPopup and its supporting scripts is just over twice the size of the jQuery plus ThickBox com