finderSelect jQuery & Zepto Plugin. Only 6kb minified.

Provides the ability to add file manager (Finder) type highlighting to all elements based on the standard single click, command+click/ctrl+click, command+drag/ctrl+drag, command+A/ctrl+A and shift+click methods. This plugin will by default add a `selected` class to all clicked elements.

View on Github

Now hosted via CDN thanks to the team at cdnjs.com

<script src="//cdnjs.cloudflare.com/ajax/libs/jquery.finderselect/0.6.0/jquery.finderselect.min.js"></script>

Also available on Open Source Software CDN by MaxCDN

<script src="//oss.maxcdn.com/libs/jquery.finderselect/0.6.0/jquery.finderselect.min.js"></script>

Tested on IE6+, Firefox, Safari & Chrome. Single click only on touch devices.

Complex Usage - Folder Structure

This complex demo shows how finderSelect can be used to create a nested file structure.

Images
Icons
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
$(function() {
var demo11 = $('#demo11').finderSelect({children:"tr.expanded"});
        var activeSegments = [""];
        function expandActive(els) {
            els.each(function() {
                var el = $(this);
                var segments = el.attr('data-path').split("/");
                segments.pop();
                var padding = (segments.length - 1) * 20;
                if($.inArray(segments.join("/"), activeSegments)  > -1) {
                    el.find('td').first().css('padding-left',padding);
                    el.removeClass('hidden')
                    el.addClass('expanded');
                } else {
                    el.addClass('hidden');
                    el.removeClass('expanded');
                }
            });

        }

        expandActive($('#demo11').find('tr'));

        $( "#demo11").find("tr.folder").dblclick(function() {
            if($.inArray($(this).attr('data-path'), activeSegments)  > -1) {
                for(var i = activeSegments.length - 1; i >= 0; i--) {
                    if(startsWith(activeSegments[i], $(this).attr('data-path'))) {
                        activeSegments.splice(i, 1);
                    }
                }
                $(this).find('.icon-folder-open').removeClass('icon-folder-open').addClass('icon-folder-close');
                $(this).find('.icon-angle-down').removeClass('icon-angle-down').addClass('icon-angle-right');
            } else {
                activeSegments.push($(this).attr('data-path'));
                $(this).find('.icon-folder-close').removeClass('icon-folder-close').addClass('icon-folder-open');
                $(this).find('.icon-angle-right').removeClass('icon-angle-right').addClass('icon-angle-down');
            }

            expandActive($('#demo11').find('tr'));
        });
});

Complex Usage - AJAX Folder Structure of Jquery Github Repo

This complex demo shows how finderSelect can be used to create a file browser powered by an external service such as the Github API in the below example.

0 File(s) Selected  



$(function() {
        var demo10 = $('#demo10').finderSelect({children:"tr.expanded"});

        var demo10BaseUrl = 'https://api.github.com/repos/jquery/jquery/contents/';
        var baseDir = "/";
        var gitHubLoaded = [""];

        function reloadGithub(els) {
            els.each(function() {
                var el = $(this);
                var segments = el.attr('data-path').split("/");
                segments.pop();
                var padding = (segments.length - 1) * 20;
                if($.inArray(segments.join("/"), gitHubLoaded)  > -1) {
                    el.find('td').first().css('padding-left',padding);
                    el.removeClass('hidden')
                    el.addClass('expanded');
                } else {
                    el.remove();
                }
            });

        }

        $.getJSON(demo10BaseUrl ,function(result){

            $.each(result, function(i, field){

                if(field.type == 'dir') {
                    $('#demo10').append('<tr data-path="'+baseDir + field.path+'" class="folder">' +
                            '<td colspan="3">'+
                            ' <i class="icon-angle-right icon-fixed-width"></i> ' +
                            '<i class="icon-folder-close icon-fixed-width"></i>' +
                            field.name +
                            '</td></tr>');
                } else {
                    $('#demo10').append('<tr data-path="'+baseDir + field.path+'" class="file" data-url="http://upload.wikimedia.org/wikipedia/en/d/d0/Hello.png">' +
                            '<td><i class="icon-fixed-width icon-none"></i> <i class="icon-file icon-fixed-width"></i> '+field.name+'</td>' +
                            '<td>'+ field.sha +'</td>' +
                            '<td>'+ field.size + '</td>' +
                            '</tr>');
                }
            });
        });

        reloadGithub($('#demo10').find('tr'));

        $( "#demo10" ).on( "dblclick", "tr.folder", function() {
            var clicked = $(this);
            if($.inArray($(this).attr('data-path'), gitHubLoaded)  > -1) {
                for(var i = gitHubLoaded.length - 1; i >= 0; i--) {
                    if(startsWith(gitHubLoaded[i], $(this).attr('data-path'))) {
                        gitHubLoaded.splice(i, 1);
                    }
                }
                $(this).find('.icon-folder-open').removeClass('icon-folder-open').addClass('icon-folder-close');
                $(this).find('.icon-angle-down').removeClass('icon-angle-down').addClass('icon-angle-right');
                reloadGithub($('#demo10').find('tr'));
            } else {

                $.getJSON(demo10BaseUrl+$(this).attr('data-path') ,function(result){

                    $.each(result, function(i, field){

                        if(field.type == 'dir') {
                            clicked.after('<tr data-path="'+baseDir + field.path+'" class="folder">' +
                                    '<td colspan="3">'+
                                    ' <i class="icon-angle-right icon-fixed-width"></i> ' +
                                    '<i class="icon-folder-close icon-fixed-width"></i>' +
                                    field.name +
                                    '</td></tr>');
                        } else {
                            clicked.after('<tr data-path="'+baseDir + field.path+'" class="file" data-url="http://upload.wikimedia.org/wikipedia/en/d/d0/Hello.png">' +
                                    '<td><i class="icon-fixed-width icon-none"></i> <i class="icon-file icon-fixed-width"></i> '+field.name+'</td>' +
                                    '<td>'+ field.sha +'</td>' +
                                    '<td>'+ field.size + '</td>' +
                                    '</tr>');
                        }

                    });
                }).success(function() {
                    reloadGithub($('#demo10').find('tr'));
                });
                gitHubLoaded.push($(this).attr('data-path'));
                $(this).find('.icon-folder-close').removeClass('icon-folder-close').addClass('icon-folder-open');
                $(this).find('.icon-angle-right').removeClass('icon-angle-right').addClass('icon-angle-down');
            }


        });

});

Advanced Usage

This advanced demo shows how finderSelect can be used as a component to create a file explorer. Make sure to try Ctrl/Cmd+Click, Ctrl+A/Cmd+A & Ctrl+Alt+A/Cmd+Opt+A (Activates only when cursor is over table), Shift+Click, Ctrl/Cmd+Click&Drag and Right Click on the table after you have made a selection.

hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM
0 File(s) Selected  



$(function() {
        var demo1 = $('#demo1').finderSelect({children:"tr",totalSelector:".demo1-count",menuSelector:"#demo1-menu"});

        $(".demo1-trash").click(function(){
            demo1.finderSelect("selected").remove();
            demo1.finderSelect("update");
        });

        // Opens Preview in Window. Opens with browser only. (There is a 90% chance a popup blocker will stop multiple windows. Working on a better solution)
        $(".demo1-preview").click(function(){
            demo1.finderSelect("selected").each(function(index){
                OpenInNewTab($(this).attr('data-url'));
            });
        });

        // Opens Edit in Window. Opens using Web Based Pixlr editor. (There is a 90% chance a popup blocker will stop multiple windows. Working on a better solution)
        $(".demo1-edit").click(function(){
            demo1.finderSelect("selected").each(function(){
                OpenInNewTab('http://pixlr.com/editor/?image='+$(this).attr('data-url'));
            });
        });
});

Extending

finderSelect was design to be easily extended. See the example below to see how the highlighting functions can be overridden to provide new functionality.

Name Type Last Accessed
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM Unsafe Zone.
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM Unsafe Zone.
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM Click Safe Zone.
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM Click Safe Zone.
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM Click Safe Zone.
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM Click Safe Zone.
hello.png Portable Networks Graphic image Friday, 24 July 2013 4:59PM Click Safe Zone.
$(function() {
                
        // Initialise the Demo with the Ctrl Click Functionality as the Default
        var demo5 = $('#demo5 tbody').finderSelect({enableDesktopCtrlDefault:true});

        // Add a hook to the highlight function so that checkboxes in the selection are also checked.
        demo5.finderSelect('addHook','highlight:before', function(el) {
            el.find('input').prop('checked', true);
        });

        // Add a hook to the unHighlight function so that checkboxes in the selection are also unchecked.
        demo5.finderSelect('addHook','unHighlight:before', function(el) {
            el.find('input').prop('checked', false);
        });

        // Enable Double Click Event.
        demo5.finderSelect("children").dblclick(function() {
            alert( "Double Click detected. Useful for linking to detail page." );
        });

        // Prevent Checkboxes from being triggered twice when click on directly.
        demo5.on("click", ":checkbox", function(e){
            e.preventDefault();
        });

        // Add Select All functionality to the checkbox in the table header row.
        $('#demo5').find("thead input[type='checkbox']").change(function () {
            if ($(this).is(':checked')) {
                demo5.finderSelect('highlightAll');
            } else {
                demo5.finderSelect('unHighlightAll');

            }
        });
                
        // Add a Safe Zone to show not all child elements have to be active.
        $(".safezone").on("mousedown", function(e){
            return false;
        });

});

TBODY Only

To enable selecting on only tbody rows simply call

# First Name Last Name Username
1 Test Data Set
2 Test Data Set
3 Test Data Set
4 Test Data Set
5 Test Data Set
$(function() {
    $('#demo2').find('tbody').finderSelect();
});

Custom Class

If you want to use a custom class for highlighting such as using Twitter Bootstrap

# First Name Last Name Username
1 Test Data Set
2 Test Data Set
3 Test Data Set
4 Test Data Set
5 Test Data Set
$(function() {
    $('#demo3').find('tbody').finderSelect({selectClass:'danger'});
});

Other Elements

This plugin works on all nested elements. The example below shows span tags within a parent div.

Label Label Label Label Label Label Label Label Label Label Label Label

$(function() {
     $('#demo4').finderSelect({selectClass:'label-success'});
});

Configuration Options

The following configuration options are available.

Name Type Description Default
selectClass string The Class applied to selected elements. "selected"
unSelectClass string The Class applied to un-selected elements. "un-selected"
currentClass string The Class applied to the current selected element. "selected-current"
lastClass string The Class applied to the last selected element. "selected-last"
shiftClass string The Class applied to the last shift selected element. "selected-shift"
ctrlClass string The Class applied to the last ctrl selected element. "selected-ctrl"
triggerUpdate string The Event omitted whenever finderSelect is updated. "finderSelectUpdate"
children selector string Manually set the child element selector. Eg "li" for list elements "selected"
event event string The Event which triggers a selection. "mousedown"
cursor css string The cursor applied to the finderSelect element "pointer"
dragEvent string The Event which triggers a drag selection. "mouseenter"
enableClickDrag boolean Enable/Disable Ctrl + Click + Drag. true
enableShiftClick boolean Enable/Disable Shift + Click bulk selection. true
enableCtrlClick boolean Enable/Disable Ctrl + Click Toggle Click. true
enableSingleClick boolean Enable/Disable The standard Single Click. true
enableSelectAll boolean Enable/Disable Select All Keyboard Shortcuts. true
enableDisableSelection boolean Enable/Disable Disabling Text Selection when needed. true
enableTouchCtrlDefault boolean Enable/Disable Ctrl + Click as the default on touch devices. true
enableDesktopCtrlDefault boolean Enable/Disable Ctrl + Click as the default. false
totalSelector selector string The Selector used to update row counts. eg ".demo-count" false
menuSelector string The Selector used to show the menu elemenet. eg "#demo-menu" false
menuXOffset integer The X Offset for the menu element 0
menuYOffset integer The Y Offset for the menu element 0

Methods

The following methods are available.

Name Parameters Description Usage
highlight element Highlights elements
$('#demo').finderSelect('highlight', $('ul'));
unHighlight - UnHighlights elements
$('#demo').finderSelect('unHighlight', $('ul'));
highlightAll - Highlights all child elements.
$('#demo').finderSelect('highlightAll');
unHighlightAll - UnHighlights all child elements
$('#demo').finderSelect('unHighlightAll');
selected - Returns all the selected elements.
$('#demo').finderSelect('selected');
children - Returns all the child elements
$('#demo').finderSelect('children');
update - Forces an update on element. Useful for when dom changes occur outside finderSelect.
$('#demo').finderSelect('update');
addHook hook, function Adds a hook to the finderSelect instance. Allows multiple assignment.
$('#demo').finderSelect('addHook','unHighlight:before', function() { /* do something */ });

Hooks

The following hooks are available.

Name Parameters Description Usage
highlight:before element, options Called before the Highlight event
$('#demo').finderSelect('addHook','highlight:before', function(element, options) { /* do something */ });
highlight:after element, options Called after the Highlight event
$('#demo').finderSelect('addHook','highlight:after', function(element, options) { /* do something */ });
unHighlight:before element, options Called before the unHighlight event
$('#demo').finderSelect('addHook','unHighlight:before', function(element, options) { /* do something */ });
unHighlight:after element, options Called after the unHighlight event
$('#demo').finderSelect('addHook','unHighlight:after', function(element, options) { /* do something */ });

© Michael Angell 2013