2

As a little practice I decided to recreate the Windows 8 Explorer file list panel, and all went well, until I wanted to add the mouse selection. It's basically the feature that allows you to select multiple files by dragging your mouse along the window, drawing a square, which should select all "files" that fall under it.

The only problem I have is that I can't seem to find a way to add the selected class to the elements under the selection

Here's the related code: (full code available in a working fiddle)

<ul class="files">
    <li>
        <span class="icon folder"></span>
        <span class="name">Folder</span>
    </li>
</ul>

.selection {
    position: absolute;
    border: 1px solid #39F;
    background-color: rgba(51,153,255,.4);
    display: none;
}

$(function(){
    var $body = $(document.body);
    var selecting = false,
        $selectionDiv = $(document.createElement('div')).addClass('selection').appendTo($body),
        startX, startY;
    $body.on({
        mousedown: function(e){
            if (e.target === document.body){
                e.stopPropagation();

                startX = e.clientX;
                startY = e.clientY;
                selecting = true;

                $selectionDiv.show().css({top:startY+'px',left:startX+'px',width:'0px',height:'0px'});
            }
        },
        mousemove: function(e){
            if (selecting){
                var currentX = e.clientX,
                    currentY = e.clientY;

                var subX = currentX - startX,
                    subY = currentY - startY;

                if (subX < 0){
                    subX *= -1;
                    $selectionDiv.css('left',startX+(currentX-startX));
                }
                else $selectionDiv.css('left',startX+'px');

                if (subY < 0){
                    subY *= -1;
                    $selectionDiv.css('top',startY+(currentY-startY));
                }
                else $selectionDiv.css('top',startY+'px');

                $selectionDiv.css({
                    width: subX,
                    height: subY,
                });
            }
        }
    }).on('mouseup blur mouseleave',function(){
        if (selecting){
            $selectionDiv.hide();
            selecting = false;
        }
    });
});
SeinopSys
  • 8,787
  • 10
  • 62
  • 110

1 Answers1

4

If I understand you correctly, you need to determine which elements are contained inside the selection box. Here's the code which seems to do the job (it's supposed to go into your mousemove event handler):

var topLeftX = Math.min(startX, currentX),
    topLeftY = Math.min(startY, currentY),
    bottomRightX = Math.max(startX, currentX),
    bottomRightY = Math.max(startY, currentY);

$('.files li').each(function() {
    var offset = $(this).offset(),
        width = $(this).outerWidth(),
        height = $(this).outerHeight();

    if (offset.left < bottomRightX
            && offset.left + width > topLeftX
            && offset.top < bottomRightY
            && offset.top + height > topLeftY) {
        $(this).addClass('selected');
    }
    else {
        $(this).removeClass('selected');
    }
});

This code goes through all the elements of your file list and runs rectangle overlap test (the algorithm for which I got from this answer) for the selection box and the list element. Usage of outerWidth() and outerHeight() makes sure that the border is also taken into consideration.

I also noticed that when you release the mouse your handler which resets the selection gets called:

$(window).click(function(){
    $('.files li').removeClass('selected');
})

As a possible solution, you can move this into your mousedown handler.

Here's JSFIddle which works for me in Chrome 35: http://jsfiddle.net/5Hzm4/2/

Community
  • 1
  • 1
izstas
  • 5,004
  • 3
  • 42
  • 56