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
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.

.editorconfig0fe2cbbba5b02489c890fce39946f688d46a98ed278
.gitattributes5832a0194d80654f662f8fda80ada5a7450de165207
.github
.gitignore2b984efe70f61291c8d86430cec5f5a4a249fbcb609
.husky
.mailmapec84393581175293af8f794f731a1fd9690219b05822
.npmignore2de60f956b4c8f84942efdaed6ac5c8e532a0961167
.npmrccffe8cdef132f31903a4971117f33f60cd9a56e616
.release-it.cjsff55b0ef187f68a2aa5f2fc95d85f299ad39e9ef1189
AUTHORS.txt1ef119e02689a472a9dd184e9d0935f8f648b78c14142
CODE_OF_CONDUCT.md1dc3de996bd138bd34152dc26460da1ed53d3913160
CONTRIBUTING.md45396af7d53d1cd7dd5c35f2e54e1d56f8019f2a9323
LICENSE.txtf642c3f7a77a5833bc3dfee643ad8ca6387b23e71097
README.md3aefeb7fa8919fd3fab4750a41cc6c60f4f7ad3414689
SECURITY.md11d9cafde0d47cb9b367b843e3e60fd40571bfb1427
build
changelog.md644317357556ee6d1f7222f70696bb3267888d917100
dist-module
dist
eslint.config.js714f2978a33e706412e17698126140de9f4afcd97803
jtr-isolate.yml56d64b66dccd1e849d22f9d6df1557e7e310ac2f371
package-lock.json626f1636459d79ffea8dd0ff11c9b18eddbb398d420173
package.json078d3cfc935a4187b4be06a3458d6c3b5172a9d97195
src
test
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