Showing posts with label jQuery. Show all posts
Showing posts with label jQuery. Show all posts

Friday, February 10, 2012

jQuery Plugins (Non-jQuery UI Version)

I was (and still am) a fan of the jQuery UI widget template system (src1, src2, lmgtfy) for creating modular UI functionality. But if you're not a fan of the overhead of including the jQuery UI libraries, you can create the same effect with just plain 'ol jQuery plugins. You don't get all the bells and whistles of the widget factory, but as you can see, the set up is still very straightforward.

(function ($) {

    var settings = {
        // ...
    };

    function _private(options) {
    }

    var methods = { //public methods
        init: function (options) {
            $.extend(settings, options || {});
            return this..each(function () {
                // ...
                // use settings
                // ...
                // call _private
                // ...
            });
        },
        destroy: function () {
            // ...
            // unbind events, remove DOM objects, etc
            // ...
        },
        f1: function (args) {
            // ...
        },
        // ...
        fN: function (args) {
            // ...
        }
    };

    $.fn.pluginName = function (optionsOrMethod) {
        if (methods[optionsOrMethod]) {
            return methods[optionsOrMethod].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof optionsOrMethod === 'object' || !optionsOrMethod) {
            return _init.apply(this, arguments);
        } else {
            $.error('Method [' + optionsOrMethod + '] does not exist');
        }
    };

})(jQuery);

Monday, January 16, 2012

jQuery Pan & Zoom for Images and Maps

This is a nice little gadget for large image viewing:
http://wayfarerweb.com/jquery/plugins/mapbox/

I'm thinking of adding a few improvements:

  1. "Smoothing out" transitions between zoom levels
  2. Support for HTML5 gestures allowing pinch-to-zoom on mobile devices
  3. Kinetic scrolling/panning
Updates to come...

Tuesday, September 20, 2011

jQuery Select Consecutive Elements

Use jQuery .slice(). For example:

// select 2nd thru 4th list items
$('li').slice(1, 3);

In fact, you have to use slice in order to select the last, say, 5, elements of a set:

// select last 5 divs of a set
$('div').slice(-5);

Monday, September 19, 2011

jQuery Simple Date Selector

I was trying forever to find a simple javascript date selector that would do well on mobile applications. Couldn't find one, so I rolled my own. It takes a text or hidden input element and inserts three select elements after it, one each for months, days, and the year. The onchange event for each select copies the selected date back to the input, and the selects are initialized from the input on page load.

The code below should be enough to give you the idea.

The selector follows the jQuery UI widget template (another good widget tutorial here).

// attach widget to input
$(document).ready(function () { 
	$('#mydate').dateselector(); 
});

// some display formatting
Date.prototype.toMMDDYYYY = function () {
    return isNaN(this) ? 'NaN' : [this.getMonth() > 8 ? this.getMonth() + 1 : '0' + (this.getMonth() + 1), this.getDate() > 9 ? this.getDate() : '0' + this.getDate(), this.getFullYear()].join('/')
}

// date selector widget
; (function ($) {
	$.widget("ui.dateselector", {
		options: {},
		shortMonths: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
		_syncToSelector: function () {
			var self = this;
			var date = new Date($(self.element).val());
			self.$container.find('select:eq(0)').val(date.getMonth());
			self.$container.find('select:eq(1)').val(date.getDate());
			self.$container.find('select:eq(2)').val(date.getFullYear());
		},
		_syncFromSelector: function () {
			var self = this;
			var date = new Date();
			date.setFullYear(self.$container.find('select:eq(2)').val());
			date.setDate(self.$container.find('select:eq(1)').val());
			date.setMonth(self.$container.find('select:eq(0)').val());
			$(self.element).val(date.toMMDDYYYY());
		},
		destroy: function () {
			var self = this;
			if (self.$container) self.$container.remove();
			$.Widget.prototype.destroy.apply(this, arguments);
		},
		_init: function () {
			var self = this;

			var $months = $('<select></select>').change($.proxy(this._syncFromSelector, this));
			for (var i = 0; i &lt; 12; i++) { $('<option></option>').val(i).text(this.shortMonths[i]).appendTo($months); }

			var $days = $('<select></select>').change($.proxy(this._syncFromSelector, this));
			for (var i = 1; i &lt;= 31; i++) { $('<option></option>').val(i).text(i).appendTo($days); }

			var thisYear = new Date().getFullYear();
			var $years = $('<select></select>').change($.proxy(this._syncFromSelector, this));
			for (var i = -2; i &lt;= 2; i++) { $('<option></option>').val(thisYear + i).text(thisYear + i).appendTo($years); }

			self.$container = $('<span class="dateselectorContainer"></span>').append($months).append($days).append($years);

			this._syncToSelector();

			self.element.hide().after(self.$container);
		}
	});
})(jQuery);

Tuesday, September 13, 2011

(Smart) jQuery Ajax Indicator

Any fool can hook into the jQuery's ajaxStart and ajaxStop functions in order to display an "ajax request in progress" indicator. But what if we want to suppress the display for requests that finish in less than X number of milliseconds? And what if we want to maintain the display actively for Y number of milliseconds (to prevent "bouncing")? Look no further. The markup:

<html>
	<head><!--blah blah--></head>
	<body>
		<div id="notify-container">
			<div class="ui-corner-bl ui-corner-br" id="ajax-loading" style="display: none;">
				Loading...</div>
			</div>
		<div>
		<!--blah blah-->
	</body>
</html>

Throw in some styling:

#notify-container 
{ 
	position: fixed; 
	left: 0; 
	top: 0; 
	width: 100%; 
	height: 0; 
	z-index: 100; 
}
#ajax-loading 
{ 
	position: absolute; 
	display: inline-block; 
	width: 100px; 
	left: 50%; 
	margin-left: -50px; 
	padding: 0.5em 1em 0.6em 1em; 
	font-weight: bold; 
	font-size: 1.1em; 
	text-align: center; 
	background-color: #ffe79c; 
	border: 1px solid #c98400; 
}

Any type of indicator works, really. I prefer a Google-esqe div that slides down from the top of the window, but it could be a spinner gif or an opaqued modal window. Doesn't matter; that's not the relevant part. Here's the magic:

$(document).ready(function () {
	// ajax working indicator
	$('#ajax-loading')
		.ajaxStart(function () {
			var $this = $(this);
			clearTimeout($this.data('waitingToHide'));
			$this.data('waitingToShow', setTimeout(function () { $this.slideDown(); }, 600));
		})
		.ajaxStop(function () {
			var $this = $(this);
			clearTimeout($this.data('waitingToShow'));
			$this.data('waitingToHide', setTimeout(function () { $this.slideUp(); }, 2000));
		});
})

Each call to ajaxStart and ajaxStop introduces the respective show and hide animations via a setTimeout delay. The function handler is saved to the jQuery element's data() store.

The real trick here is that before this happens, the data() store is checked and cleared of any pending animations. In this way, if ajaxStop is called before the show animation, the show animation will be "short-circuited". Similarly, if another call to ajaxStart occurs while the hide animation is pending, the hide animation will be cancelled.