How to find selected items in javascript marquee selection box without using a loop?

I am writing my own drag and drop file manager. This includes the javascript marquee select box, which, when active, calculates the elements (files) that intersect and selects them, adding a class to them.

I am currently doing a check during the mousemove handler, iterating over the array of element coordinates and determining which of them intersect in the drag and drop selection window.

Currently, the function looks like this:

selectItems : function(voidindex){

                        var self = this;
                        var coords = self.cache.selectioncoords;

    for(var i=0, len = self.cache.items.length; i<len; i++){
           var item = self.cache.items[i];
           var itemcoords = item.box_pos;

           if(coords.topleft.x < (itemcoords.x+201) && coords.topright.x > itemcoords.x && coords.topleft.y < (itemcoords.y+221) && coords.bottomleft.y > itemcoords.y){
               if(!item.selected){
                  item.selected = true;
                  item.html.addClass('selected').removeClass('activebutton');
                  self.cache.selecteditems.push(i);
                  self.setInfo();
               }
           }
           else{
               if(item.selected){
                  item.selected = false;
                  if(!voidindex || voidindex !== i){
                      item.html.removeClass('selected');
                  }
                  var removeindex = self.cache.selecteditems.indexOf(i);
                  self.cache.selecteditems.splice(removeindex, 1);
                  self.setInfo();
           }
       }
  }
},

, , DOM . . , .

, 201px 221px.

, , , , - .

, .

:

 selectioncoords : {
                    topleft : {
                        x : 0,
                        y : 0
                    },
                    topright : {
                        x : 0,
                        y : 0
                    },
                    bottomleft : {
                        x : 0,
                        y : 0
                    },
                    bottomright : {
                        x : 0,
                        y : 0
                    },
                    width : 0,
                    height : 0
                }

, self.cache.items, :

item : {
       box_pos : {
             x : 0,
             y : 0
       },
       grid_pos : {
              row : 1,
              column : 1
       }


    }

, (/), ( ).

, , : , , , mousemove?

.

+5
4

. . - , . , -. self.cache.items :

var cacheLookup = {};

function initCacheLookup() {
    var items = self.cache.items;
    for( var i = 0, n = items.length;  i < n;  i++ ) {
        var item = items[i];
        var key = [ item.grid_pos.row, item.grid_pos.column ].join(',');
        cacheLookup[key] = item;
    }
}

, , , - :

var itemWidth = 201, itemHeight = 221;

var tl = selectioncoords.topleft, br = selectioncoords.bottomright;
var left = Math.floor( tl.x / itemWidth ) + 1;
var right = Math.floor( br.x / itemWidth ) + 1;
var top = Math.floor( tl.y / itemHeight ) + 1;
var bottom = Math.floor( br.y / itemHeight ) + 1;

var selecteditems = [];
for( var row = top;  row <= bottom;  row++ ) {
    for( var col = left;  col <= right;  col++ ) {
        var key = [ row, col ].join(',');
        var item = cacheLookup[key];
        if( item ) {
            selecteditems.push( item );
        }
    }
}
// Now selecteditems has the items intersecting the rectangle

, , .

, , . , , , self.cache.items. - cacheLookup , .

: ( )? , , 0-3, 4-7, 8-11 .. , .

, -. initCacheLookup() , :

var nCols = 4/*whatever*/;  // defined somewhere else
var itemWidth = 201, itemHeight = 221;

var tl = selectioncoords.topleft, br = selectioncoords.bottomright;
var left = Math.floor( tl.x / itemWidth );
var right = Math.floor( br.x / itemWidth );
var top = Math.floor( tl.y / itemHeight ) * nCols;
var bottom = Math.floor( br.y / itemHeight ) * nCols;

var items = self.cache.items;
var selecteditems = [];
for( var iRow = top;  iRow <= bottom;  iRow += nCols ) {
    for( var col = left;  col <= right;  col++ ) {
        var index = iRow + col;
        if( index < items.length ) {
            selecteditems.push( items[index] );
        }
    }
}
// Now selecteditems has the items intersecting the rectangle

, . item.box_pos item.grid_pos. , , , .

:

201 221 . , , , .

. , . :

selectioncoords: {
    topleft: {
        x: 0,
        y: 0
    },
    topright: {
        x: 0,
        y: 0
    },
    bottomleft: {
        x: 0,
        y: 0
    },
    bottomright: {
        x: 0,
        y: 0
    },
    width: 0,
    height: 0
}

. , :

selectioncoords: {
    left: 0,
    right: 0,
    top: 0,
    bottom: 0
}

, , , : " . topleft.x bottomleft.x? , ? ?"

, , item.box_pos item.grid_pos , . , , :

box_pos.x === ( grid_pos.column - 1 ) * itemWidth
box_pos.y === ( grid_pos.row - 1 ) * itemHeight
+2

, .

. , x y, , () .

, x y / . , , ( ) / . , , , , (), . , , .

, ( ) , , . , , , , .

, kludgy, mousemove . , .

+3

, , . , X, Y X1, Y2, X1, Y2.

...

var Grid = function(pixelWidth, pixelHeight, boxSize) {

  this.cellsIn = function(x1, y1, x2, y2) {
    var rv = [];
    for (var x = x1; x < x2; x += boxSize) {
      for (var y = y1; y < y2; y += boxSize) {
        var gx = Math.ceil(x/boxSize);
        var gy = Math.ceil(y/boxSize);
        rv.push(this.cells[gx][gy]);
      }
    }
    return rv;
  } // cellsIn()


  this.add = function(x1, y1, x2, y2, o) {
    var cells = this.cellsIn(x1, y1, x2, y2);
    for (var i in cells) {
      cells[i].push(o);
    }
  } // add()


  this.get = function(x1, y1, x2, y2) {
    var rv = [];
    var rv_index = {};
    var cells = this.cellsIn(x1, y1, x2, y2);
    for (var i in cells) {
      var cell = cells[i];
      for (var oi in cell) {
        if (!rv_index[cell[oi]]) {
          rv_index[cell[oi]] = 1;
          rv.push(cell[oi]);
        }
      }
    }
    return rv;
  } // get()


  this.cells = [];
  for (var x = 0; x < Math.ceil(pixelWidth/boxSize); x++) {
    this.cells[x] = [];
    for (var y = 0; y < Math.ceil(pixelHeight/boxSize); y++) {
      this.cells[x][y] = [];
    }
  }

};

, , , , .

, / . , , ( ) Grid / . , , , , " ".

. . . . DemoGrid: http://www.thepointless.com/js/ascii_monsters.js

DemoGrid ( , ), x, y, radius . , , . / .

+2

,

  • self.cache.items
    • (0,0), (1,0), (2,0), (0,1), (1,1), (1,2), (0,2), (1,2), (2,2)
    • (1), (1, 2), (0,2), (1, 2), (2,2)
  • BAD - (0,0), (2,0) (1,2), (1,3), (2,1), (2,3)
.

, , .

// Some 'constants' we'll need.
number_of_columns = 4;
item_width = 201;
item_height = 221;

// First off, we are dealing with a grid system, 
// so that means that if given the starting x and y of the marquee,
// we can determine which element in the cache to start where we begin.
top_left_selected_index = Math.floor(selectioncoords.topleft.x / item_width) + (Math.floor(selectioncoords.topright.y / item_height) * number_of_columns );

// Now, because the array is in order, and there are no empty cache points, 
// we know that the lower bound of the selected items is `top_left_selected_index`
// so all we have to do is walk the array to grab the other selected.

number_columns_selected = (selectioncoords.bottomright.x - selectioncoords.topleft.x) / item_width;
// if it it doesn't divide exactly it means there is an extra column selected
if((selectioncoords.bottomright.x - selectioncoords.topleft.x) % item_width > 0){
  number_columns_selected += 1;
}

// if it it doesn't divide exactly it means there is an extra column selected
number_rows_selected = (selectioncoords.bottomright.y - selectioncoords.topleft.y) / item_height;
if((selectioncoords.bottomright.y - selectioncoords.topleft.y) % item_height > 0){
  number_rows_selected += 1;
}

// Outer loop handles the moving the pointer in terms of the row, so it
// increments by the number of columns.
// EX: Given my simple example array, To get from (1,0) to (1,1) 
// requires an index increase of 3
for(i=0; i < number_rows_selected; i++){
  // Inner loop marches through the the columns, so it is just one at a time.
  // Added j < number_of_columns in case your marquee stretches well past your content
  for(j=0; j < number_columns_selected && j < number_of_columns; j++){
    // Do stuff to the selected items.
    self.cache.items[top_left_selected_index + (i * number_of_columns) + j];
  }
}
0

All Articles