Simple jQuery AJAX w/ ColdFusion

I had a request to supply a sample HTML page that would send a subscriber’s email address to ColdFusion without reloading the page. So I figured I’d post it here for any others looking for a simple example. This method uses jQuery, AJAX and a ColdFusion component.

index.html or index.cfm

<!DOCTYPE html>
<html>
	<head>
	</head>
	<body>
		<div id="subscribeContainer">
			<input type="email"><button type="button">Subscribe</button>
		</div>
		<script src="//ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>

		<script>
			(function($) {
				// bind the button with an event handler
				$('#subscribeContainer button').click( function(e) {
					// when button is pushed, POST data to remote ColdFusion component method
					$.post(
						'subscription.cfc',
						{
							method: 'subscribe',
							email: $('#subscribeContainer input').val()
						}
						)
						.done( function() {
							// everything worked
							$('#subscribeContainer').text('You have been subscribed.');
						})
						.fail( function() {
							// something failed
							$('
<span>There was an error subscribing.</span>').appendTo('#subscribeContainer');
						}
					);
				});
			})(jQuery);
		</script>
	</body>
</html>

subscription.cfc

component {

	remote void function subscribe( required string email ) {
		// call database insert method here
	}

}

JavaScript Sort Object

I just created a JavaScript method that sorts a JavaScript object by key name case insensitive. Source at https://gist.github.com/CFJSGeek/5550678.

/**
 * Sort JavaScript Object
 * CF Webtools : Chris Tierney
 * obj = object to sort
 * order = 'asc' or 'desc'
 */
function sortObj( obj, order ) {
	"use strict";

	var key,
		tempArry = [],
		i,
		tempObj = {};

	for ( key in obj ) {
		tempArry.push(key);
	}

	tempArry.sort(
		function(a, b) {
			return a.toLowerCase().localeCompare( b.toLowerCase() );
		}
	);

	if( order === 'desc' ) {
		for ( i = tempArry.length - 1; i >= 0; i-- ) {
			tempObj[ tempArry[i] ] = obj[ tempArry[i] ];
		}
	} else {
		for ( i = 0; i < tempArry.length; i++ ) {
			tempObj[ tempArry[i] ] = obj[ tempArry[i] ];
		}
	}

	return tempObj;
}

#case-insensitive, #javascript, #object, #sort

jQuery UI Widget Factory Live Binding

I recently developed my first jQuery UI Custom Widget using the jQuery UI Widget Factory. I found the Widget Factory to be a nice framework for what I needed to do and took away a bit of the complexity normally needed for a UI plugin.

It did introduce a level of complexity for live event binding. For example, I want all dynamically generated anchor tags to run a method when clicked.

The Widget Factory wants us to use the “this._on” method to accomplish this. The Widget Factory is a little undocumented so it was hard to figure this one out.

At first I tried the following:

$.widget( "CFWT.myWidget", {

	_create: function() {
		this._on('.myClass', {
			click: function(event) {
				var $container = $(event.target);
				doSomething($container);
			}
		});
	}
	
	_doSomething: function(container) {
		console.log(container);
	}

});

However I quickly found out that the event was only bound with any classes created the first time. After some Q&A on Stack Overflow I finally figured out the correct way.

$.widget( "CFWT.myWidget", {

	_create: function() {
		this._on(this.element, {
			'click.myClass': function(event) {
				var $container = $(event.target);
				this._doSomething($container);
			}
		});
	}
	
	_doSomething: function($container) {
		console.log($container);
	}

});

This new code will bind any “myClass” class that is created inside the widget’s container. If you want it document wide then replace “this.element” with “document” or another container.

You may also notice that I use “$(event.target)” instead of “this”. That is because “this” doesn’t change to the event. Instead, I believe, it remains at the widget level, which can be pretty confusing.

A working example can be found at http://jsfiddle.net/vEAhq/8/

#bind, #event, #jquery, #live, #ui, #widget

Sublime Tern

Thanks to Emmet for releasing the “Sublime Tern” package. It implements TernJS into Sublime Text.

“TernJS is a JavaScript type inference engine written by Marijn Haverbeke. It analyses your JS code on-the-fly and provides autocompletion, function argument hints, jump-to-definition, and various automatic refactoring operations.”

I like stuff that “just works”, but since this package is pretty new it lacks a bit of documentation and Q&A.

I did also try the newly released official “TernJS for Sublime” package but that just became complicated compared to Emmet’s version and seemed to slow the editor way the heck down. So I uninstalled that rather quickly. I’m sure it’ll become much better as time goes on.

If you take a look at the TernJS Demo, you’ll see right away that using Ctrl-Space after the characters “co” will auto-complete to “console” or “confirm”. This is something I expected to “just happen” in the Sublime Tern package. But to my horror, it appeared that it just didn’t work.

After some discussion in GitHub, I finally figured out that you have to add some libraries into the project settings, which is kind of annoying. But I did just see that they released a new update to GitHub just now that will accept defaults for library loading which will be quite nice.

But basically here’s the answer to solve what appeared to be “broken” to me. Add this config object to your sublime-project file (Project > Edit Project):

"ternjs": { "libs": ["browser"] }

But I almost always use jQuery so I also added that:

"ternjs": { "libs": ["browser", "jquery"] }

Now I see the auto-completion options I was looking for.

If you program in JavaScript I highly recommend installed one of the two TernJS packages.

The reason I looked so deeply into this was because Adobe’s Brackets implemented it and it was touted as a great feature to have!

#sublime-text, #ternjs

jQuery Issue With Multiple Selectors and clone()/appendTo()

On a current project, the website was and many interactive jQuery “modules” where supplied by a 3rd party designer. We I pieced it together with ColdFusion everything worked great on IE8+, FF and Chrome. However it was soon discovered that a large customer of the client couldn’t upgrade past IE7 at the moment. After running the JavaScript through jsLint, that fixed the majority of the issues. Heck, just a couple of commas out of place will make a nightmare out of IE7.

However there was an issue that took me a whole day to figure out. (We’re talking thousands of lines of code to debug with no error being thrown).

As a general description, the site is a customized e-commerce site in its basic functionality. It has a category navigation tree to the left, with a product result grid to the right. When an item on the grid is clicked, the grid is hidden and replaced by a product detail section populated via AJAX.

First of all, there where around 1,500 categories that where populated into the DOM on page load via an un-ordered list. It was then converted into a cascading tree. The way you’re supposed to do it for accessibility and SEO purposes right? Well, IE7 didn’t take too kindly to that and took about a whole minute to display. IE8+ took probably around a second.

To resolve this issue I created a jQuery UI Widget via the Widget Factory. This required upgrading the version of jQuery being used, and I chose the latest one at 1.9.1 that still supported IE7.

After the tree was created and implemented, it became clear that only one product in the product grid was showing as opposed to the 15 for each page.

I determined that jQuery 1.7.2 loaded the grid as expected, but both 1.8 and 1.9 versions only loaded one. The method used to populate the grid was to clone a hidden div, populate it, append it to the parent container and make it visible using data from an AJAX JSON data set. It was looped via an $.each() method.

Because there where no errors being thrown, and I needed that newer version of jQuery I started the process of elimination. That process took me to the bitter end. I found that I couldn’t use multiple elements inside a selector. For example:

$('#myDIV1,#myDIV2').hide();

The above would inhibit all but one cloned container to be displayed. The workaround was to split up the selectors:

$('#myDIV2').hide();
$('#myDIV2').hide();

This would only happen in IE7. IE8+, FF, and Chrome worked just fine. It also only happened in jQuery 1.8+.

I could reproduce this any time, ripping out most of the app down to the essentials.

However at this time, creating a basic app in another virtual site works just fine. I may try to figure it out at a later time, but right now I can’t duplicate the issue.

But I’m writing this in case anyone runs into this edge case and needs a workaround. Hopefully I can find the cause and submit a bug to the jQuery team.

#appendto, #clone, #ie7, #internet-explorer, #jquery, #ui

img Element error Event

If you develop and push to production, you most likely have experienced broken images on your dev instance. You may have also run into broken images on production due to user/admin error, etc. Wouldn’t it be nice if you could display a default placeholder image without having to take a hit on your file system, checking to see if it exists, before your load each image?

Thanks to Ryan Stille’s recent blog post, I have been made aware of a (not so great) solution. Apparently the img tag, along with others such as object, script and style have error events. We can listen for the error event and load a placeholder image in place of the default browser error image.

The catch is the event handler must be attached before the browser fires the error event. Also, the error event may not be correctly fired when the page is served locally. The error event relies on HTTP status codes and will generally not be triggered if the URL uses the “file:” protocol.

In simpler terms, the only solution I’ve found is to either place the event handler inline with the img tag, assign the img src via JavaScript or recursively search each image’s complete state or naturalWidth once the window is done loading. I’ve tried using “$().on()” and “$(“img”).error()” both after the document loads and inline before the element is loaded. However neither solution works, which doesn’t make much sense to me.

I am including multiple examples because this is not a one-solution-fits-all scenario.

The first working example displays a placeholder image using the inline method if the error event is thrown. Notice the onerror handler is reset when it’s run so you don’t run into an infinite loop if your placeholder image also fails.

<img src="images/myImage.jpg" alt="My Image" onerror="imageError(this)">

<script>
function imageError(element) {
    element.onerror='';
    element.src='/images/imgPlaceholder.png';
}
</script>

The second working example, also using the inline method, will call a script that will report the broken image and load the placeholder. The script returns an image with the proper image MIME type.

<img src="images/myImage.jpg" alt="My Image" onerror="imageError(this)">

<script>
function imageError(element) {
    element.onerror='';
    element.src='logBrokenImage.cfm?image=' + element.src';
}
</script>

The third working example uses JavaScript to load the image and displays a placeholder if that image fails to load.

<img id="myImage">

<script>
    $('img').one( 'error', function() {
        $(this).attr( 'src', '/images/imgPlaceholder.png' );
    });

    $('#myImage').attr( 'src', 'myImage.jpg' );
</script>

The final working example recursively searches through each image after the window loads. If it finds the state incomplete or the natural width of the image is 0, then it loads the placeholder image.

<img src="images/myImage.jpg" alt="My Image">

<script>
$(window).load(function() {
    $('img').each(function() {
        if ( !this.complete || ( !$.browser.msie && ( typeof this.naturalWidth == "undefined" || this.naturalWidth == 0 ) ) ) {
            this.src = '/images/imgPlaceholder.png';
        }
    });
});
</script>

#error, #event, #handler, #img, #javascript, #jquery, #src

jQuery UI Dialog Centering Issue

I’m working with some code that’s been worked over many, many times over the years. It could a thorough scrubbing, but that’s not in the scope of my request.

I just upgraded the jQuery UI to version 1.9.2 and jQuery to 1.8.3 and added a simple dialog.

test_dialogIt was expected that the dialog would center itself to the browser window. However it appeared to be centering itself to the html element and scrolling the page to make it centered if the content was longer than the window height.

Here’s the code:

<html>
<head>
    <title>My Title</title>
</head>
<body>
    <div id="userNoticeDialog" style="display: none;" title="Notice">
        test content
    </div>

    <script type="text/javascript">
        $("#userNoticeDialog").dialog();
    </script>
</body>
</html>

After a process of elimination and seeing that there was no doctype declared, I tried adding a doctype. That resolved the issue.

<!DOCTYPE html>
<html>
<head>
    <title>My Title</title>
</head>
<body>
    <div id="userNoticeDialog" style="display: none;" title="Notice">
        test content
    </div>

    <script type="text/javascript">
        $("#userNoticeDialog").dialog();
    </script>
</body>
</html>

I believe the reason was the lack of a DOCTYPE caused the (Chrome) browser to go into quirks mode, while the inclusion of the DOCTYPE caused the (Chrome) browser to go into standards mode. However, I’m not sure how to detect which mode the Chrome browser is currently in. Does anyone know?

#center, #dialog, #doctype, #html, #jquery, #ui

CKEditor 4 Required Field Validation

I’ve used FCK Editor for years, but now with the FireFox 17+ incompatibility it’s time to upgrade to CKEditor.

I ran into some “stupid challenges” along the way. For example I tired out my brain trying to get it to work with jQuery. It appears in CKEditor v3.x there was a jQuery adapter. But in version 4 it wasn’t included. I tried to reuse it, but that seems to fail horribly for me. So I finally gave up on that concept. If anyone has a good way to implement jQuery with CKEditor I’d love to hear from you!

To get things rolling a just call the ckeditor.js file and add the class “ckeditor” to my textarea.

<form>
    <textarea class="ckeditor" id="noticeMessage" name="message"></textarea>
</form>
<script type="text/javascript" src="ckeditor/ckeditor.js"></script>

Now I want to verify that there’s actual text entered. To do that I remove all HTML markup and return the length of the string returned from the CKEditor API. The “noticeMessage” for the “instances” variable is the ID of the “textarea” element.

<form>
    <textarea class="ckeditor" id="noticeMessage" name="message"></textarea>
</form>
<script type="text/javascript" src="ckeditor/ckeditor.js"></script>
<script type="text/javascript">
    $("form").submit( function() {
        var messageLength = CKEDITOR.instances['noticeMessage'].getData().replace(/<[^>]*>/gi, '').length;
        if( !messageLength ) {
            alert( 'Please enter a message' );
        }
    }
</script>

Now if we test this with the word “test” it returns 5 characters. Well that’s not good, because last I knew math “test” only contains 4 characters. If we remove “test”, the correct value of 0 is returned. In this case it really doesn’t matter much because I’m just looking to see if anything is there. But it was annoying me and could prove bad in the future for further tests if I forgot about the issue.

The reason for this is if you view the source it inserts a line return automatically during editing. Normally I can fix this quite easily with jQuery, but because that concept made my head explode, I had to find a different way.

The trim() method didn’t come along in JavaScript until version 1.8.1. So while my current browser users are okay, my older browsers users such as IE8 aren’t so lucky.

To resolve this issue, I test for the “trim()” prototype method. If it doesn’t exist I extend the String prototype object with a trim() method. So thanks to some guy with the alias “Timo” I’ve put this thinking into place. The length result of string “test” now returns an accurate value of 4.

<form>
    <textarea class="ckeditor" id="noticeMessage" name="message"></textarea>
</form>
<script type="text/javascript" src="ckeditor/ckeditor.js"></script>
<script type="text/javascript">
    /**
     * Compatibility fix for trim()
     * Browsers 3.5+, Safari 5+, IE9+, Chome 5+ and Opera 10.5+ support trim() natively
     * So we're detecting it before we override it
     * This is here because CK Editor isn't playing nice with jQuery
     * Thanks to http://blog.stevenlevithan.com/archives/faster-trim-javascript
     */
    if(!String.prototype.trim) {  
        String.prototype.trim = function () {  
            var c;
                 for (var i = 0; i < this.length; i++) {
                     c = this.charCodeAt(i);
                     if (c == 32 || c == 10 || c == 13 || c == 9 || c == 12) continue; else break;
                 }
                 for (var j = this.length - 1; j >= i; j--) {
                     c = this.charCodeAt(j);
                     if (c == 32 || c == 10 || c == 13 || c == 9 || c == 12) continue; else break;
                 }
                 return this.substring(i, j + 1);
        };  
    }

    $("form").submit( function() {
        var messageLength = CKEDITOR.instances['noticeMessage'].getData().replace(/<[^>]*>/gi, '').trim().length;
        if( !messageLength ) {
            alert( 'Please enter a message' );
        }
    }
</script>

#ckeditor, #javascript, #validation

Handling Expired Sessions via AJAX & FW/1

This is a followup to my “Framework One AJAX Method (FW/1)” post (https://christierney.com/2012/07/14/framework-one-ajax-method-fw1/).

Scenario:

  1. You use the session scope to define if a user is logged in or not
  2. You use jQuery AJAX to pull JSON data from FW/1 action URL’s
  3. The user’s session has expired after x minutes of inactivity after login
  4. If the session is expired the user is directed to a login page after trying to navigate

So what happens in this scenario? Instead of the expected JSON data your AJAX call receives the HTML of a login page with a status of 200. Can’t do too much with this.

Here’s a code example that will pass the client a 403 error (Forbidden) in the header and return no content. jQuery will then redirect the user to a login screen when it sees this status code.

First here’s a simlified FW/1 Application.cfc setupRequest() method:

void function setupRequest() {

	var reqData = getHTTPRequestData();

	if( structKeyExists( reqData.headers, 'X-Requested-With' ) && reqData.headers[ 'X-Requested-With' ] == 'XMLHttpRequest' && !structKeyExists( session, 'user' ) ) {
		getpagecontext().getresponse().setstatus( 403 );
		abort;
	}
}

This code detects if the call came from an AJAX request ( getHTTPRequestData().headers.X-Requested-With = ‘XMLHttpRequest’ ) and if the session still knows about the user. If it is an AJAX request and the user is not known, then set the status code of the return page to 403 and stop processing any more code. If you try to use throw instead of abort, it will overwrite the status code to 500.

The second simple example is the jQuery piece:

$( document ).ready( function() {

	$( this ).ajaxError( function( e, jqXHR, settings, exception ) {
		if( jqXHR.status == 403 ) {
			location.href = '?logout';
			throw new Error( 'Login Required' );
		} else if( !jqXHR.statusText == 'abort' && jqXHR.getAllResponseHeaders() ) {
			alert( 'There was an error processing your request.\nPlease try again or contact customer service.\nError: ' + jqXHR.statusText );
		}
	});

});

Here we are globally looking at all AJAX requests. Since the status code 403 is in the error class it will throw an error. The .ajaxError() method picks up this error and handles it.

If the status code is detected as a 403 (which we set in our ColdFusion code) then we direct the user to a logout page (which in turn directs to a login page) and throws a JS error. The throw statement is supposed to stop all JS processing, however if you have an error handler attached to the specific AJAX call, then that will still fire. The error message will just be seen if you are viewing the JS console.

If there’s another error caught it first looks to see if the request was aborted or if the user navigated away from the page. In these two cases I don’t want to display an error. If anything else is caught, I display a generic message.

#ajax, #coldfusion-2, #fw1, #jquery, #session

Hide A DOM Container Clicking Outside It Using jQuery

Let’s say we have a DIV container with some content in it. We want to hide that container if the user clicks outside its bounds. Pretty simple solution:

$( document ).mouseup( function( e ) {
    if( $( "#myDiv" ).has( e.target ).length === 0 ) {
        $( "#myDiv" ).hide();
    }
});

#click, #container, #div, #dom, #hide, #jquery