Programmatically Animated Page Scroll with jQuery

With the increasing use of AJAX to populate a page without reloading it, you come to find out that you’d like to get your users back to a certain point in the page, if not the top. If you where to navigate to a new page via the browser, you always end at the top. But when just replacing a container with new information this doesn’t happen. Depending upon your scenario, this may be a good idea for usability.

The following code snippet is a simple example. It will detect if your user is below 210 pixels on the screen. If you are then it will animate the page, scrolling it up to the 210px mark. You may want to use an extended jQuery .position() method to find the top of your #result container instead of using a static page height value.

$.get( 'ajax/test.html', function( data ) {
    $( '#result' ).html( data );

    if ( $( window ).scrollTop() > 210) {
        $( "html, body" ).animate({
            scrollTop: 210
        }, "slow" );
    }
});

#jquery, #scroll

Accessing JavaScript Methods From Outside Its Self-Invoking Function Wrapper

Let’s say you have a method you want to be globally accessible, but want a self-invoking function around it to make sure it’s jQuery $ alias safe.

In this example, the following code is in global.js:

(function( $ ) {
   function showLoader( cfg ) {
      if( cfg == 'show') {
         $( '.loader' ).show();
      } else //more code...
   }
})(jQuery);

In this example, the following code is in myPage.js:

(function($) {
   showLoader( 'show' );
})(jQuery);

You will find that myPage.js cannot access the showLoader() method even though global.js was called first in the HTML page.

The fix for this is to push the showLoader() method out to the window (global) scope:

(function( $ ) {
   window.showLoader = function( cfg ) {
      if( cfg == 'show') {
         $( '.loader' ).show();
      } else //more code...
   }
})(jQuery);

By assigning the function to the window scope it is now globally accessible. Just remember that it is now no longer protected by the closure but it still has access to the jQuery $ alias.

jQuery Select All Checkboxes

I needed to come up with a generic way of checking all checkboxes that could be reused using jQuery. At first I thought about using the data- attribute to define which class of checkboxes to check. But then found a great way to just check all checkboxes that are located in their element container. (The .on method required jQuery 1.7)

HTML Example Code:

<div class="control-group">
<input type="checkbox" class="selAllChksInGroup"> All
<input type="checkbox" value="NE"> Nebraska
<input type="checkbox" value="FL"> Florida
</div>

JavaScript Code:

$(document).ready(function(){

$("input[type=checkbox].selAllChksInGroup").on("click.chkAll", function( event ){
    $(this).parents('.control-group:eq(0)').find(':checkbox').prop('checked', this.checked);
});

});

jQuery Galleriffic Plugin History Update

Using the Galleriffic version 2.0.1 you will find it incompatible with the 2010-09-11 jQuery History plugin. It currently only works with the 2009 history plugin version.

When you insert the pageload function and its calls into your HTML, update it to the following. Lines 3 and 15 are updated.

// Initialize history plugin.
 // The callback is called at once by present location.hash.
 $.history.init(pageload);

// set onlick event for buttons using the jQuery 1.3 live method
 $("a[rel='history']").live('click', function(e) {
 if (e.button != 0) return true;

 var hash = this.href;
 hash = hash.replace(/^.*#/, '');

// moves to a new page.
 // pageload is called at once.
 // hash don't contain "#", "?"
 $.history.load(hash);

return false;
 });

Then update jquery.galleriffic.js:
Line 495:

$.history.load(String(imageData.hash));

Line 868:

if (this.enableHistory && !$.history.init)

jquery.validate Range Method Patch

If you use the range attribute inline to validate against using the jquery.validate plugin 1.8.1, your validation will probably always fail and receive the message “Please enter a value between NaN and {1}.” or “Please enter a value between {0} and NaN.”.

This drove me nuts for almost a whole day looking for a fix. I finally got smart and looked at the current issues in the Github project. “markusblair” submitted a patch, but that was also incorrect.

To correct this issue, modify the jquery.validate.js file on line 852:

		$.each(['rangelength', 'range'], function() {
			if (rules[this]) {
				rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
			}
		});

and replace it with:

		// Patch by Chris Tierney (CF Webtools 7/13/2011)
		// Issue logged at https://github.com/jzaefferer/jquery-validation/issues/112
		$.each(['rangelength', 'range'], function() {
			if (typeof rules[this] == 'string') {
				rules[this] = rules[this].split(",");
			}
			if (rules[this]) {
				rules[this] = [Number(rules[this][0]), Number(rules[this][1])];
			}
		});

Be sure to update your minified version as well and this should solve the range and potentially a similar rangelength method issue immediately.

jquery.validate Better Date Method

The jquery.validate plugin currently has the validation method “date” which will correctly validate the format “xx/xx/xxxx”. However you can enter a date such as “40/01/2011” and it will pass. Obviously there is no month 40 or even day 40.

Borrowing from the “dateITA” validation method in the additional-methods.js I created a “dateUS” validation method that will detect for not only a correctly US formatted date but a valid date as well using the “mm/dd/yyyy” format.

Add this to your additional-methods.js file or load in another methods file (ex:my-methods.js):

/**
  * Return true, if the value is a valid date, also making this formal check mm/dd/yyyy.
  *
  * @example jQuery.validator.methods.date("01/01/1900")
  * @result true
  *
  * @example jQuery.validator.methods.date("13/01/1990")
  * @result false
  *
  * @example jQuery.validator.methods.date("01.01.1900")
  * @result false
  *
  * @example <input name="pippo" class="dateUS" />
  * @desc Declares an optional input element whose value must be a valid date.
  *
  * @name jQuery.validator.methods.dateUS
  * @type Boolean
  * @cat Plugins/Validate/Methods
  */

jQuery.validator.addMethod(
	"dateUS",
	function(value, element) {
		var check = false;
		var re = /^\d{1,2}\/\d{1,2}\/\d{4}$/;
		if( re.test(value)){
			var adata = value.split('/');
			var mm = parseInt(adata[0],10);
			var dd = parseInt(adata[1],10);
			var yyyy = parseInt(adata[2],10);
			var xdata = new Date(yyyy,mm-1,dd);
			if ( ( xdata.getFullYear() == yyyy ) && ( xdata.getMonth () == mm - 1 ) && ( xdata.getDate() == dd ) )
				check = true;
			else
				check = false;
		} else
			check = false;
		return this.optional(element) || check;
	},
	"Please enter a date in the format mm/dd/yyyy"
);

Use “dateUS” as your validation method.
Example: <input type=”text” class=”dateUS” name=”myDate”>

Returning Multiple Value Elements to ColdFusion Remote Method via jQuery AJAX

Lets say I have a select element that supports multiple selected values:

<form>
<select id="mySelect" multiple size="3">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</form>

I wish to return this information to my ColdFusion CFC that contains a remote method via AJAX:

$.ajax({
	type: 'POST',
	url: 'myCFC.cfc',
	dataType: 'JSON',
	data: {
		'method':'myMethod',
		'value':$('mySelect').val()
	}
});

My CFC method looks like:

<cffunction name="myMethod" access="remote" output="false">
	<cfargument name="value" required="true" type="string">

	<cfset local.myVar = arguments.value>
	<!--- more logic --->
</cffunction>

This solution will error out due to the fact that the key “value” passed via AJAX is actually passed as “value[]”. Note the appended brackets denoting an array. I can’t reference “#arguments.value[]#” as this tries referencing an array and “#arguments[‘value[]’]#” is not allowed.

I tried all sorts of ways to try and get at that key value in the method. The only way I could come up with was to reference the argument via an index such as “arguments[1]”. But my real code was too complex to rely upon this method.

Another way was to append “.toString()” call the the end of the “val()” call when retrieving the values selected. This converts the array of values to a comma-delimited string of values. However this method will not allow the use of commas.

What I ended up doing was inserting some logic to convert any arrays into a pipe-delimited list. It’s not the best solution in the world but I think it’ll work.

var thisVal = $(this).val();
if ($.isArray(thisVal))
	var thisValRtn = thisVal.join("|");
else
	var thisValRtn = thisVal;

$.ajax({
	type: 'POST',
	url: 'myCFC.cfc',
	dataType: 'JSON',
	data: {
		'method':'myMethod',
		'value':thisValRtn
	}
});

If you have a better method than this, I’d love to hear about it.

What Constitutes an Attribute or Property in jQuery 1.6.1

jQuery 1.6.1 RC1 was released today, helping resolve the .attr() backwards compatibility issue along with a few other fixes.

jQuery.com released a nice chart on what .attr() and .prop() should be used for going forward from jQuery 1.6+. I thought I’d publish this for future reference. Note that all properties can still be accessed via 1.6.1, but not necessarily via 1.6.

According to jQuery.com the .prop() method should be used for boolean attributes/properties and for properties which do not exist in html (such as window.location). All other attributes (ones you can see in the html) can and should continue to be manipulated with the .attr() method.

Attribute Property
accesskey async
align autofocus
class checked
contenteditable location
draggable multiple
href readOnly
id selected
label
rel
src
tabindex
title
type
width

Understanding jQuery 1.6’s DOM Attribute and Properties

jQuery 1.6 released a significant change to the way it handles DOM attributes and properties.

Take for example in version 1.5.2, the following code would return isCheckedAttr as “true”.

<input type="checkbox" checked="checked" />

<script type="text/javascript">
var isCheckedAttr = $('input').attr("checked");
</script>

In version 1.6 the addition of .prop() and .removeProp() changes what the .attr() method will return when referencing DOM properties. The following example will return isCheckedAttr as “” and isCheckedProp as “true”. Notice 1.5.2 returned “true” instead of “” for isCheckedAttr.

<input type="checkbox" checked="checked" />

<script type="text/javascript">
var isCheckedAttr = $('input').attr("checked");
var isCheckedProp = $('input').prop("checked");
</script>

Now… check this out. In HTML and XHTML you can set the checked property as an attribute. This example in jQuery 1.6 will return “checked” for isCheckedAttr (remember 1.5.2 returned “true”). The isCheckedProp will still return “true”.

<input type="checkbox" checked="checked" />

<script type="text/javascript">
var isCheckedAttr = $('input').attr("checked");
var isCheckedProp = $('input').prop("checked");
</script>

Try this case on for size… If I don’t set the checked attribute or property isCheckedAttr will always return “undefined” (not an empty string) and isCheckedProp will return false (until I click the checkbox which will make it true).

<input type="checkbox" />

<script type="text/javascript">
var isCheckedAttr = $('input').attr("checked");
var isCheckedProp = $('input').prop("checked");
</script>

This all makes sense, though a bit confusing since the attribute is basically converted into a property at DOM load making upgrading to version 1.6 much more difficult than most recent version upgrades, so be sure to do some extra testing.

Okay, ready for this, one more item to throw at you… if I’m reading the bug ticket right, if an attribute does not exist but the property does, the .attr() will bring back the property boolean value as the attribute in the next release (probably 1.6.1). This appears to be a backwards compatibility fix for reading properties, but not setting them.

UPDATE 5/9/2011: appendTo, a jQuery company I highly respect, released an executive brief on jQuery 1.6 stating that it recommends not upgrading to jQuery 1.6 until 1.6.1 is released. This version will return either the name of the attribute (if true) or undefined (if false) for .attr(BooleanAttribute) and could be released as early as Tuesday, May 10, 2011. It also has a good read on the rest of the features new to this version.

John Resig also released an example:

“Let’s pretend we’re working with a checkbox that isn’t checked at all:

<input type="checkbox" id="c"/>

$("#c").attr("checked") => false // 1.5.2
$("#c").attr("checked") => undefined // 1.6
$("#c").attr("checked") => undefined // 1.6.1

The user then checks the checkbox OR we set the check by doing .prop(“checked”, true) OR we set the check by doing [0].checked = true, the result would be:

$("#c").attr("checked") => true // 1.5.2
$("#c").attr("checked") => undefined // 1.6
$("#c").attr("checked") => "checked" // 1.6.1

Note that if we had done .attr(“checked”, “checked”) OR .attr(“checked”, true) to set the checked property then .attr(“checked”) would’ve returned “checked” in both 1.6 and 1.6.1.

In short: In jQuery 1.6.1 .attr(BooleanAttribute) now returns either the name of the attribute (if true) or undefined (if false). Which is consistent with the rest of the API and with the DOM itself.

I should note that all of this is a moot point if you’re using .prop(“checked”) – that will just return true/false consistently based upon the current checked state of the checkbox.”

Playing Interference with jQuery and Prototype

Lately I’ve had to deal with jQuery conflicting with Prototype.

For starters I was forced to upgrade from jQuery 1.4.2 to 1.5.2 to fix a conflict when doing clones.

But of course there’s always the ‘$’ conflict. So I wanted to come up with a slick way of keeping the ‘$’ alias available for use by jQuery code.

For starters the existing code looks like this:

<script src="prototype.js" type="text/javascript"></script>
<script src="jQuery.min.js" type="text/javascript"></script>
<script type="text/javascript">
	jQuery.noConflict();
	jQuery(document).ready(function(){
		/* js code here */
	});
</script>

The ‘jQuery.noConflict()’ statement kills off the jQuery ‘$’ alias for me. So to have ‘$’ available for my use I wrapped everything around a Self-Invoking Anonymous Function looking like this:

(function( $ ) {
	$(document).ready(function() {
		/* js code here */
	});
})(jQuery);

https://gist.github.com/firefighter990/5340332

Notice that the jQuery object is now being passed in via the $ argument inside a JavaScript closure. Thus everything I do inside the closure will not conflict with any other library.