CURRENT PROJECTS
loading
CATEGORIES AND POSTS
loading
overset
DEVELOPMENT LOG FOR JIM PALMER
Posted 09/12/2010 in jquery


After scouring the intraweb for a suitable time slider, jQuery plugin or not, and not satisfied with extending the jQuery UI - I built this small and lightweight hour block slider control. I kept the design as simple as possible with the only CSS3 being the moz/webkit border radius - that and there are a few image assets.

Here is a demo of 3 blocksliders on dynamically loaded using specific hours as "blocks":

loading demo..


Lightweight: 4KB for the development version with comments - 2KB after Google Closure compiler

Assets & Source on Google Code Project Page: http://code.google.com/p/jquery-blockslider/source/browse/#svn/trunk
Example usage

step 1: simply include the blockslider.js after your jquery.js inclusion in the head tag:
...
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"
	type="text/javascript"></script> 
<script src="blockslider.js" type="text/javascript"></script> 
...
step 2: assign the blockslider to the block template html you wish to use in the jQuery onLoad:
$(function() {
	$('.blockslider').blockslider();
});
This is assuming the .blockslider selector is selecting a container div that contains at least the 3 core child divs: .block (1 or more for each block to select), .guide (only 1 for the slider guide), .cursor (only 1 for the slider cursor that is drag-able). There are several example layouts for these divs that I will showcase later. The layout is mostly controlled in the CSS for such things as the size of each block, the size of the cursor (i.e. the size of 1 block) and the size of the guide (i.e. width is equal to the total width of all the blocks). I plan on making this more dynamic in that you can include the CSS sizing of the blocks and the blockslider will auto-generate the slider controls without having to do it all by hand in the CSS.

step 3: to programmatically change the position of a blockslider:
$('.blockslider').blockslider(2);
This will work with multiple blockslider controls or one if you wish. The blockslider plugin function is the only function in the $.fn namespace and supports chaining and proper use of scope with multiple blocksliders.

step 4: to assign a callback function after a specific block is selected:
$('.blockslider:last').blockslider({
	initPosition: 8,
	moveCursorToCallback: function (pos) {
		$('.blockslider:last .status')
			.html($('.blockslider:last .block:eq(' + pos + ')').html());
	}
});


Core blockslider plugin source - blockslider.js
/*
 * blockslider.js - v0.1 - simple horitonal slider UI - jquery plugin - MIT license
 * Author: Jim Palmer
 */
(function ($) {
    $.fn.blockslider = function (options) {
        // if numeric argument, not object, move the cursor
        if (typeof options === 'number') {
            return this.each(function () {
                moveCursorTo(this, options);
            });
        }
        // initialize: iterate through selected items - chainable
        return this.each(function () {
            
            var target = this,
                opts = $.extend({
                    // initial position of the cursor - index of block
                    initPosition: 1,
                    // enable click events being attached to blocks
                    enableBlockClick: true,
                    // prevent cursor and callback being run if pos is same as current pos
                    disableDuplicatePos: true,
                    // callback function after cursor is moved
                    moveCursorToCallback: function (pos) { }
                }, options || {});
            
            // store the options for each selected element
            $(target).data('blockslider.opts', opts);
            
            // drag-drop support
            $('.cursor', target).unbind('mousedown.blockslider')
                .bind('mousedown.blockslider', {target:target}, function (e) {
                    clearEvents(e);
                    var guide = $('.guide', e.data.target),
                        lbound = guide.offset().left,
                        rbound = lbound + guide.outerWidth();
                    $(document).unbind('mousemove.blockslider').bind('mousemove.blockslider', {
                                target: e.data.target,
                                lbound: lbound,
                                rbound: rbound
                            }, function (e) {
                        clearEvents(e);
                        // check within bounds
                        if (e.pageX > e.data.lbound && e.pageX < e.data.rbound ) {
                            // step in X axis
                            var pos = Math.floor(
                                (e.pageX - e.data.lbound) / 
                                $('.cursor', e.data.target).outerWidth());
                            moveCursorTo(e.data.target, pos);
                        }
                    });
                    // add drag css class to cursor
                    $(this).addClass('cursorDrag');
                    // clear drag-drop sliding event handler
                    $(document).unbind('mouseup.blockslider')
                        .bind('mouseup.blockslider', {cursor:this}, function (e) {
                            clearEvents(e);
                            $(e.data.cursor).removeClass('cursorDrag');
                            $(document).unbind('mousemove.blockslider mouseup.blockslider');
                        });
                // add the hover for the cursor
                }).unbind('mouseover.blockslider mouseout.blockslider')
                .bind('mouseover.blockslider mouseout.blockslider',  function (e) { 
                    $(this).toggleClass('cursorHover');
                });
            
            // guide click location
            $('.guide', target).unbind('click.blockslider')
                .bind('click.blockslider', {target:target}, function (e) {
                    var cursor = $('.cursor', e.data.target),
                        curWidth = cursor.outerWidth(),
                        lbound = $(this).offset().left,
                        rbound = lbound + $(this).width();
                    if (e.pageX > lbound && e.pageX < rbound ) {
                        var pos = Math.floor((e.pageX - lbound) / curWidth);
                        moveCursorTo(e.data.target, pos);
                    }
                });
            
            // specific block click location
            $(target).children('.block', target).each(function (i) {
                $(this).unbind('click.blockslider')
                    .bind('click.blockslider', {target:target}, function (e) {
                        moveCursorTo(e.data.target, i);
                    }).mousedown(function (e) {
                        clearEvents(e);
                    });
            })
            .unbind('mouseover.blockslider mouseout.blockslider')
            .bind('mouseover.blockslider mouseout.blockslider', function () {
                $(this).toggleClass('blockHover');
            });
            
            // intialize the cursor
            moveCursorTo(target, opts.initPosition);
            
        });
    };
    function moveCursorTo (target, pos) {
        var opts = $(target).data('blockslider.opts') || {},
            blocks = $(target).children('.block');
        // check valid range in blocks
        if (pos < 0 || pos >= (blocks.length - 0))
            return true; // continue
        // duplicate the position click if already current position?
        if (opts.disableDuplicatePos && pos === opts.currPos)
            return true; // continue
        // select slider hour block
        $('.blockSelected', target).removeClass('blockSelected');
        blocks.eq(pos).addClass('blockSelected');
        var cursor = $('.cursor', target),
            cursorWidth = cursor.outerWidth();
        cursor.css('margin-left', (pos * cursorWidth));
        opts.currPos = pos;
        $(target).data('blockslider.opts', opts);
        if (typeof opts.moveCursorToCallback === 'function')
            opts.moveCursorToCallback(pos, blocks.eq(pos));
    };
    function clearEvents (e) {
        // prevent drag text selection of hour blocks
        document.selection && document.selection.empty();
        window.getSelection && window.getSelection().removeAllRanges();
        e.preventDefault && e.preventDefault();
        e.stopPropagation && e.stopPropagation();
    }
})(jQuery);
comments
loading
new comment
NAME
EMAIL ME ON UPDATES
EMAIL (hidden)
URL
MESSAGE TAGS ALLOWED: <code> <a> <pre class="code [tab4|tabX|inline|bash]"> <br>
PREVIEW COMMENT
TURING TEST
gravatar