<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Stillnet Studios &#187; AJAX / JavaScript</title>
	<atom:link href="http://www.stillnetstudios.com/category/ajax-javascript/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.stillnetstudios.com</link>
	<description>Web development notes and commentary from Ryan Stille</description>
	<lastBuildDate>Tue, 27 Jul 2010 14:40:41 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Making an &#8216;Apply to all&#8217; with jQuery</title>
		<link>http://www.stillnetstudios.com/making-an-apply-to-all-with-jquery/</link>
		<comments>http://www.stillnetstudios.com/making-an-apply-to-all-with-jquery/#comments</comments>
		<pubDate>Tue, 06 Jul 2010 16:32:00 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>

		<guid isPermaLink="false">http://www.stillnetstudios.com/?p=865</guid>
		<description><![CDATA[Here is a little &#8216;Apply To All&#8217; feature I added to a large form we had.  There were many rows of data, each with several columns the user needs to fill out.  Most of the time the values they were entering in the top row ended up being what they would set all [...]]]></description>
			<content:encoded><![CDATA[<p>Here is a little &#8216;Apply To All&#8217; feature I added to a large form we had.  There were many rows of data, each with several columns the user needs to fill out.  Most of the time the values they were entering in the top row ended up being what they would set all the subsequent rows to.  So I wanted to add an easy way they could set all the later rows to the value of the first.</p>
<p>This was actually very easy to do with jQuery.  I added a class to each form element, and links on the first row, so my code ended up looking something like this:<br />
<span id="more-865"></span></p>
<pre><code>&lt;cfloop query="qryRows"&gt;
  &lt;tr&gt;
    &lt;td&gt;
      &lt;input type="checkbox" class="verified" name="verified_#CurrentRow#"&gt;
      &lt;cfif CurrentRow EQ 1&gt;<a href="JavaScript:;" onclick="applyToAll('verified')">&amp;#8595;</a>&lt;/cfif&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;input type="checkbox"class="reviewed" name="reviewed_#CurrentRow#"&gt;
      &lt;cfif CurrentRow EQ 1&gt;<a href="JavaScript:;" onclick="applyToAll('verified')">&amp;#8595;</a>&lt;/cfif&gt;
    &lt;/td&gt;
    &lt;td&gt;
      &lt;select class="okToContinue" name="okToContinue_#CurrentRow#"&gt;
        &lt;option&gt;Yes&lt;/option&gt;
        &lt;option&gt;No&lt;/option&gt;
      &lt;/select&gt;
      &lt;cfif CurrentRow EQ 1&gt;<a href="JavaScript:;" onclick="applyToAll('verified')">&amp;#8595;</a>&lt;/cfif&gt;
    &lt;/td&gt;
  &lt;/tr&gt;
&lt;/cfloop&gt;</code></pre>
<p>So now each type of form element is grouped together with a class, and the first row has links next to each element.  If I played around with this a little more I think I could add the link with jQuery too, instead of having to check the CurrentRow variable and do this manually.</p>
<p>Here is the applyToAll function.  Note you have to copy the values a little differently depending on if the element is a checkbox or a select control.</p>
<pre><code>// grabs the first element in a class, then applies its value to all elements with that class
applyToAll = function(classToApplyTo) {

  // need to apply differently depending on if its a select control or a checkbox. We read what kind of element it is by looking at the 'type'
  switch ( $('.' + classToApplyTo + ':first').attr('type') ) {
    case 'select-one':
      var baseValue = $('.' + classToApplyTo + ':first').val();
      $('.' + classToApplyTo).val(baseValue);
      // also call the change method, just in case there was an onchange handler set on the element
      $('.' + classToApplyTo).change();
      break;
    case 'checkbox':
      var baseValue = $('.' + classToApplyTo + ':first').attr('checked');
      $('.' + classToApplyTo).attr('checked', baseValue);
      // also call the change method, just in case there was an onchange handler set on the element
      $('.' + classToApplyTo).change();
      break;
    }
  }</code></pre>
<p>Here is a demo:</p>
<form>
<table>
<tr>
<td align="center"><b>A</b></td>
<td align="center"><b>B</b></td>
<td align="center"><b>Ok?</b></td>
</tr>
<tr>
<td nowrap="true">
<input type="checkbox" class="verified" name="verified_1"><a href="JavaScript:;" onclick="applyToAll('verified')">&#8595;</a>
</td>
<td nowrap="true">
<input type="checkbox" class="reviewed" name="reviewed_1"><a href="JavaScript:;" onclick="applyToAll('reviewed')">&#8595;</a>
</td>
<td nowrap="true" width="50">
<select class="okToContinue" name="okToContinue_1">
  <option>Yes</option><br />
  <option>No</option><br />
  </select>
</td>
<td><a href="JavaScript:;" onclick="applyToAll('okToContinue')">&#8595;</a></td>
</tr>
<tr>
<td>
<input type="checkbox" class="verified" name="verified_2">
</td>
<td>
<input type="checkbox" class="reviewed" name="reviewed_2">
</td>
<td colspan="2">
<select class="okToContinue" name="okToContinue_2">
  <option>Yes</option><br />
  <option>No</option><br />
  </select>
</td>
</tr>
<tr>
<td>
<input type="checkbox" class="verified" name="verified_3">
</td>
<td>
<input type="checkbox" class="reviewed" name="reviewed_3">
</td>
<td colspan="2">
<select class="okToContinue" name="okToContinue_3">
  <option>Yes</option><br />
  <option>No</option><br />
  </select>
</td>
</tr>
</table>
</form>
<p>Some of the form elements had onchange handlers that made other text show/hide on the screen, so in the applyToAll function I manually call the &#8216;change&#8217; method so those events fire on the elements.</p>
<p>This change has saved the users time on filling out huge forms.</p>
<p><script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script></p>
<p><script>
applyToAll = function(classToApplyTo) {
  // need to apply differently depending on if its a select control or a checkbox. We read what kind of element it is by looking at the 'type'
  switch ( $('.' + classToApplyTo + ':first').attr('type') ) {
    case 'select-one':
      var baseValue = $('.' + classToApplyTo + ':first').val();
      $('.' + classToApplyTo).val(baseValue);
      // also call the change method, just in case there was an onchange handler set on the element
      $('.' + classToApplyTo).change();
      break;
    case 'checkbox':
      var baseValue = $('.' + classToApplyTo + ':first').attr('checked');
      $('.' + classToApplyTo).attr('checked', baseValue);
      // also call the change method, just in case there was an onchange handler set on the element
      $('.' + classToApplyTo).change();
      break;
    }
  }
</script></p>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/making-an-apply-to-all-with-jquery/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using CFWindow within a form</title>
		<link>http://www.stillnetstudios.com/using-cfwindow-within-form/</link>
		<comments>http://www.stillnetstudios.com/using-cfwindow-within-form/#comments</comments>
		<pubDate>Sun, 28 Mar 2010 03:13:21 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>
		<category><![CDATA[ColdFusion]]></category>

		<guid isPermaLink="false">http://www.stillnetstudios.com/?p=817</guid>
		<description><![CDATA[The CFWindow tag can be useful to prompt for information on a form.  We use this sometimes after a user has clicked the submit button.  We do some client side validation in JavaScript, and if certain conditions are met, we use CFWindow to prompt for some additional information.
Its a little tricky though.  [...]]]></description>
			<content:encoded><![CDATA[<p>The CFWindow tag can be useful to prompt for information on a form.  We use this sometimes after a user has clicked the submit button.  We do some client side validation in JavaScript, and if certain conditions are met, we use CFWindow to prompt for some additional information.</p>
<p>Its a little tricky though.  If you try to use a CFWindow tag inside a CFForm tag you will get a ColdFusion error.  If you use it inside a regular form tag you won&#8217;t get an error, but it won&#8217;t work like you expect.  Lets take this code for example:<br />
<span id="more-817"></span></p>
<pre><code>&lt;form action="somefile.cfm"&gt;
	Name: &lt;input name="name"&gt;

	&lt;cfwindow name="win"&gt;
		Age:&lt;input name="age"&gt;&lt;br /&gt;
		&lt;br /&gt;
		&lt;input type="submit"&gt;
	&lt;/cfwindow&gt;

	&lt;br /&gt;&lt;br /&gt;
	&lt;input type="button" Value="Continue" onclick="ColdFusion.Window.show('win')"&gt;
&lt;/form&gt;</code></pre>
<p>In this example we have a simple form that prompts for a name.  When the user clicks the submit button, we use cfwindow to open a popup window and prompt for age.  There is a submit button in the popup window so the user can submit the form from there.</p>
<p>But when you run this code and click on the submit button in the popup window, nothing happens.  Thats because ColdFusion places the CFWindow code near the end of the file.  It won&#8217;t be inside your FORM tags.  Changing the submit button to a button with an onclick event that submits the form won&#8217;t help either.  It will cause the form to be submitted, but only the name field will be passed in.  Age won&#8217;t be passed because that form element wasn&#8217;t inside the form tags. </p>
<p>The way around this is to use JavaScript to copy the data from the form fields to hidden fields that are inside the form.  Lets change the code to this:</p>
<pre><code>&lt;form action="somefile.cfm" id="theForm"&gt;
	Name: &lt;input name="name"&gt;
	&lt;input type="hidden" name="age" id="hidden_age"&gt;
	&lt;cfwindow name="win"&gt;
		Age:&lt;input name="age" id="age"&gt;&lt;br /&gt;
		&lt;br /&gt;
		&lt;input type="button" onclick="doSubmit()"&gt;
	&lt;/cfwindow&gt;

	&lt;br /&gt;&lt;br /&gt;
	&lt;input type="button" Value="Continue" onclick="ColdFusion.Window.show('win')"&gt;
&lt;/form&gt;</code></pre>
<p>Here I&#8217;ve added a hidden field for the age, and given IDs to both age fields and the form tag so I can reference them easily.  I&#8217;ve also changed the submit button to a regular button, and gave it an onclick handler of doSubmit.  Here is the code for doSubmit():</p>
<pre><code>&lt;script language="JavaScript"&gt;
function doSubmit() {
  var hiddenAge = document.getElementById("hidden_age");
  var age = document.getElementById("age");
  hiddenAge.value = age.value;
  document.getElementById("theForm").submit();
}
&lt;/script&gt;</code></pre>
<p>Now when the user clicks the button from the popup window, the values from that window are copied to the hidden form elements that exist in the form, and they will be passed to the server.</p>
<p>I use jQuery, which makes this even easier.  I don&#8217;t have to setup a bunch of hidden fields in the form, I just create them in the doSubmit() function.  You just need to have one element inside the form already that you can reference, so you can add an element after it.  In this example I know the name element is there so I am adding the new form elements after that.</p>
<pre><code>&lt;script language="JavaScript"&gt;
function doSubmit() {
  $('#name').append('&lt;input type="hidden" id="hidden_age" name="age"&gt;');
  $('#hidden_age').val( $('#age').val() );

  document.getElementById("theForm").submit();
}
&lt;/script&gt;</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/using-cfwindow-within-form/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>A secure, ajaxy captcha with cfimage</title>
		<link>http://www.stillnetstudios.com/ajaxy-captcha-cfimage/</link>
		<comments>http://www.stillnetstudios.com/ajaxy-captcha-cfimage/#comments</comments>
		<pubDate>Fri, 12 Jun 2009 03:05:19 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>
		<category><![CDATA[ColdFusion]]></category>

		<guid isPermaLink="false">http://www.stillnetstudios.com/?p=460</guid>
		<description><![CDATA[A while back I had to implement a captcha on a client&#8217;s site.   The site owner wanted a simple small captcha (that ruled out reCAPTCHA).  We decided to try the new captcha features of ColdFusion8.  What you may not realize is that the new captcha feature does not provide the whole [...]]]></description>
			<content:encoded><![CDATA[<p>A while back I had to implement a captcha on a client&#8217;s site.   The site owner wanted a simple small captcha (that ruled out <a href="http://recaptcha.net/">reCAPTCHA</a>).  We decided to try the new captcha features of ColdFusion8.  What you may not realize is that the new captcha feature does not provide the whole captcha <em>system</em>, instead it merely can create captcha images.  Its up to you how you implement your captcha system.</p>
<p>Before I get too far just let me state for the record that I <em>dislike</em> captchas and will be happy when they are looked upon like we look at the &lt;blink&gt; tag now.  So you don&#8217;t need to leave comments telling me how I shouldn&#8217;t be using a captcha in the first place. <img src='http://www.stillnetstudios.com/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' />   The client specifically wanted this feature at this point in time.</p>
<p>I&#8217;ve seen some approaches that place the clear text value of the captcha in a hidden field.  Then when the form is submitted they compare that value against what the user had typed in.  I don&#8217;t feel this way is very secure.  It will stop simple bots, but you need to guard against more than just that.  Sometimes spammers code their bots to work against a specific site.  If they find your hidden clear text captcha value, they will easily grab it and use it to submit your form.  If this is a simple contact form then you might not have much to worry about, but if its a &#8220;send to a friend&#8221; feature &#8211; watch out, those are high value targets.</p>
<p>Encrypting the hidden value doesn&#8217;t help much either.  That adds one more step to what the spammer needs to do.  They will have to manually read one of your captcha images &#8211; then they have the clear text and encrypted values to your captcha system.    Now they can just submit that encrypted/plain text pair over and over again to your form.<br />
<span id="more-460"></span><br />
To really be secure you have to keep track of the captcha value on the server side.  I decided to use a database but you could also use an in-memory variable or even use the file system.  One of the requirements was to allow the user to ask for a new captcha image without having to reload the whole form.  That requires ajax, but cfimage has no built in facilities for easily using it through ajax.  But its not too hard to work around.  Here is a cfc I created to wrap all this up.  My storage and retrieval methods in here use a database but you could easily change them to use the application scope or whatever you like.  The important part is to delete the captcha from your system (database, application scope, whatever) after its been solved.  This will prevent an attacker from manually decoding one captcha and then using that is his script.</p>
<pre><code>&lt;cfcomponent hint="Creates captchas using cfimage. Supports ajax refresh." output="false"&gt;

	&lt;cffunction name="createCaptcha" returntype="struct" hint="Returns a structure containing captcha properties." access="remote"&gt;
		&lt;cfargument name="minLength" default="6"&gt;
		&lt;cfargument name="maxLength" default="6"&gt;
		&lt;cfargument name="id" default="captchaImg" displayname="an ID to be used in the image tag."&gt;
		&lt;cfargument name="difficulty" default="low" hint="low | medium | high"&gt;

		&lt;cfset var local = {}&gt;
		&lt;cfset local.retVal = {}&gt;

		&lt;!--- declaring the keys this way ensures they will be delivered to the javascript in lower case, which it expects ---&gt;
		&lt;cfset local.retval["imgsrc"] = ""&gt;
		&lt;cfset local.retval["captcha_id"] = ""&gt;
		&lt;cfset local.retval["imgtag"] = ""&gt;

		&lt;cfset local.captchaText = makeCaptchaString(Arguments.minLength,Arguments.maxlength)&gt;

		&lt;!--- store the the captcha value in the DB. Get the ID so it can be used later to look up the captcha value ---&gt;
		&lt;cfquery name="local.qryCaptchaID" datasource="#Request.PrimaryDataSource#"&gt;
		SET NOCOUNT ON
		INSERT INTO captcha_values (captcha_value) VALUES
		(&lt;cfqueryparam value="#local.captchaText#" cfsqltype="cf_sql_varchar"&gt;)
		SELECT SCOPE_IDENTITY() AS captcha_id
		SET NOCOUNT OFF
		&lt;/cfquery&gt;

		&lt;!--- need to return the id ---&gt;
		&lt;cfset local.retval.captcha_id = local.qryCaptchaID.captcha_id&gt;

		&lt;cfsavecontent variable="local.retVal.imgtag"&gt;
			&lt;cfimage action="captcha" text="#local.captchaText#" difficulty="#Arguments.difficulty#"&gt;
		&lt;/cfsavecontent&gt;

		&lt;!--- tease out the image url, too.  This can be used to refresh the captcha with ajax. ---&gt;
		&lt;cfset local.regExResult = ReFind("src=""([^""]*)",local.retVal.imgtag,1,1)&gt;
		&lt;cfset local.retVal.imgsrc = Mid(local.retVal.imgtag,local.regExResult.pos[2],local.regExResult.len[2])&gt;

		&lt;!--- need to add an ID so we can manipulate with JavaScript. ---&gt;
		&lt;cfset local.retVal.imgtag = Replace(local.retVal.imgtag,"&lt;img","&lt;img id=""#Arguments.id#""","one")&gt;

		&lt;cfreturn local.retVal&gt;

	&lt;/cffunction&gt;

	&lt;cffunction name="getCaptchaText" returntype="string" hint="Returns the text associated with a captcha_id" access="public"&gt;
		&lt;cfargument name="captcha_id"&gt;

		&lt;cfset local = {}&gt;

		&lt;cfquery name="local.qryCaptcha" datasource="#Request.PrimaryDataSource#"&gt;
		SELECT captcha_value FROM captcha_values WHERE
		captcha_id = &lt;cfqueryparam value="#Arguments.captcha_id#" cfsqltype="cf_sql_integer"&gt;
		&lt;/cfquery&gt;

		&lt;cfif local.qryCaptcha.RecordCount EQ 0&gt;
			&lt;cfreturn ""&gt;
		&lt;/cfif&gt;

		&lt;!--- else we did find a captcha with this ID.  Now delete it, and return the value we found ---&gt;
		&lt;cfquery datasource="#Request.PrimaryDataSource#"&gt;
		DELETE FROM captcha_values WHERE
		captcha_id = &lt;cfqueryparam value="#Arguments.captcha_id#" cfsqltype="cf_sql_integer"&gt;
		&lt;/cfquery&gt;

		&lt;cfreturn local.qryCaptcha.captcha_value&gt;
	&lt;/cffunction&gt;

	&lt;cffunction name="makeCaptchaString" returnType="string" output="false" access="private"&gt;
		&lt;cfargument name="minLength"&gt;
		&lt;cfargument name="maxLength"&gt;

		&lt;!--- Don't use any characters that can be confused with each other ---&gt;
		&lt;cfset var chars = "23456789ABCDEFGHJKMNPQRS"&gt;
		&lt;cfset var length = randRange(Arguments.minLength,Arguments.maxLength)&gt;
		&lt;cfset var result = ""&gt;
		&lt;cfset var i = ""&gt;
		&lt;cfset var char = ""&gt;

		&lt;cfscript&gt;
		for(i=1; i &lt;= length; i++) {
			char = mid(chars, randRange(1, len(chars)),1);
			result&amp;=char;
		}
		&lt;/cfscript&gt;

		&lt;cfreturn result&gt;
	&lt;/cffunction&gt;
&lt;/cfcomponent&gt;</code></pre>
<p>Then in your form page you need a little javascript to allow the user to refresh the image.</p>
<pre><code>&lt;cfajaxproxy cfc="cfimageCaptcha" jsclassname="cfimageCaptcha"&gt;
&lt;script language="JavaScript"&gt;
function refreshCaptcha() {
	var jsCaptcha = new cfimageCaptcha();
	jsCaptcha.setCallbackHandler(populateCaptcha);
	jsCaptcha.createCaptcha();
}

function populateCaptcha(result) {
	var imgTag = document.getElementById("captchaImg");
	var hiddenTag = document.getElementById("captcha_id");

	imgTag.src = result.imgsrc;
	hiddenTag.value = result.captcha_id;

}
&lt;/script&gt;</code></pre>
<p>Then display the actual captcha on the page:</p>
<pre><code>&lt;cfset myCaptcha = CreateObject("component","cfimageCaptcha").createCaptcha()&gt;
#myCaptcha.imgtag#&lt;a href="JavaScript:refreshCaptcha()"&gt;change code&lt;/a&gt;&lt;br /&gt;
Type in the letters you see above: &lt;input type="text" name="captcha_value"&gt;
&lt;!--- need to pass along the ID of the captcha so we can look it up later ---&gt;
&lt;input type="hidden" name="captcha_id" id="captcha_id" value="#myCaptcha.captcha_id#"&gt;</code></pre>
<p>With me so far?  Now here is how you validate the captcha on your &#8220;action&#8221; page:</p>
<pre><code>&lt;cfif Form.captcha_value NEQ CreateObject("component","cfimageCaptcha").getCaptchaText(Form.captcha_id)&gt;
       &lt;p&gt;Throw your failed captcha error here!&lt;/p&gt;
&lt;/cfif&gt;</code></pre>
<p>Here is the SQL I used for my captcha table.</p>
<pre><code>CREATE TABLE [captcha_values] (
	[captcha_id] [int] IDENTITY (1, 1) NOT NULL ,
	[captcha_value] [varchar] (50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL ,
	[date_added] [smalldatetime] NULL CONSTRAINT [DF_captcha_values_date_added] DEFAULT (getdate())
) ON [PRIMARY]
GO</code></pre>
<p>And here is the query I run nightly to clear out abandoned entries that are older than 24 hours:</p>
<pre><code>DELETE FROM captcha_values WHERE
DateDiff(hour,date_added,CURRENT_TIMESTAMP) &gt; 24</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/ajaxy-captcha-cfimage/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Programatically adding additional JS onload functions</title>
		<link>http://www.stillnetstudios.com/additional-js-onload-functions/</link>
		<comments>http://www.stillnetstudios.com/additional-js-onload-functions/#comments</comments>
		<pubDate>Mon, 27 Apr 2009 15:47:18 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://www.stillnetstudios.com/?p=415</guid>
		<description><![CDATA[Sometimes when writing JavaScript I need to have something run as soon as the page has finished loading.  This is usually done by placing a call to the function in the body&#8217;s onload attribute like:
&#60;body onload="myFunc()"&#62;
But this is not always possible.  For example by the time you get to your logic that decides [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes when writing JavaScript I need to have something run as soon as the page has finished loading.  This is usually done by placing a call to the function in the body&#8217;s onload attribute like:</p>
<p><code>&lt;body onload="myFunc()"&gt;</code></p>
<p>But this is not always possible.  For example by the time you get to your logic that decides it necessary to call a function onload, the header may have already been displayed by a cfinclude or by your framework.</p>
<p>You could use JavaScript to set the onload event, like</p>
<p><code>window.onload = myFunc;</code></p>
<p>But what if there was already something in the onload attribute of the body tag?  The above code will reset whatever was there.  But here is a nice snippet of code that will <em>add</em> functions to the onload event.  I can&#8217;t take credit for it, and I don&#8217;t remember exactly where I found it but its been quite useful to me.  It works in all the popular browsers.</p>
<pre><code>function addLoadEvent(func) {
	var oldonload = window.onload;
	if (typeof window.onload != 'function') {
		window.onload = func;
	} else {
		window.onload = function() {
			if (oldonload) {
				oldonload();
			}
			func();
		}
	}
}</code></pre>
<p>Then you can add as many functions as you want to be called when the page loads.  Note you do not use parenthesis when specifying the function names &#8211; you aren&#8217;t calling then, just referencing them.</p>
<pre><code>addLoadEvent(myFunc);
addLoadEvent(myFunc2);
addLoadEvent(yetAnotherFunction);</code></pre>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/additional-js-onload-functions/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>Dynamically adding and removing form elements</title>
		<link>http://www.stillnetstudios.com/dynamically-adding-and-removing-form-elements/</link>
		<comments>http://www.stillnetstudios.com/dynamically-adding-and-removing-form-elements/#comments</comments>
		<pubDate>Mon, 05 Jan 2009 16:34:16 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://www.stillnetstudios.com/?p=242</guid>
		<description><![CDATA[Sometimes its helpful to allow users to dynamically add or remove elements from a form.  An example might be an image upload page, where you want to allow users to upload a varying number of images.   Using JavaScript you can easily place more/less links on the page that show and hide form [...]]]></description>
			<content:encoded><![CDATA[<p>Sometimes its helpful to allow users to dynamically add or remove elements from a form.  An example might be an image upload page, where you want to allow users to upload a varying number of images.   Using JavaScript you can easily place more/less links on the page that show and hide form elements.</p>
<p>This is often done by placing a large number of elements on the form, and setting most of them to be initially hidden with CSS.  When the user clicks the &#8220;show&#8221; link, the next element is unhidden.  This is not the best solution however, especially when there is a possibility of having tens of elements &#8211; thats a lot of unnecessary html to send down the pipe and render.</p>
<p>Another method is to actually create and destroy elements using the DOM functions.  Here&#8217;s how I did this recently on a page that need to have any number of form elements.<br />
<span id="more-242"></span><br />
First I setup a hidden form field that would contain the number of elements currently displayed on the form.  This value gets changed as new elements are removed or added, so I can keep track of where to add or remove them from.  Its also very useful when I submit the form, to know how many elements to look for.</p>
<p>I start out initially showing 5 elements, so I set this hidden field to that number.</p>
<pre><code>&lt;input type="hidden" name="numberOfInputs" value="5"&gt;</code></pre>
<p>Here is the form element I needed to replicate:</p>
<pre><code>&lt;p&gt;
  &lt;span style="width: 18px; float: left;"&gt;<strong style="color: red;">1</strong>.&lt;/span&gt;
  &lt;input width="20" type="text" name="FileHeader_<strong style="color: red;">1</strong>" value=""&gt;:&amp;nbsp;&amp;nbsp;
  &lt;select name="InternalField_<strong style="color: red;">1</strong>"&gt;
    &lt;option value=""&gt;Ignore&lt;/option&gt;
    &lt;option value="FirstName"&gt;
    &lt;option value="LastName"&gt;
    &lt;option value="Etc..."&gt;
  &lt;/select&gt;
&lt;/p&gt;</code></pre>
<p><em>(the items in red are values that change with each line on the form)</em></p>
<p>And here is what they looked like on the screen:<br />
<img src="http://www.stillnetstudios.com/wp-content/uploads/2009/01/form_ele_1.gif" alt="" title="Screenshot of form elements" width="345" height="169" class="alignnone size-full wp-image-250" /></p>
<p>The first thing I did was add an ID to the <code>&lt;p&gt;</code> tags so I could reference them.</p>
<pre><code>&lt;p <strong style="color: red;">id="row_#RowCount#"</strong>&gt;
  &lt;span style="width: 18px; float: left;"&gt;1.&lt;/span&gt;
  &lt;input width="20" type="text" name="FileHeader_1" value=""&gt;:&amp;nbsp;&amp;nbsp;
  &lt;select name="InternalField_1"&gt;
    &lt;option value=""&gt;Ignore&lt;/option&gt;
    &lt;option value="FirstName"&gt;
    &lt;option value="LastName"&gt;
    &lt;option value="Etc..."&gt;
  &lt;/select&gt;
&lt;/p&gt;</code></pre>
<p>Then I created two functions, one for removing elements and another for adding them.  The &#8220;Show More&#8221; and &#8220;Show Less&#8221; links shown in the screenshot above then link to these functions.</p>
<p>The removal one is easy.</p>
<pre><code>&lt;script language="JavaScript"&gt;
function ShowLess() {
    // get a reference to the hidden element that contains the number of inputs counter
    var numberOfInputsEle = document.getElementById("numberOfInputs");

    // if there is only one input left on the screen, don't let them remove that one
    if (numberOfInputsEle.value &lt;= 1) { return; }

    // get the actual count value from the hidden element
    var lastRowNum = numberOfInputsEle.value;

    // now we can get a reference to the form element that needs to be removed
    var eleToRemove = document.getElementById("row_" + lastRowNum);

    // then remove it
    eleToRemove.parentNode.removeChild(eleToRemove);

    // and decrement our input counter
    numberOfInputsEle.value = lastRowNum - 1;

}
&lt;/script&gt;</code></pre>
<p>The function to add another element is a little more involved.  Since I know the first line will always be there, I clone that element, along with its children.  Then alter the parts of it that reference &#8220;1&#8243;, changing them to be the counter value for the new element. Then add it at the end of the current list of form elements. I added a div around the whole group of elements with an id of &#8220;formElementsParent&#8221;.  I know the last element inside this div is a paragraph containing the show more/less links, so I can find that and then use the insertBefore() function to add the new element to the DOM.</p>
<pre><code>&lt;script language="JavaScript"&gt;
function ShowMore() {
    // get a reference to the hidden element that contains the number of inputs counter
    var numberOfInputsEle = document.getElementById("numberOfInputs");

    // read in the value, increment it so we have the correct new counter value
    var newRowNum = parseInt(numberOfInputsEle.value) + 1;

    // clone the first row, reset it, and use it for the new row
    var newNode = document.getElementById("row_1").cloneNode(true);

    // change the ID
    newNode.id = "row_" + newRowNum;

    // need to reset the form elements, as they could have already had values.
    // I reference them using getElementsByTagName and searching for them. I could
    // have given them each unique IDs, but then those additional IDs would need
    // to be changed, too, since they'd have the counter value in them.
    newNode.getElementsByTagName("span")[0].innerHTML = newRowNum + ".";
    newNode.getElementsByTagName("input")[0].name = "FileHeader_" + newRowNum;
    newNode.getElementsByTagName("input")[0].value = "";
    newNode.getElementsByTagName("select")[0].name = "InternalField_" + newRowNum;
    newNode.getElementsByTagName("select")[0].selectedIndex = 0;

    // get a reference to the container holding all the form elements
    var overallParent = document.getElementById("formElementsParent");

    // the &lt;p&gt; that contains the more/less links has an ID of "moreAndLessLinks", so we
    // can use that to insert our new element just before it.
    overallParent.insertBefore(newNode,document.getElementById("moreAndLessLinks"));

    // finally, write back the new counter value to the hidden form field
    numberOfInputsEle.value = newRowNum;
}
&lt;/script&gt;</code></pre>
<p>Try it out below:</p>
<p>[HTML1]</p>
<p>I should note that although I&#8217;m using P elements to contain my rows here, I realize many developers still use tables to lay out their forms.  This method works just fine with tables, too, you would just reference your TR in the places I&#8217;m referring to my P.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/dynamically-adding-and-removing-form-elements/feed/</wfw:commentRss>
		<slash:comments>18</slash:comments>
		</item>
		<item>
		<title>Resetting the DOM and JavaScript when using the &#8216;Back&#8217; button in FireFox</title>
		<link>http://www.stillnetstudios.com/reset-javascript-firefox-back-button/</link>
		<comments>http://www.stillnetstudios.com/reset-javascript-firefox-back-button/#comments</comments>
		<pubDate>Tue, 24 Jun 2008 02:51:24 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://www.stillnetstudios.com/2008/06/23/reset-javascript-firefox-back-button/</guid>
		<description><![CDATA[A while back I had a couple posts on preventing double form submissions and showing an in-progress meter for long running pages.  These work great, as long as you are using a type of validation that redisplays the form when there are errors.
This doesn&#8217;t work so well when you are not redisplaying the form. [...]]]></description>
			<content:encoded><![CDATA[<p>A while back I had a couple posts on <a href="/2007/01/30/prevent-multiple-form-submissions/">preventing double form submissions</a> and <a href="/2007/02/04/animated-in-progress-indicator-for-long-running-pages/">showing an in-progress meter</a> for long running pages.  These work great, as long as you are using a type of validation that redisplays the form when there are errors.</p>
<p>This doesn&#8217;t work so well when you are not redisplaying the form.  For example, I&#8217;ve seen some validation where the user is shown an error message and just told to use their &#8216;Back&#8217; button to fix the errors.  Sometimes a JavaScript history.go(-1) link is displayed as well.  I had to work on a site today that used this type of validation.<br />
<span id="more-101"></span><br />
I added a nice spinning wheel in-progress indicator, and some code to prevent the form being submitted more than once.  But when FireFox users (2.x and 3.x) had an error on their form and used their &#8216;Back&#8217; button, the form was redisplayed as they left it &#8211; without the DOM or JavaScript variables being reset.  What I mean by that is the &#8220;in-progress&#8221; spinning wheel was still spinning (because a div element was still set to visible), and the submit button still said &#8220;Please Wait&#8221; instead of submit.  And of course they couldn&#8217;t submit it after fixing their errors (because of the anti-double submit JavaScript code).</p>
<p>This didn&#8217;t happen in IE &#8211; the page was <em>reset</em> so that the in-progress meter was set to hidden, and the submit button value was back to its original state: &#8220;Submit&#8221; instead of &#8220;Please Wait&#8221;.  I did a lot of googling to see if there was a way around this problem.  I found many people asking this same question, but no good answers.  Here is a large thread on <a href="http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_21638368.html">Experts Exchange</a> (scroll to the bottom to see all the answers) about this issue.  You can see many people offer solutions which they are <em>sure</em> should fix the problem &#8211; but they don&#8217;t.  Many people suggested using &#8220;onload&#8221; to set the submit button back to its original value.   The problem is <em>none</em> of the JavaScript code is being run &#8211; onload won&#8217;t be run, nor will any other JS code you have in the page.  Its served exactly as you left it, from your cache.  On the subject of cache &#8211; setting this page to no-cache won&#8217;t solve the problem either, because then all the users form data will be gone.</p>
<p>But I finally did come across a solution.  The existence of an onUnload function will make FireFox behave properly when going &#8216;Back&#8217; to the previous page.  I&#8217;m not exactly sure why this works, I&#8217;m guessing it has something to do with making FireFox register the page as <em>unloaded</em>, so that when you come back, it loads again, running any initial Javascripts that are defined.</p>
<p>You don&#8217;t have to actually do anything in your unload function, it just has to exist.  You don&#8217;t have to have access to the <body> tag either, thank goodness, as this is sometimes hard to do within a framework.</p>
<p>Here&#8217;s how to define an empty onUnload function:</p>
<pre><code>&lt;script language="JavaScript"&gt;
window.onunload = function(){};
&lt;/script&gt;</code></pre>
<p>Thanks to the <a href="http://www.firefoxanswer.com/firefox/672-firefoxanswer.html">FireFox Answer site</a> where I finally found this solution.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/reset-javascript-firefox-back-button/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>ColdFusion 8 auto-suggest shows through other form fields</title>
		<link>http://www.stillnetstudios.com/cf8-autosuggest-shows-through/</link>
		<comments>http://www.stillnetstudios.com/cf8-autosuggest-shows-through/#comments</comments>
		<pubDate>Wed, 30 Jan 2008 23:17:24 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>
		<category><![CDATA[ColdFusion]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://www.stillnetstudios.com/2008/01/30/cf8-autosuggest-shows-through/</guid>
		<description><![CDATA[I was creating a form the other day that had 3 form fields, layed out vertically on top of one another.  I switched the first field to be a cfinput, so I could use the autosuggest feature:


Then I switched the other two fields to be cfinputs so I could add autosuggest to them, too. [...]]]></description>
			<content:encoded><![CDATA[<p>I was creating a form the other day that had 3 form fields, layed out vertically on top of one another.  I switched the first field to be a cfinput, so I could use the autosuggest feature:</p>
<p><img id="image83" src="http://www.stillnetstudios.com/wp-content/uploads/2008/01/cfinput-1.png" alt="cfinput example 1" /><br />
<span id="more-80"></span><br />
Then I switched the other two fields to be cfinputs so I could add autosuggest to them, too.  And that is where I ran into problems:</p>
<p><img id="image84" src="http://www.stillnetstudios.com/wp-content/uploads/2008/01/cfinput-2.png" alt="cfinput example 2 - showing through" /></p>
<p>This happens in IE 6 and 7, but not FireFox.  The problem occurs because when ColdFusion creates the <code>&lt;input&gt;</code> tag from a <code>&lt;cfinput&gt;</code>, it wraps it in two divs with a bit of related CSS.  There is a conflict with the way ColdFusion sets the CSS float and z-index properties.</p>
<p>Thanks to my co-worker, CSS guru <a href="http://jake.cfwebtools.com">Jake Churchill</a>, for figuring out a solution.  All that needs to be done is to re-layer the inputs by setting their zindex.  ColdFusion names the surrounding divs in an consistent way (thankfully they don&#8217;t use random names like some of the other ColdFusion JavaScript functions!), so we can just add a little CSS of our own to fix the problem.  The divs end up named like myformfield1autosuggest, myformfield2autosuggest, etc.  Knowing that, we can place this into our html page:</p>
<pre><code>&lt;style type="text/css"&gt;
	#field1autosuggest {
		z-index:3;
	}
	#field2autosuggest {
		z-index:2;
	}
	#test3autosuggest {
		z-index:1;
	}
&lt;/style&gt;</code></pre>
<p>Even better, here is a JavaScript script file you can reference in your page that will automatically fix these issues (thanks Jake!).</p>
<p><a id="p85" href="http://www.stillnetstudios.com/wp-content/uploads/2008/01/fixautosuggestjs.txt">Download here</a> (note: due to formatting issues that may occur on this page, you really should download the script using this link, instead of copying and pasting from this page)</p>
<p>The script contents:</p>
<pre><code>function fixAutoSuggest() {
	divs = document.getElementsByTagName("div");
	s = "&lt;style type="text/css"&gt;\n";
	counter = 1;
	for (x = divs.length-1; x &gt;= 0; x--) {
		if ( divs[x].getAttribute("id") &amp;&amp; divs[x].getAttribute("id").indexOf("autosuggest") &gt; -1) {
			s += "#"+divs[x].getAttribute("id")+" {z-index:"+(counter++)+"!important;}\n";
		}
	}
	s += "&lt;/style&gt;";
	document.write(s);
};</code></pre>
<p>You must then call this function <em>after</em> your form elements have been written to the browser.  So &#8211; after your closing form tag, put this:</p>
<p><code>&lt;script type="text/javascript"&gt;fixAutoSuggest();&lt;/script&gt;</code></p>
<p>It does <em>not</em> work to simply call the function in the body&#8217;s onload attribute.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/cf8-autosuggest-shows-through/feed/</wfw:commentRss>
		<slash:comments>21</slash:comments>
		</item>
		<item>
		<title>My GreaseMonkey Script: craigslist spam buttons</title>
		<link>http://www.stillnetstudios.com/my-greasemonkey-script-craigslist-spam-buttons/</link>
		<comments>http://www.stillnetstudios.com/my-greasemonkey-script-craigslist-spam-buttons/#comments</comments>
		<pubDate>Thu, 08 Feb 2007 14:10:48 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://www.stillnetstudios.com/2007/02/08/my-greasemonkey-script-craigslist-spam-buttons/</guid>
		<description><![CDATA[Last weekend I wrote my first GreaseMonkey script.  If you are unfamiliar with GreaseMonkey, its a FireFox extension that lets you run specific JavaScripts on specifc webpages.  This is incredibly cool, you can basically bend websites to whatever you want.  Everything from changing the look and feel to adding new functionality.  [...]]]></description>
			<content:encoded><![CDATA[<p>Last weekend I wrote my first GreaseMonkey script.  If you are unfamiliar with GreaseMonkey, its a FireFox extension that lets you run specific JavaScripts on specifc webpages.  This is incredibly cool, you can basically bend websites to whatever you want.  Everything from changing the look and feel to adding new functionality.  GreaseMonkey puts <em>you</em> back in control of your browsing experience.  A neat example is the GM script that, when you are browsing Amazon, will put a link to the book at your local library and indicate if its available there.</p>
<p>The GreaseMonkey script I wrote works with Craigslist.org.  Craigslist is a popular free classifieds site.  Craigslist&#8217;s posts are not moderated, so anyone can post an ad.  That includes spammers.  Craigslist spam is mostly controlled by craigslist users themselves.  See an ad thats spam?  Click the spam button.  If enough people mark it as such, it automatically gets taken down.  I really like CL, so I do my part by flagging any spam I see.  To do this you must click on the item title from the list, then click the spam button on the detailed listing.  But I can often identify spam just from the title.  Posts like &#8220;FREE 4gb ipod nano!!&#8221; and &#8220;Take Your New Pair of Uggs!&#8221; are obvious spam.<br />
<span id="more-32"></span><br />
So my script adds the flag-as-spam buttons to the listing page, so you don&#8217;t have to click onto the detail page of each item to flag it.</p>
<p>You can get my script from <a href="http://userscripts.org/scripts/show/7399">userscripts.org</a>, an archive of GreaseMonkey scripts.  Here is what it looks like:</p>
<p>Before:<br />
<img id="image33" src="http://www.stillnetstudios.com/wp-content/uploads/2007/02/gm_before.gif" alt="Before GreaseMonkey script" style="border: 1px solid black;" /></p>
<p>After:<br />
<img id="image34" src="http://www.stillnetstudios.com/wp-content/uploads/2007/02/gm_after.gif" alt="After GreaseMonkey script"/ style="border: 1px solid black;" ></p>
<p>And the <em>great</em> news that I discovered while writing this is that a lot of GreaseMonkey scripts run in <a href="http://www.opera.com">Opera</a>, my favorite web browser.  So I made sure to write this one in a way that worked in Opera.  To use this in Opera, go to the Tools menu, then Preferences -> Advanced -> Content -> JavaScript options, and choose a directory for the &#8220;User Javascript files&#8221; field.  Then drop <a href="http://userscripts.org/scripts/show/7399">my script</a> in that directory.  Thats it!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/my-greasemonkey-script-craigslist-spam-buttons/feed/</wfw:commentRss>
		<slash:comments>31</slash:comments>
		</item>
		<item>
		<title>Animated in-progress indicator for long running pages</title>
		<link>http://www.stillnetstudios.com/animated-in-progress-indicator-for-long-running-pages/</link>
		<comments>http://www.stillnetstudios.com/animated-in-progress-indicator-for-long-running-pages/#comments</comments>
		<pubDate>Sun, 04 Feb 2007 06:23:33 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://ryan.cfwebtools.com/2007/02/04/animated-in-progress-indicator-for-long-running-pages/</guid>
		<description><![CDATA[On pages that take more than a few seconds to complete, its a good idea to show some type of in-progress meter.  Things like bulk email processing and large file uploads are good candidates.  Often this doesn&#8217;t need to be as complicated as a real progress meter, where you show the actual percentage [...]]]></description>
			<content:encoded><![CDATA[<p>On pages that take more than a few seconds to complete, its a good idea to show some type of in-progress meter.  Things like bulk email processing and large file uploads are good candidates.  Often this doesn&#8217;t need to be as complicated as a real progress meter, where you show the actual percentage complete.  Instead, an &#8220;in progress&#8221; meter just gives feedback to the user to let them know that something is going on.<br />
<span id="more-26"></span><br />
I usually place a hidden element on the page, then use JavaScript to make it appear when the form is submitted.  Something like:</p>
<pre><code>&lt;p style="visibility:hidden" id="inprogress"&gt;
    &lt;img id="inprogress_img" src="progress.gif"&gt;
    Please Wait...
&lt;/p&gt;</code></pre>
<p><code>&lt;input type="submit" onclick="document.getElementById('inprogress').style.visibility='visible'"&gt;</code></p>
<p>Note that there is an image used in the in-progress message, this is important.  A static &#8220;Please Wait&#8221; message tells your user they should wait, but without the feedback of the moving image, they could still get the impression something is locked up.  You can get some awesome free progress meters from <a href="http://www.ajaxload.info/">http://www.ajaxload.info</a>.</p>
<p>Of course Internet Explorer throws a wrench into things.  IE will stop any animated gifs from animating when the form is submitted.  This is a problem in IE6 <em>and</em> 7.  This is far worse than having no progress meter, since it appears as if something has locked up.  But there is a work around.  We just need to kick start the animation after the form submits.  We do this by resetting the source of the image with JavaScript.  We have to reset the src <em>after</em> the form submits, and the form submits when our function returns.  So we make use of the setTimeout function, which executes a line of JavaScript after a specified number of milliseconds.  We&#8217;ll also throw in an anti-double submit feature similar to what I mentioned in my last post.</p>
<pre><code>&lt;script language="JavaScript"&gt;
var submitted = false;
function doSubmit() {
	if (! submitted) {
		submitted = true;
		ProgressImg = document.getElementById('inprogress_img');
		document.getElementById("inprogress").style.visibility = "visible";
		setTimeout("ProgressImg.src = ProgressImg.src",100);
		return true;
		}
	else {
		return false;
		}
	}
&lt;/script&gt;</code></pre>
<p><code>&lt;input type="submit" onclick="return doSubmit()"&gt;</code></p>
<p>If your form is long enough that it needs scroll bars, be sure to place the in-progress message where the user will see it when they click the submit button.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/animated-in-progress-indicator-for-long-running-pages/feed/</wfw:commentRss>
		<slash:comments>23</slash:comments>
		</item>
		<item>
		<title>Preventing multiple form submissions</title>
		<link>http://www.stillnetstudios.com/prevent-multiple-form-submissions/</link>
		<comments>http://www.stillnetstudios.com/prevent-multiple-form-submissions/#comments</comments>
		<pubDate>Tue, 30 Jan 2007 16:28:43 +0000</pubDate>
		<dc:creator>Ryan</dc:creator>
				<category><![CDATA[AJAX / JavaScript]]></category>
		<category><![CDATA[Web Development]]></category>

		<guid isPermaLink="false">http://ryan.cfwebtools.com/2007/01/30/prevent-multiple-form-submissions/</guid>
		<description><![CDATA[Here&#8217;s a small snippet to help prevent users from clicking the submit button more than once.  When this happens you can end up with duplicate data, or even multiple charges on the user&#8217;s credit card.  This trick uses JavaScript, and degrades nicely if JS is turned off.
&#60;input type="submit" value="Place Order"
onclick="if (this.value == 'Place [...]]]></description>
			<content:encoded><![CDATA[<p>Here&#8217;s a small snippet to help prevent users from clicking the submit button more than once.  When this happens you can end up with duplicate data, or even multiple charges on the user&#8217;s credit card.  This trick uses JavaScript, and degrades nicely if JS is turned off.</p>
<p><code>&lt;input type="submit" value="Place Order"<br />
onclick="if (this.value == 'Place Order') { this.value = 'Please Wait...'; return true;} else return false;"&gt;</code></p>
<p>On some applications it is also a good idea to redirect the user to a success page, instead of just displaying a success message.  That way they can&#8217;t accidentially refresh their browser and POST the form again.  This is complicated if you&#8217;re displaying a receipt or similar, where you need the data from the form post to create the success message.  You could stuff those values in the session in that case.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.stillnetstudios.com/prevent-multiple-form-submissions/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
