Archive for the 'jQuery' Category


jQuery Validation and ASP.Net Web Forms 0

I’ve been dark for a while, but there has been a lot in the works. Hopefully there will be a flood of posts coming, most with a happier outcome than this one; however, to get back in the swing of things, I’ll take make it short and sweet.

I get really frustrated with ASP.Net Web Forms

We all know and love jQuery. It is an amazing JavaScript framework. It’s made it possible, for even the staunchest back-end developers, to get excited about writing UI. ASP.Net is the bane of jQuery enjoyment. I’ve covered this in jQuery Selectors and ASP.Net Controls. I have a new frustration: the giant HTML Form ASP.Net puts in the Web Form page.

There is an amazing jQuery validation plugin. It can do just about any validation you’ll need with minimal effort. If you want to validate a single form, it’s great; however, if you need to validate only a portion of the form, it’s a pain.

Since ASP.Net sticks its own <form> tag on the page by default, you’re pretty much screwed. The only option I’ve found so far is to go through each element, running a validation on the individual elements.

Here is an example of what I’m doing:

$().ready(function(){

	$("form").validate();	

	$("#firstname").rules("add", {
	 	minlength: 2,
		required:  true
	});

	$("#submit").click(function(){
		var valid = true;
		valid = valid && $("form").validate().element("#firstname");
		alert(valid);
   	});
});

I hope to find a better solution soon, but this will do for now.

Animating Partial Postbacks and UpdatePanels with jQuery 4

Update panels make it really easy to do pseudo ajaxish stuff with little or no extra effort. Wrap your controls in a an UpdatePanel, pay for the entire page to post back and go through its life-cycle and you’re pretty much there. I’ve already written a bit on the extra steps you’ll need if you’re doing some jQuery magic inside that UpdatePanel. It’s not difficult and easy enough to either delegate up the chain to something outside the UpdatePanel (e.g. event delegation) or rinse and repeat the jQuery after the partial post back is complete (e.g. add_endRequest).

What would be really slick-o-matic is if we could do some animation to transition the partial postback so it wouldn’t just explode with new content in our face. Now granted, that may be an overstatement. The content changes usually aren’t drastic or very distracting to but let’s add a bit of polish.

Keep in mind this is a first draft, done in about 30 minutes after an idea landed in my head at 3am. It still needs some polishing, but the major hurdles are taken care of.

For our example, we’ll have a simple Div that will be worked upon in a partial postback. Our goal will be to gently transition the display of that work to the user. Something like this.

Original Video – More videos at TinyPic

We’ll start off with some basic styles to help illustrate the change. We’ll change the content of the Div and also the class.

	#mydiv{ width:200px; border:solid 1px black; font-size:xx-large; }
	.myclass0 { height:200px; background-color:Blue; }
	.myclass1 { height:100px; background-color:Red; }
	.myclass2 { height:150px; background-color:Lime; }
	.myclass3 { height:50px; background-color:Purple; color:White; }

Nothing too exciting, but our partial postback will give us some new dimensions and style. It’ll get the point across.

Next we need some HTML.

<form id="form1" runat="server">
	<asp:ScriptManager ID="ScriptManager1" runat="server">
	</asp:ScriptManager>
	<div>
		Original time: <%= DateTime.Now.ToLongTimeString() %> 
		<asp:UpdatePanel ID="UpdatePanel1" runat="server">
			<ContentTemplate>
				<asp:Button runat="server" Text="Button" ID="MyButton"
						OnClick="MyButtonClick"
					OnClientClick="return ClientClick(this,'#mydiv'); " />
				<div id="mydiv" runat="server">
					<asp:Label runat="server" Text="Label"
                                           ID="MyLabel"></asp:Label>
				</div>
			</ContentTemplate>
		</asp:UpdatePanel>
	</div>
</form>

The magic is going to live in the ClientClick event handler, but forget about that for now. The server side OnClick event will do some trivial work.

 protected void MyButtonClick(object sender, EventArgs e)
 {
	 MyLabel.Text = DateTime.Now.ToLongTimeString();
	 var c = "myclass" + DateTime.Now.Millisecond % 4;
	 mydiv.Attributes.Add("class",c);
 }

This could just as easily toggled a view from edit mode to read-only, expanded a detailed area, etc. Here, we are just picking one of four css classese to use on the div and also giving a label the current time. Like I said this isn’t the cool part.

The final steps are to do the following:

  • Get the jQuery animation to happen on the click of the button.
  • Get the partial postback to wait until the animation is done.
  • Get the DOM to appear in the pre-animation state when it comes back from the partial postback.
  • Get the animation to happen once we have the new page ready to go.

The function needs to know what needs to be animated upon. I’ve been using slide transitions, but the animation is something I think can very easily be abstracted out. We make some quick tests to see if we are in the process of animating or if the animation has been completed. I struggled with how to delay the button click’s post back from happening until the animation is complete and finally decided to just let it happen a second time, but to flag the second click to allow it to continue posting back. I’m using the jQuery data feature to keep some state about the button.

If we are currently sliding, then there is nothing left to do, and if we are done sliding then we can skip the other steps and continue the postback.

if (d.data("isSliding")) return false;
if (d.data("doneSliding")) return true;

If we don’t meet those conditions, we need to slide. We set our flag, wire up the end request handler and the callback to the slide method and kick off the slide.

The slide method is the simplest.

d.slideUp(500,
	function() {
		d.data("isSliding", false);
		d.data("doneSliding", true);
		$(sender).click();
	});

Slide our control up, and when we’re done, set some flags and kick off the click a second time. With the flags set, we’ll fire off the postback. Nothing really fancy here either. Notice we can still reference d here. We don’t need to find it again.

The endRequest handler looks a bit more involved.

var prm = Sys.WebForms.PageRequestManager.getInstance();

var f = function(s, e) {
	d = $(whatToSlideDown);
	d.css("display", "none");
	d.slideUp(0);
	d.slideDown(500);
	prm.remove_endRequest(f);
}

prm.add_endRequest(f);

Here, inside the function we do need to ask jQuery to find what we want again. The DOM has been changed and the old d we created from $(whatToSlideUp) probably isn’t there anymore. In addition, we need to set the element in the state we left the old element. In this case, we want the display to be set to “none”. This allows the slideDown to work just like we would expect following a slideUp.

This is part of the setup that I don’t like. I’ve tried quickly running a slideUp(0) on the the new elements, but I was seeing a flash of the default state before getting the slide up. This is something that I would like to factor out. The animation, including this default state should be a parameter to the function.

Here is a look at the function as a whole. It’s a stupid function name I know, but that is easily changed.

function ClientClick(sender,whatToSlideUp,whatToSlideDown) {

	if (!whatToSlideDown)
		whatToSlideDown = whatToSlideUp;

	var d = $(whatToSlideUp);

	if (d.data("isSliding")) return false;
	if (d.data("doneSliding")) return true;

	d.data("isSliding", true);

	var prm = Sys.WebForms.PageRequestManager.getInstance();

	var f = function(s, e) {
		d = $(whatToSlideDown);
		d.css("display", "none");
		d.slideUp(0);
		d.slideDown(500);
		prm.remove_endRequest(f);
	}

	prm.add_endRequest(f);

	d.slideUp(500,
		function() {
			d.data("isSliding", false);
			d.data("doneSliding", true);
			$(sender).click();
		});
	return false;
}

That’s it. There is definitely room to polish this up and make it more flexible. I’ll post back when I get it up and running in a real application so it looks more useful.

Scoping JavaScript with Module Pattern 0

I’ve entered the world of actually writing JavaScript. In the past, usability was really a secondary concern. I need to do a postback to make a button visible, no problem. The screen will flash, but who cares. It’s not like the user is going to stop using my application to do the stack of work on their desk. They have to use it. It’s their only option.

Now that isn’t entirely true. We attempted to make it as usable as possible, but anything that even close to using jQuery was probably just, according to the powers that be, the developers just playing around with cool toys. I’m not sure the value of the user experience fell nicely into a quantifiable metric. I’m beginning to rant, so back to the point.

In my new adventures with jQuery, I’ve been writing a lot of JavaScript. The biggest complaint I’ve had with my JavaScript is that everything is global and can potentially be overwritten by subsequent JavaScript, or I might be overwriting someone else’s functions or variables. My JavaScript files looked like the following.

var foo = "berry";
function doWork(s){
	alert(s+foo);
}

$(function(){doWork("foo")});

I just cross my fingers that no one else has a doWork function or foo variable. There has to be a way to scope this stuff.

Luckily, I found and article that explains exactly that. It explains the Module Pattern and how to use it to limit the scope of JavaScript.

I’ll let you read the article for a more complete description that I could put together, but ultimately, my JavaScript ends up looking like this.

myScript = function(){
  return {
	init:function(){doWork("foo");}
  }
  var foo = "berry";
  function doWork(s){
	alert(s+foo);
  }
}();
$(myScript.init);

In this case, init is the only method that gets exposed. You can’t invoke doWork. Also, by adding the scope of myScript, you can have greater confidence your methods and variables aren’t getting overwritten.

My practice thus far is to have one JavaScript file for each page and user control. The scope of each page’s file is the name of the page, or control. So you could see Login.aspx.js with a scope of MyApp_Login.

So far so good. We’ll see how it goes.

Finding parent elements with jQuery 1.3 0


magnifying monkey man.

I was recently writing a little javascript function that would traverse up the object graph and apply jQuery selectors to pick the correct parent that I needed. It was a pretty basic function, but I had that gut feeling that this is something that I shouldn’t be writing. It has to be already written by someome. 

Luckily, with jQuery 1.3, it’s part of the core library. New in this version is the closest method which will find the closest parent that matches the selector you pass.

ASP.Net Ajax payload size compared to jQuery 0

Let me start off by saying this isn’t scientific, or possibly even accurate, but was pretty shocking at first glance. I wanted to take a look at what the true size of our pages were, so I brought up Chrome’s nice resource inspector and refreshed and average page from our intranet application. It was a bit shocking at first.

image

All that orange is javascript! Now, I know the browser is probably caching that, when it goes through IIS – Cassini won’t cache it—, butit still seems like a pretty big chunk, but it’s probably all the jQuery and jQuery plugins we’re using right?

image

Without the rest of the files, it’s hard to put that into perspective, but trust me ,that is pretty small. What about the plugins?

image

Those are pretty small too. All that javascript has to be somewhere right? What about ASP.Net Ajax?

image

I had to scale the image so it would fit, but there is a lot there. You may have noticed that we are using the min version of the jQuery file. Is the ASP.Net Ajax minified?

NOPE!

I haven’t really looked to see if it is possible to shrink that JS somehow, we’ll see about doing that soon.

jQuery Selectors and ASP.Net Controls 19

As I’ve already mentioned, this is the bane of jQuery within an ASP.Net application. Selecting the control based on its ID becomes difficult with ASP.net Controls and even worse with naming containers. There have been a few methods I’ve used to find the ASP.Net control in the dom.

Injecting the ClientID

$(“#<%= fooTextBox.ClientID %>”)

This is my least favorite, but the most precise. There are no additional elements or attributes to add and you’re guaranteed to get the exact control you’re after. Another bad side effect is the javascript has to be inside the ASPX page. That’s not ideal either, although you could argue that having a small script that just has variables for the control names isn’t that bad.

var fooTextBoxClientID = “<%= fooTextBox.ClientID %>”;

Still icky.

Add Custom Attribute

You could add a custom ID in the markup of the control.

<asp:TextBox runat=”server” id=”fooTextBox” myID=”foo” />

Then select on that custom ID.

$(“input[myID=’foo’]”)

This seems to make the code less DRY, but it works well.

Find With Ending

Since the name you give you .Net control is only prepended with its naming garbage, you can still find your names using the ends with selector.

$(“input[ID$=’fooTextBox’]")

This works well when the controls are assured to appear only once of the form; however, when inside a repeating control such as a ListView or a reusable control, this method falls apart.

Find With Class

I probably use this method more than any other since most of the time I have a parent frame of reference which limits the scope enough that makes my class name unique. That scope limiter also helps with the other methods, but using class names has worked the best for me.

$(“.inputTextBox")

Be careful not to use the CSS class like you would ID and create a CSS class for every element.

Wrap in HTML Elements

Also, you can wrap the control in an HTML element that will provide you full control over its ID.

<span id=”fooText”><asp:TextBox runat=”server” ID=”FooTextBox” /> </span>

Then select it using the child / ancestor selector.

$(“#fooText > input”)

You’re adding unneeded markup here, but you might already have it and just need to add the ID attribute, or select it using the class name.

I hope this helps with some headaches surrounding ASP.Net and jQuery.

Update: Since then, I’ve been using the following method more and more. The only change I’ve made is I’ve started to prefix my control name with the “_” to make sure I’m not picking up any control names that might carry similar ending names.

Like…

$(“input[ID$=’_PhoneTextBox’]")

and…

$(“input[ID$=’_CellPhoneTextBox’]")

Without the “_” both would match the following.

$(“input[ID$=’PhoneTextBox’]")

jQuery and UpdatePanels 6

While using a jQuery plugin to round corners, they started to disappear inside UpdatePanel’s after a partial post back. I found someone else on StackOverflow had the same problem. Luckily, the solution was right there as well.

$(document).ready(function() {
    doReady();

    var prm = Sys.WebForms.PageRequestManager.getInstance();
    prm.add_endRequest(function(s, e) {
        doReady();
    });
});

function doReady() {
    $('.roundedCorners').corners();
}

I’m interested in trying the livequery jQuery plugin, but will go with this for now.

jQuery and Event Delegation Example 0

I wanted to give some examples that actually run. The last post might have been a little abstract and brief.

First, don’t forget to link in the jQuery script.

<script src="jquery-1.2.6.js" type="text/javascript" charset="utf-8"></script>

Some CSS to pretty things up.

	.myContainer{
		width:300px;
		font-family:Arial;
	}

	.fooberry{
		margin:5px;
	}
	.summary{
		background-color:blue;
		color:white;
		padding:3px;
	}

	.details{
		background-color:green;
		color:white;
	}

	.toolbar-details{
		cursor: pointer;
	}

Some HTML to manipulate.

<div class="myContainer">
	<div class="fooberry">
		<div class="summary">
			<div class="summary-text">
				Just a little information.
			</div>
			<div class="toolbar">
				<span class="toolbar-details">details</span> |
				<span>date: 1/3/2009</span>
			</div>
		</div>
		<div class="details">
			More information....
		</div>
	</div>

	<div class="fooberry">
		<div class="summary">
			<div class="summary-text">
				Different information.
			</div>
			<div class="toolbar">
				<span class="toolbar-details">details</span> |
				<span>date: 1/4/2009</span>
			</div>
		</div>
		<div class="details">
			More information....
		</div>
	</div>

</div>

Finally the jQuery.

<script>
	$(document).ready(function(){
		$(".fooberry").click(function(e){
			if($(e.target).is(".toolbar-details")){
				$(this).find(".details").slideToggle(500);
			}
		});
	});
</script>

This layout could probably be adjusted so the problems I mentioned in the previous post aren’t a problem, but let’s just look at the event delegation.

The div with the class “fooberry” contain the physical layout for the items of our list. We use jQuery to attach a click event handler to each “fooberry” div and then use jQuery again to examine the arguments of the event, and yet again to find the div we want to toggle.

I am sure it is possible to attach the event to the “myContainer” div. We would need some additional jQuery since the following line would find all “details” divs.

$(this).find(".details").slideToggle(500);

This happens because the “this” in this statement will now be the “myContainer” div. We could probably write a query to find the parent of the “e.target” who’s class is “fooberry”, and then use that element in place of “this” in the previous statement. This would reduce the overhead of the redundant event handlers, and work with all additional “fooberry” divs added via Ajax.

I hope this is useful for you as it is for me.

Here is the entire HTML.

	<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
	   "http://www.w3.org/TR/html4/strict.dtd">

	<html lang="en">
	<head>

		<script src="jquery-1.2.6.js" type="text/javascript" charset="utf-8"></script>

		<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
		<style>
			.myContainer{
				width:300px;
				font-family:Arial;
			}

			.fooberry{
				margin:5px;
			}
			.summary{
				background-color:#373999;
				color:white;
				padding:3px;
			}

			.details{
				background-color:#689980;
				color:white;
			}

			.toolbar-details{
				cursor: pointer;
			}
		</style>

		<script>
			$(document).ready(function(){

				$(".fooberry").click(function(e){
					if($(e.target).is(".toolbar-details")){
						$(this).find(".details").slideToggle(500);
					}
				});
			});
		</script>

	</head>
	<body>

		<div class="myContainer">
			<div class="fooberry">
				<div class="summary">
					<div class="summary-text">
						Just a little information.
					</div>
					<div class="toolbar">
						<span class="toolbar-details">details</span> |
						<span>date: 1/3/2009</span>
					</div>
				</div>
				<div class="details">
					More information....
				</div>
			</div>

			<div class="fooberry">
				<div class="summary">
					<div class="summary-text">
						Different information.
					</div>
					<div class="toolbar">
						<span class="toolbar-details">details</span> |
						<span>date: 1/4/2009</span>
					</div>
				</div>
				<div class="details">
					More information....
				</div>
			</div>

		</div>

	</body>
	</html>

jQuery and Event Delegation 0

We’re currently implementing a collapsible panel in one of our web pages using the ASP.Net Ajax Collapsible Panel Extender. The problem we are having is the Collapsible Panel Extender requires Control ID’s for the expand, collapse and target controls.

Usually this isn’t a problem, but we want to expand a panel inside a ListView control when a something is clicked outside the ListView. So now the problem is he only way to find the control ID is to do a complicated a string of .Parent and .FindControl("") calls to eventually call the .ClientID of the control we want. This becomes very brittle as the code breaks if the UI changes and some expects a parent to be there, when it’s been moved in he markup. This obviously needs to change.

So now to try it (the first way) in jQuery. It should be very easy right?

	$("#" + buttonID).click(function(){
							$("#" + panelID).slideToggle(500);
						  });

Again, we have the same problem. The buttonID and panelID are not easy to get. We need that nasty ClientID; however, what dropping the ASP.Net controls in favor of old school HTML controls. We would end up with something like this?

	$('#button<%# Eval("myPK") %>').click( //...

Ugh…that will work, but it looks nasty too. Generating Javascript on the fly is cool and all I guess, but I don’t like it here.

Here comes Event Delegation to the rescue. Instead of attaching that click event handler to the actual item, let the event delegate up to a parent container, and catch it there. Then, using jQuery to only peer downward is elegant.

	$(document).ready(function(){
		$(".outerDiv").click(function(e){
			if($(e.target).is(".collapseButton")){
				$(this).find(".collapsePanel").slideToggle(500);
			}
		})
	});

That’s it! That’s awesome! It works every time it appears on the page. Remember, we are inside a ListView so we repeat the need for this several times. We don’t care what is between the element with the “outerDiv” CSS class and the other elements because jQuery takes care of finding them. We’re no longer brittle! We don’t need the control ID’s because we only care about the CSS classes now. No more Control ID’s! We no longer have difficulty finding out second cousin twice removed element because we found a common ancestor and look down from there. We don’t need to assign event handles for each button. Sweet! It is a win/win/win.

Welcome to jQuery 2

My first task at my new organization was to verify text being added to a freeform text field is not already associated to a different record of the same type. This would be similar to checking if a username is taken before completing a registration process. As you have probably encountered with the registration process, the typical way to complete this test is to wait for the user to click save button and then test the database for the existing record. It would be a lot nicer if the test would happen automatically as you type, so this is what I set out to do.

Doing this with ASP.Net AJAX would probably be possible, but given we are starting to invest in jQuery, I took this as a great opportunity to learn something new, while still delivering a great user experience. While working through some quick and dirty jQuery examples, I quickly see its potential. It feels nice to be in a dynamic language for a change.

My first task was to fire off a function after a certain amount of time elapses without the user typing into the field. I know Javascript provides a timer mechanism, but I’m hoping jQuery provides a nice abstract to it…and it does. I’m able to find my text box and attach my timers. It was when I first tried to move this out of my static html proof of concept into an ASP.Net application hat I first realized the bane of ASP.Net when using jQuery, control id’s.

All of the examples in jQuery have really nice, hand crafted control id’s.

	$("#myTextbox").keypress(function(){
		$("#myTextbox").oneTime(1000,"mytimer",function(){alert("times up");});
	});

That’s great, but my text box is inside a user control, inside another user control, inside a page. I don’t get id = "myTextbox". I get id = "ctl0__ctl1_ctl1_etc_myTexbox", which makes the selector a little more difficult to write. Luckily, we can use some nasty escaped ASPX code to handle that. I’m not sure this is the best way to handle it, but it is working for now.

	<script type="javascript">
		var controlName = "<%= myTextbox.ClientID %>";
		$("#" + controlName).keypress(function(){"pressed it");});
	</script>

On occasion, the client id is not as easy to come across. For example, in my particular case, I am trying to select a text box inside the InsertItemTemplate of a ListView.

<asp:ListView ID="ListView1" runat="server">
    <!-- ... -->
    <InsertItemTemplate>
        <asp:TextBox ID="myTextbox" runat="server" />
        <script language="javascript" type="text/javascript">
            var controlName = '<%= ListView1.InsertItem.FindControl("myTextbox"); %>';
        </script>
    </InsertItemTemplate>
</asp:ListView>

…OK. I’ve learned not to be too ambitious with my blogs post. I’ll be contributing in smaller chunks form now on.