Saturday, September 06, 2008

WOA vs SOA...The Battle of the "Oriented Architectures"

Everybody Loves Web Oriented Architecture

Hmmm...interesting read indeed. While this isn't the party line for my employer, I think WOA is the way to go personally. It just seems lighter weight to me and more "agile", which of course I like. Then again, a lot of this stuff is all semantics anyway! ;-)

Labels:

permalink | leave a comment

Monday, September 01, 2008

Domino Web Developers...Will Our Apps Be Better In New Google Browser?

Exciting times, eh? Tomorrow, Google is releasing "Google Chrome", a new, built-from-the-ground-up web browser that promises to be a modern platform for applications and web pages.

With announcements like this, plus all the new goodies like xPages coming from IBM, you really do need to start brushing up on your web skills. While I *love* rich client development, I believe it's equally important to be able to use your skills when developing for the browser. I've met far too many Domino developers who have said they don't do web. Believe me...you want to start studying up on your html, css and javascript. You won't be disappointed and they are skills that will serve you well outside of Lotus land too.

Labels: , ,

permalink | leave a comment

Monday, April 28, 2008

Unobtrusive JavaScript...You Should Totally Do This

In my JavaScript session at last year's version of LotusDeveloper (an excellent conference, by the way...you should go), I discussed the concept of unobtrusive javascript and showed several examples. Just as CSS was a blessing to us as we started to shift to the mindset of separating presentation from content (not so hard for us Notes folks really!), so is unobtrusive javascript the next stage of evolution. It's about separating behavior from content. If we can take out all the formatting junk from our HTML, can't we take all the javascript out as well? The answer is a resounding "yes" and it's a great ideal to strive for.

I haven't talked a whole lot about web design subjects here, since I thought the niche of focusing on the Notes client was more important, but this is a subject that I might just tackle more in future installments. In the meantime, there are a lot of good resources out there to bring you up to speed on the concept. I'm not really sure if this practice has caught on in the Domino world, or even how prevalent it really is in the broader world of web design, but it's a technique that I've found has a lot of merit.

To get you started, here's a nice little example. It's very simple, but walks you through the process of making a page of unobtrusive script. Check it out and see what you think. I'm interested if it will change the way you approach your code.

Labels:

permalink | leave a comment

Monday, November 26, 2007

Rounded Corners Are Nice

Quick tool tip of the day: If you have a (ahem) rounded corner fetish but aren't so handy with Photoshop or Paint Shop Pro, there's an online tool that will allow you to quickly add rounded corners to any photo or graphic.

Just head to RoundPic.com and either select a photo from your computer or enter in a URL. Click the "Round It!" button and your anti-aliased, rounded corner image will magically appear. You can even select the amount of rounding you want and the background color that will define the space "behind" the image. A downloadable image is then made available for your use. A perfect solution from a website that focuses on one common problem. Enjoy!

Labels: ,

permalink | leave a comment

Saturday, November 18, 2006

Fun With The DOM...Playing With The Action Bar

I promised some more technical articles awhile back and haven't delivered on that promise yet, so...here you go. I'm going to stray from my Notes client focus this time to talk about web development. In this post, I'm going to spend a little time dissecting the Domino-generated action bar as it is represented in the Document Object Model (DOM). While many blog authors have focused on ways to modify the look and feel of the action bar, if you utilize the power of the DOM, you can do much more. Like what, you may ask? How about:

Generating a right-click menu...



...or generating a dynamic drop-down menu...



...or whatever you can think up. Basically, if you know how to utilize the DOM to get a reference to the action bar object, you are limited only by your imagination. I'm sure smarter people than I can come up with even better techniques. But, since this is my blog, I'll give it the old college try and explain things as best I can.

My idea when putting this technique together was to allow the Domino developer coming from a Notes client background to write action buttons as they always do...no need to write special code for the web. Of course, it's my opinion that you should learn as much as you can about web techniques. When you do, you'll find yourself relying less and less on Domino-generated code. However, there's something quite satisfying about whipping out a line or two of formula language and having the code execute in the browser. If you think so too, read on.

When Domino generates the action bar (on a form, view, page, whatever), the underlying HTML that is created is a table. Each action button that exists on the design element is housed in its own table cell. To see this in action, just view the source (in your browser) of any page that has an action bar. Let's take an example view with three action buttons: "Expand", "Collapse" and "Create New Document". For now, the code behind these buttons is unimportant. What we care about is the HTML output. In this example, it would look something like this:


<table border="1" cellspacing="2" cellpadding="2">
<tr valign="middle">
<td><a onclick="showHide('descRow', 1) return false;" href="">Expand</a></td>
<td><a onclick="showHide('descRow', 0) return false;" href="">Collapse</a></td>
<td><a href="/DHTML2006.nsf/New?OpenForm" target="_self">Create New Document</a></td>
</tr>
</table>


As you can see, Domino creates a table with a single row and then generates three <td>s...one for each action. So far, so good.


Representations in the DOM

One thing that I've found when working with other Domino developers is although they have probably used techniques that manipulate the DOM in their applications, many don't really know about the DOM or understand what its about. Here's my (probably oversimplified) version: The Document Object Model describes an HTML document in a tree form. It basically allows you to use an object-oriented methodology to parse HTML and to operate on the contents of that HTML. When a page loads in your browser, the browser creates a hierarchical representation of all of the HTML elements. This hierarchical representation is often called the Document Tree. The document tree consists of "nodes", which are basically objects that have properties and methods. There are several types of nodes in the DOM, but usually we work with either element nodes or text nodes. An element node is a node on the Document Tree that was created by an individual HTML tag. For example, a <div> element in your code will create an element node branch of the tree. A text node is a node that is created by some general content...usually text. It's interesting to note that nodes can have many relationships since the DOM is hierarchical in nature. An element node can have child nodes, for example, or siblings, a parent, etc.

Let's take a look at a simple example that might explain this a bit better.

Assume we have a standard unordered list describing what happens at a typical rock concert from the perspective of the roadies.


  • Sound Check
  • Main Event
  • Break Down


In HTML, this is written as

<ul>
<li>Sound Check
<li>Main Event
<li>Break Down
</ul>


Represented as a document tree, it would look something like this:



The opening <ul> tag creates a new branch in the document tree...a new element node. This element node has three children, the <li> elements. These in turn have child elements as well...the text nodes that represent the content within the <li> tags ("Sound Check", "Main Event" and "Breakdown"). Pretty cool, yes?

Finding objects within the document tree is a matter of traversing the various nodes, using the relationship between elements. For example, if I have an object representing the first <li> element (let's say it has an ID of "node0"), then if I want to retrieve its parent, I would write node0.parentNode, which would give me an object representing the <ul> tag. If I wanted to get the text inside the middle <li> tag in our concert example, I could traverse the tree like so:


//Line 1 (assuming in our example that we want the first <ul> on the page)

var theList = document.getElementsByTagName('ul')[0];

//Line 2 (this would result in an alert box with the text "Main Event")

alert(theList.childNodes[1].firstChild.nodeValue);





Let's break this down:

Line 1 uses getElementsByTagName to return an array of objects representing all the <ul> tags on the page. We want the first <ul>, so we use subscript 0 to get the first object in the array.

Line 2 uses some of the node relationships to get what we want. childNodes returns an array of all the children of a given element node, while firstChild returns just that...the first child node. Thus, we are getting the second child of theList (the second <li> tag) and then the first child of that element (the text node that holds the content we want). Finally, nodeValue returns the actual text inside the node (i.e. "Main Event").

I don't want to go too deep here, so suffice it to say that there are many of these node relationships available (firstChild, lastChild, nextSibling, etc.) and it is quite worthwhile to learn them.

You can see from this fairly simple example that the document tree can get complex very quickly. A far easier way to traverse the tree is to use an element's ID attribute. You've probably done this before. The JavaScript to get a particular object is simply document.getElementById('idvalue'), where 'idvalue' is the ID attribute of the object in question. It is highly recommended that you give your elements ID attributes...it will make your life much easier. Going back to the simple example, let's put some IDs in and re-write the code.


<ul ID="concertList">
<li ID="firstPhase">Sound Check
<li ID="secondPhase">Main Event
<li ID="thirdPhase">Break Down
</ul>


Now to get the text in the middle <li>, we could simplify our code to:


alert(document.getElementById('secondPhase').firstChild.nodeValue);


Ah...much cleaner!

In addition to element nodes and text nodes, there is a special kind of node known as an attribute node. An attribute node is not really a part of the document tree, but it does provide a handy way of getting at attributes for a given element node. If you wanted to change the 'href' attribute for a link on a page from www.google.com to www.yahoo.com, you could do this by manipulating the attribute node of the <a> object. Assume the link had an ID of "thelink". The JavaScript to do this would then be: document.getElementById('thelink').setAttribute('href', 'http://www.yahoo.com'). You can manipulate attributes using:

setAttribute()
createAttribute()
getAttribute()
removeAttribute()
etc.

Again, I'd recommend looking into these functions in more detail...


Aside: To learn more about the DOM, check out the following great book:

Stuart Landridge, DHTML Utopia: Modern Web Design Using JavaScript & DOM (SitePoint, 2005).


OK...at this point you're thinking..."Great...thanks for all the boring DOM mumbo jumbo...on with the good stuff, geek boy!" I promise that this is important, though, since it will make manipulating the action bar seem really easy. So let's get to it, shall we?

As I mentioned at the top of the post, the Domino translation engine outputs the action bar as a table and it is always the first table on the page. So the first thing we want to do when manipulating the action bar is to get this object.


var actionBarTable = document.getElementsByTagName('Table')[0];


Next, we want to get all the <a> elements, since these hold the actual code to execute and the labels for the action buttons. Again, we'll use getElementsByTagName to return an array of all the <a> elements that are within the table.


var actionAnchors = actionBarTable.getElementsByTagName('a');


Now the fun begins...and this is where your imagination can come up with some great uses for the objects we have in the actionAnchors array. Usually, you'll iterate through the objects in this array to pull out the onClick events or href attributes and also grab the labels. So...how could you get information from the first action button?

To get the label, you would traverse the DOM:


actionAnchors[0].firstChild.nodeValue //remember...the <a> tag creates an element node and it has a child node which is the label text


To determine what the action actually does, you can check if it has an onClick event (if you coded your action button with JavaScript, then this will be true). If it doesn't, then it will have an href attribute (usually used to trigger an @Formula).


if (actionAnchors[0].onClick) {
do something with the onClick attribute;
}
else {
do something with actionAnchors[0].href;
}



Now, it's time to put these ideas together into a concrete example. Here's a sample database you can use if you want to follow along.

We are going to use the action bar generated by Domino to create a simple CSS drop-down menu. There are many freely available menus out on the internet and I encourage you to use the great work others have done. Don't reinvent the wheel...just use Domino to leverage it's power. CSS Menus are generally regarded as the most flexible and "safe" kind of dynamic menus since they rely on standard HTML tags which allow the menu to degrade gracefully in the event there is a problem with the CSS or the browser does not support a feature that is used. There are many excellent CSS menus available on the internet and a quick search on your favorite search engine will turn these up. For purposes of this demo, I'm using CSS Express Menus from projectseven.com.

To get started, there are a couple of downloads to grab from the projectseven website (these are already in the example database). You'll find the CSS (which is quite simple and elegant) in the page titled 'p7exp.css'. There is also a small snippet of javascript that is used to address some shortcomings in IE (p7exp.js). The structure of the dynamic menu is simply built with an unordered list. You can see a hardcoded example of this structure by examining the "StaticCSSMenu" form in this database. Here's the basic idea:


<div id="menuwrapper">
<ul id="p7menubar">
<li><a href="#">Home</a></li>
<li><a class="trigger" href="#">Trigger One</a>
<ul>
<li><a href="#">Sub 1.1</a></li>
<li><a href="#">Sub 1.2</a></li>
<li><a href="#">Sub 1.3</a></li>
<li><a href="#">Sub 1.4</a></li>
</ul>
more code here...
</li></ul>
<br class="clearit">
</div>


Note that the menu is a single, nested unordered list. Each link that acts as a trigger to show a sub-menu is assigned a class="trigger". The trigger class sets a downward-pointing arrow as a background image, indicating that a sub-menu is available. The code looks like this:


<li><a class="trigger" href="#">Trigger One</a>


Viewed without CSS, this code gives you the following:



It's fairly easy to style this unordered list, then, to provide the functionality we're looking for. So, the plain unordered list magically becomes:



Hopefully, at this point you are thinking you are thinking to yourself, "Self...with Notes, I can do all sorts of cool list processing and text manipulation. I bet I could dynamically create the unordered list and have a customized, on-the-fly CSS menu." Well, you are one smart cookie, since that is exactly what we are going to do...using the action bar as our source.

Let's review the first step in the process. This part is generic to any solution you would use to manipulate the action bar. The action bar in design mode for our example looks like this:



1. Iterate through the <a> tags in the Action Bar and create three arrays; one to hold the a flag that tells us what the action is (an href or onClick), one to hold the actual code and one to hold the label.


var whatAction = new Array();
var contextAction = new Array();
var labelAction = new Array();

var actionBarTable = document.getElementsByTagName("Table")[0];
var actionAnchors = actionBarTable.getElementsByTagName("a");
for (i=0; i < actionAnchors.length; i++) {
if(actionAnchors[i].onclick) {
contextAction[i] = actionAnchors[i].getAttribute("onclick").toString
().replace(/^function anonymous\(\)\n{\n/,"").replace(/\n}$/,"");
contextAction[i] = contextAction[i].replace(/return false/, "");
whatAction[i] = "click";
}
else {
contextAction[i] = actionAnchors[i].href;
whatAction[i] = "href";
}
labelAction[i] = actionAnchors[i].firstChild.nodeValue
}


As you can see, this code loops through the actionAnchors object, which is an array of all the <a> elements in the action bar table. In this loop, we check if the <a> element that is being processed has an onclick event. If it does, we use the DOM to get the onclick attribute and place it into the contextAction array. I'm doing some string manipulation at the same time in order to remove the function definition that the browser automatically sets up for the onClick event (see aside for more detail). If the action doesnt have an OnClick event, then it is using an href, so we place this code into the contextAction array. At the same time we add stuff to the contextAction array we also add the text of the action to the labelAction array and we set the flag in the whatAction array to let us know if the action was an onClick or href. Note that I could have used a multidimensional array to store all of these things, but I find it easier to work with multiple single dimension arrays.

Using the example in the database, here's what the arrays would look like if we visualized them as a graphic:



Aside: Different browsers use different function names for the functions that represents certain events. Here's a simple example you can try in your database to see what I mean:

Create a new action button and add the following JavaScript to it:


alert("Here is some simple code");
alert(this.onclick);


When you click this action button on the web, the second dialog box that appears will show you how the browser represents that onClick event. IE and some other browsers use the function name "anonymous", a function that does not define any arguments. Firefox and other browsers use the event name as the name of the function and they use an argument called "event" (which is the Event object that gets passed to an event handler).




Now that you have all of this information within your three arrays, the logic from here on out is dependent on what you want to do. In our case, we want to generate an unordered list. Notice the labels I used for the action buttons include text values separated by an "*". I did this in order to define both the drop-down text and the label for the action within that drop-down menu. Thus, we'll have three drop-down menus in our example, "Demo", "View Actions" and "Create". Making use of this, we're going to iterate through the arrays and throw the menu name into a variable. As we walk the array, if we find that the current label has the same menu name, it will be part of the same <ul>. If it's not the same, we've encountered a new menu and we have to close the existing <ul> and start another one. In either case, we also construct the <li> tag and include the code for it, either using the href value or the onClick event. Please note that in this example, you'll need to keep actions that should be in the same menu grouped together. We could account for items in the same menu being located in different spots on the action bar, but that would make the scripting much more difficult.

Here's the code:


//Generate our unordered lists for use in the CSS Menu

var currentMenu;
var menuButtonText;
var currentMenuLabel;
var menuOutput = '';

//This function is a helper to construct the <li> elements
function makeLink (linkType, linkCode, linkLabel) {
if (linkType == "href") {
menuOutput += "<li><a href=\"" + linkCode + "\">" + linkLabel
+ "</a></li>";
}
else {
menuOutput += "<li><a href=\"#\" onClick=\"" + linkCode + "\">" +
linkLabel + "</a></li>";
}
}

//Iterate through the arrays now
for (i=0; i < contextAction.length; i++) {
menuButtonText = labelAction[i].split('*');
if (menuButtonText[0] == currentMenuLabel) {
//if the menu names are the same, just add an new <li> to the existing <ul>
makeLink(whatAction[i], contextAction[i], menuButtonText[1]);
}
else { //otherwise, create a new menu
currentMenuLabel = menuButtonText[0];
menuOutput += "</ul><li><a class=\"trigger\" href=\"#\">" +
currentMenuLabel + "</a><ul>" //adds the new menu drop down
makeLink(whatAction[i], contextAction[i], menuButtonText[1]);
}
}

//Hides the Domino generated action bar and HR
var actionBarTable = document.getElementsByTagName("Table")[0];
var actionBarHR = document.getElementsByTagName("HR")[0];
actionBarTable.style.display='none';
actionBarHR.style.display='none';

//Write the menu code out to the document
document.write('<div id=\"menuwrapper\"><ul id=\"p7menubar\"><ul>'+ menuOutput +
'<br class=\"clearit\"></ul></li></ul></div>');

//Call P7_ExpMenu() for IE compatibility
P7_ExpMenu();


If you take some time to deconstruct this, you'll find it's not very hard at all. The most difficult part was coming up with the concept.


All of this code lives on a single subform called "CSSMenu" within the example database. If you want to use this in an existing database without knowing anything about how it all works, all you need to do is copy the following design elements into your application:

CSSMenu (subform)
p7exp.css (page)
p7exp.js (page)
p7PM_dark_south.gif (image)

Wherever you position the subform on your main form is where the menu bar will appear. You can leave it at the top of the page, place it within a positioned element, within a table, etc. You'll also need to modify the labels of your action buttons (and maybe their positions) so that they reflect the name of the menu they should go under and the text that should have within that menu (e.g. Rock*Roll). The only other design change is to reference the p7exp CSS file and javascript file in the HTML head of your page.

(The sample database uses the following code in the HTML Head Content, since it uses a conditional comment for making a slight change in the CSS for IE.)


DBPath := @WebDbName;

"<link rel=\"stylesheet\" href=\"p7exp.css\" type=\"text/css\">" + @NewLine +
"<script src=\"p7exp.js\"></SCRIPT>" + @NewLine +
"<!--[if lte IE 7]>" + @NewLine +
"<style>" + @NewLine +
"#menuwrapper, #p7menubar ul a {height: 1%;}" + @NewLine +
"a:active {width: auto;}" + @NewLine +
"</style>" + @NewLine +
"<![endif]-->" + @NewLine +
"<style>body {margin:0; padding:0;font:11pt/1.5 sans-serif;}</style>"


I just tried this. It literally took me 7 minutes to plop the elements into a completely non-web enabled database and have a beautiful looking and nicely functional drop down menu generated from the action bar. Pretty sweet I think!

If you made it this far, then I hope you see the power in using techniques like this to manipulate the DOM. The specifics of this article aren't nearly as important as the underlying idea. Once you know how to access objects via the DOM, the world of dynamic web applications will completely open up to you. I hope that the content here made some sense to you, but if you have any questions, please leave a comment and I'll answer it here.

----

Update: Even before I posted this, I wondered how easy it would be to grab a vertical CSS menuing system from the internet and modify it for use here. Well, it took less than 10 minutes. Since most CSS menus use an unordered list, the only code I had to change was the line that actually writes the menu out to the browser. I also downloaded the CSS and JS files from JavaScript Kit and plopped them in the database. I created a new subform ("VerticalCSSMenu") and just changed the last line. I also made a slight modification to the css to use colors for the menu and hover rather than graphics. So...as you can see, this idea is very easy to extend/change, etc. to meet any needs you might have.



As always, I hope you find this useful. If you come up with other good ideas, please feel free to share. Cheers!

Labels: ,

permalink | leave a comment

Thursday, April 27, 2006

SnTT: Good Looking and Useable Form Validation

Ah...Form validation. A subject that could probably fill several tomes and has been source of debate since the web browser was first introduced. There are probably as many ways to implement validation on your web forms as there are opinions about the best way to do it. Even though there is a ton of information out there about the hows and whys of form validation, I still see many sub-par implementations of the concept in Domino-based web sites. This certainly has nothing to do with the technology itself. I think most of us know by now that you can do just about anything in Domino...even if it calls for righteous hacks. :-) So...what's the reason for poor validation techniques in Domino. Typically I've found that it's just that developers aren't aware of some simple, yet powerful ways to use web technologies in their apps. The purpose of this entry is to introduce you to a really easy technique that you can implement right away to have nice clean, useable validation in your Domino forms.

For our purposes today, I'm going to use client-side validation. That is, we'll use JavaScript to check the fields before the form is ever submitted to the server. This is fast and efficient, since we let the client do the work. Unfortunately, this can be handled very poorly. The most common implementation of client-side form validation I see is the use of the JavaScript alert function, which pops up a message to the user telling them that they forgot to fill in a certain field. This might be OK for only one or two fields, but beyond that it is bad news from a usability perspective. What typically happens is that the user fills out the field referenced in the alert box and then clicks the submit button again. If there is another field that fails validation, then an error message for that field pops up. Yuck...do this several times and bye bye user.

Aside: Always remember you can't just rely on client-side validation to do your work. Your user might have JavaScript disabled or a tricky hacker might find an easy way to exploit a hole in your app. Client-side validation is great for quick, immediate feedback to the user without requiring a trip to the server, but you have to do your true validation on the server-side.


A slightly better way is to provide a list of all fields that failed in a single alert call. At least this time the user knows everything that went wrong. The problem is the limited memory retention that we have. Once the user clicks the OK button to dismiss the dialog box, that's it...the validation message is gone. If there are several fields listed, the user either has to remember all the fields that need to be fixed or they have to jot them down. Again, not the best solution when you are striving for a useable app.

You can get really fancy with server-side validation, since you can write in lots of error information when you present the form back to the user. But in our case, how can we provide useful error information that doesn't interfere with the rest of the interface and still be quick and lightweight? Here's a picture of what I'm striving for. This example is from Amazon. Although their validation is done server-side, I want to mimic this kind of idea for my Domino forms.

amazon1


Here's the cool trick and secret to doing this...I create a hidden div which is used to display my validation messages. If validation fails, this div is revealed to the user. If you place this at the top of your form, the user sees the feedback immediately and can continue filling in the form while still referring back to the error messages. Of course, since this is all just HTML and CSS, your div can be positioned anywhere it makes sense in the context of your form. This is really flexible and easy!

The code to handle all of this is quite easy. First, check out the sample to see how this works...then come back.

OK...that's pretty cool, eh? Here's the code for the div that holds the error messages.




This div is empty and has a class I called "hideMe". For simplicity, my CSS definition for the classes that will show or hide this information looks like:



In practice, these classes might have more to them, but it's not necessary right now.

The "ErrorMsg" div resides at the top of the form. When the user tries to submit the form, the validation code is called. You can use whatever validation code you currently have (Jake has done some great stuff with this). In order to get this to work with your current validation routine, you'll just have to do a couple of extra things:

1. Construct your error message using HTML.
2. Write this message to the "ErrorMsg" div.
3. Show the "ErrorMsg" div by changing it's class to "showMe".

For this example, I took a very streamlined part of Jake's code to demonstrate. Here's the whole scenario step by step.

1. User opens the page and clicks the 'Continue' button. This invokes a call to the 'canYouDigIt' function.

2. 'canYouDigIt' builds an array to hold the required fields and the text that you want displayed in the error message.




3. It then calls the 'validateRequiredFields' function and submits the form if all is OK. If validation fails, it builds the HTML code that will be displayed to the user and then places that code in the "ErrorMsg" div using the innerHTML property. When that is done, it calls 'showIt' function to change the "ErrorMsg" div class to "showMe".



Note: empty_fields is a global variable that is declared in the JS Header.

In the event that validation fails, the HTML that you construct can be as simple or complex as you want. In this case, I built a fieldset and placed a table within that fieldset.

That's all there is to it. The cool thing about this solution is that it is pretty easy to plug in anywhere and requires a fairly small amount of code. Of course, there's a lot that can be done to improve it. Let's look at one other usability enhancement. I find that users really like it when they not only get a message of what needs to be fixed, but when they get a visual indicator on the fields themselves. This can be accomplished with the addition of just a couple more lines of code. In your validation code, when you find a field that fails validation, set the class of that field to "formerror". Then, in your CSS, define the properties you want for the "formerror" class.

Here's what I used:



With this little enhancement, the background color and border color of the field is changed and a small image appears. If this doesn't get their attention, then all is lost! ;-)

You can see how all this fits together by viewing the source of my sample page, but for completeness, here is the remainder of the code that does all the work. The code that is important for this example is the line where the class of the field is set to nothing (to reset possible past errors) and when it is set to "formerror" in the event that the field is blank.




If you got this far, congratulations...boy can I ramble on! :-) Anyway, I hope you find this a useful and user-friendly form validation technique. If you'd be interested in having this in a sample database, please leave a comment or ping me and I'll put one together. Cheers!

Technorati tags:

Labels: , ,

permalink | leave a comment