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

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