原文:http://mattberseth.com/blog/2008/06/getting_jquery_goodness_into_a_1.html#comment-23799
So clearly I am obsessing about getting jQuery features into my WebForm applications. I would seriously love to see the following added ...
- A more powerful selector. Finding elements by ID (ala $get) in an ASP.NET page is usually pretty awkward because the ID values isn't known until runtime. This alone causes many people problems. Unless I am mistaken the next version of ASP.NET AJAX will include a selector to find items by class name as well, but I have not heard anything for supporting more advanced stuff like ...
1: // good ...
2: var alternatingRows = $select('TABLE TR.alt');
3:
4: // better ...
5: var alternatingRows = $select('TABLE TR:odd');
6:
7: // best!
8: var alternatingRows = $select('TABLE TR:nth-child(odd)');
- A more fluent programming model. jQuery seriously embraces the 'find some elements and do something with them' mantra. And they make it really simple to do - if you want a zebra striped table that also supports the row hover effects, all you need to do is the following ...
1: // add the row hover highlighting
2: $("TABLE TR")
3: .mouseover(function() {$(this).addClass("highlight");})
4: .mouseout(function() {$(this).removeClass("highlight");});
5:
6: // add the zebra striping
7: $("TABLE TR:odd").addClass("odd");
8: $("TABLE TR:even").addClass("even");
- JavaScript only plug-ins. There is something like 6.5 Bazillion jQuery plugins. Seven new ones have already been created since you last checked your email. I have to believe that part of the reason there are so many of these floating around is because they are so darn easy to create and distribute. Plus if you crack some of them open it is almost sickening how simple they are. As a matter fact you could create a zebraStripedPlusRowHighlighting plug-in from the above code snippet and then just do the following to apply it to all of your TABLE elements
1: // add zebra striping and highlighing to all my TABLE's
2: $('TABLE').zebraStripedPlusRowHighlighting({'hoverClass':'highlight', 'oddClass':'odd', 'evenClass','even'});
So by this point you might think I get a kick-back or something from jQuery for pushing this stuff so hard, but truly I am not. Its just that I get a little jealous browsing the jQuery plug-in page and seeing all of the cool stuff that I can't use.
And so I laid out my approach to bridge this gap between ASP.NET AJAX and jQuery. My first thought was to simply port some of the most useful jQuery features over into some sort of JavaScript shim. But after thinking about this I am not sure if it this is so great either. Play this forward: say I was able to get a large number of the jQuery features into the shim - what does this really get me? I still can't use any of the jQuery plug-ins without porting them over too. And I would think it would be practically impossible to ever get to a point where there is feature parity between my shim module and jQuery. And suppose I could reach that point - this new extension library would be most likely just as big as jQuery is in the first place. Which was one of the reasons I stated previously for not using jQuery - I didn't want the bloat of 3 JavaScript libraries {ASP.NET AJAX, AjaxControlToolkit, jQuery}.
So I think I need to do some more work here thinking this through.
- I am sort of ashamed to say this, but I never really looked into what kind of overlap there is between the 3 libraries. I assume there is quite a bit between XmlHttpRequest, Animation, DOM manipulation, Browser detection, selectors, data types, helper functions, etc ... But I have not actually looked into seeing what exactly this overlap is and if there would be a way to minimize some of it. Say if I could get jQuery selector without any of the other stuff ... jQuery lite?
All this being said, I did take a look at some of the jQuery programming model fundamentals just because I was curious. And I learned more than a few things so I thought I would pass them along*.
* disclaimer: I am pretty new to jQuery so none of this stuff is gospel and correct me if you see something I missed.
The Wrapped Set
A core piece of the jQuery programming model is what is referred to as the wrapped set. This wrapped set is the collection of DOM elements that you wish to execute some operation (I believe jQuery refers to these as commands) on. jQuery's powerful selector returns a wrapped set that contains all of the DOM elements that match the provided selector. Once you have a reference to a wrapped set, you can execute some operation upon all elements within the set. So you can easily do stuff like this ...
1: // add the grid css class to all TABLE elements
2: $('TABLE').addClass('grid');
3:
4: // show the id of the INPUT element when it is clicked
5: $('INPUT').click(function(){ alert($(this).id); });
Chaining
The wrapped set contains a ton of useful methods. And most of them return the same value - the reference to the wrapped set. This allows you to chain commands together keeping the code very concise yet still very readable. The following example of adding highlighting to a TABLE uses this chaining.
1: // example that shows how calls are chained together
2: $('TABLE TR')
3: .mouseover(function(){ $(this).addClass('highlight');})
4: .mouseout(function(){ $(this).removeClass('highlight'); })
Updating my Shim
So, to continue my experiment of trying to get some of this stuff into my WebForms apps, I made the following changes to plug-ins I discussed in my previous post:
- I created a JavaScript class that is a stripped down version of jQuery's. It contains a collection of DOM elements and contains methods like ..
- add, get, remove, clear, each -> basic collection operations
- addClass, removeClass -> basic DOM operations
- blur, focus, mousedown, mouseup, etc... -> shortcuts for adding event handlers
- After I created the wrapped set class, I updated the $select function to return an instance of this wrapped set that contains all of the elements that matched the selector. I also renamed $select to $majax. So now you can do things like this ...
1: // attach all of the textbox elements and
2: // and apply the focus class when the textbox
3: // obtains the focus
4: $majax('textbox')
5: .focus(function(){ $majax(this).addClass('focus'); })
6: .blur(function(){ $majax(this).removeClass('focus'); });
Which would turn the background of all of the DOM elements that have the textbox css class to yellow (via the focus css class) when they have focus. It would look something like this ...
- And finally, I changed the plug-ins to use my wrapped set prototype to add their commands to instead of the Sys.UI.Control class. This made a nice difference in how I was able to apply my plug-ins to a collection of DOM elements. Checkout the OLD and NEW syntax examples below.
1: // OLD:2: $select('#roundedPanel1, #roundedPanel2').each(function(e){3: // add the rounded corners (6 pixel radius) to the panels4: e.round(6);
5: });
6:
7: // NEW:8: $majax('#roundedPanel1, #roundedPanel2').round(6);
And here is how the new RoundedCorners plugin is defined. The main changes are round function is attached to the elementSet prototype (this is the wrapped set class), the _round function operates on all elements in the current set and the _round function returns a reference to the wrapped set - allowing chaining.
1: Type.registerNamespace("majax");2:
3: majax.RoundedCorners = function() {4: majax.RoundedCorners.initializeBase(this);5: }
6:
7: majax.RoundedCorners.prototype = {
8:
9: initialize : function() {10: majax.RoundedCorners.callBaseMethod(this, 'initialize');11:
12: // attach the plugin to the elementSet class13: majax.elementSet.prototype.round = this._round;14: },
15:
16:
17: dispose : function() {18:
19: majax.RoundedCorners.callBaseMethod(this, 'dispose');20: },
21:
22: _round : function(radius) {23: // create rounded corners for each of the elements24: // in the collection25: this.each(function(){26: $create(AjaxControlToolkit.RoundedCornersBehavior, {"Radius":radius}, null, null, this);27: });
28:
29: // for chaining30: return this;31: }
32: }
33:
34: // register the class35: majax.RoundedCorners.registerClass('majax.RoundedCorners', Sys.Component);36:
37: // create the singleton38: $create(majax.RoundedCorners, null, null, null);39:
40: // Since this script is not loaded by System.Web.Handlers.ScriptResourceHandler41: // invoke Sys.Application.notifyScriptLoaded to notify ScriptManager42: // that this is the end of the script.43: if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();
Where to Next?
Good question. So far the small jQuery-ish shim I created seems useful. Its 200 LOC right now its pretty small, but if I continue down this path it will no doubt grow quickly. So I am not really sure where I am going next - but I don't feel like giving up either.
How About You?
What about you? Are you a WebForms developer that is crushing over jQuery too? Do you have a problem using 3 JavaScript libraries - ASP.NET AJAX, the Toolkit and jQuery? How do you think this problem (if there is one) should be solved?
That's it. Enjoy!