Форум программистов, компьютерный форум, киберфорум
Наши страницы
jQuery
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.63/8: Рейтинг темы: голосов - 8, средняя оценка - 4.63
Zuenf
0 / 0 / 0
Регистрация: 29.01.2014
Сообщений: 6
1

Событие выделения элементов на странице. Нужна критика!

30.01.2014, 16:24. Просмотров 1376. Ответов 10
Метки нет (Все метки)

Событие обрабатывает пользовательские "тычки", "рисует" область выделения и записывает выделенные элементы в массив. Подробнее ниже.

Если вам не трудно, посмотрите, что сделано не правильно/не логично.
Буду рад услышать, все, что приходит на ум по коду и его оформлению.

Код события - https://www.dropbox.com/s/masf1jhjpegl6o2/SelectElements%20Event.js
Кликните здесь для просмотра всего текста
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
//Контроль выделения элементов 
 ;(function($){ 
    //Функции контроля над выделенными элементами 
    $.fn.controlSelectedElements = function(){ 
        if($(this).data(selelem.datakey) === undefined) return false; 
        var elem = this; 
        return { 
            add: function(arr){ 
                console.log(this); 
                return unionArr($(elem).data(selelem.datakey).selectedElements, arr); 
            }, 
  
            clear: function(){ 
                $(elem).data(selelem.datakey).selectedElements = []; 
            }, 
  
            get: function(){ 
                return $.extend([], $(elem).data(selelem.datakey).selectedElements); 
            } 
        } 
    } 
  
    //Событие выделения элементов 
    var $event = $.event, 
    $special = $event.special, 
    selelem = $special.selelem = { 
        options: { 
            selectableClass: 'selectable', 
            visualSelectionClass: 'selection'
        }, 
          
        datakey: "selelemkey", 
  
        //Вызывается при каждом бинде события 
        add: function( obj ){  
            var data = $.data( this, selelem.datakey ), 
            opts = obj.data || {}; 
            //Добавлен новый обработчик события 
            data.related ++; 
            //Записываем переданные настройки 'в' элемент 
            $.each( selelem.options, function( key, def ){ 
                if ( opts[ key ] !== undefined ) 
                    data[ key ] = opts[ key ]; 
            }); 
        }, 
  
        //Вызывается при каждом анбинде события 
        remove: function(){ 
            $(this).data(selelem.datakey).related --; 
        }, 
          
        //Вызывается при бинде события selelem 
        setup: function(){ 
            //Если для данного элемента уже создавался обработчик данного события, 
            //то больше не надо 
            if ($(this).data(selelem.datakey)) return; 
            //Инициализируем данные события из стандартных  
            var data = $.extend({}, selelem.options); 
            //Тут будут храниться все выделенные элементы 
            data.selectedElements = []; 
            //Счетчик обработчиков события для данного элемента 
            data.related = 0; 
            //Создаем блок визуального выделения 
            selelem.initVisualSelection(this, data.visualSelectionClass); 
            //Записываем данные события 'в' элемент 
            $(this).data(selelem.datakey, data); 
            //Создаем обработчик нажатия мыши для данного элемента 
            $(this).on('mousedown', selelem.init); 
            //И так же отлавливаем событие нажатия клавиш для обределения состояния ctrl 
            $(document).on('keydown', selelem.isCtrl); 
        }, 
          
        //Нажата мышь на данном элементе 
        init: function(event){ 
            var  target = event.target,  
            //Оределяем координаты мыши относительно данного элемента 
            coord = mouseCoordRelativeElement(event, this), 
            //и все данные события записанные 'в' элемент. 
            data = $(this).data(selelem.datakey); 
            //Координаты выделения first - левый верхний угол, last - правый нижний 
            data.coord = {first: coord, last: coord};    
            //Позволяет отследить начало выделения(первый mousemove после mousedown) 
            selelem.firstmove = true; 
            //Мышь нажата не на выделяемом элементе 
            if(!$(target).hasClass(data.selectableClass)){ 
                //Ctrl не нажат -> значит очищаем старые выделенные элементы  
                if(!selelem.ctrl) data.selectedElements = []; 
                //Запрещаем выделение текста 
                selelem.textselect(false); 
                //Делаем блок визуального выделения видимым 
                selelem.startVisualSelection(this, data); 
                //Теперь можно отслеживать перемещение мыши и момент отпускания  
                $(this).on('mousemove mouseup', data, selelem.handler); 
            //Мышь нажата на выделяемом элементе 
            }else{//доработать 
                //Ищем элемент в уже выделенных 
                var index = data.selectedElements.indexOf(target); 
                //Ctrl не нажат 
                if(!selelem.ctrl){ 
                    //Элемент уже выделен -> ничего не делаем 
                    if(index != -1) return; 
                    //Очищаем старые выделенные элементы  
                    data.selectedElements = []; 
                } 
                //lastSelectedElements - элементы добавленные к основному выделению(data.selectedElements) при последним выделении 
                data.lastSelectedElements = []; 
                //Если он не был ранее выделен то  
                if(index == -1){ 
                    //Добавляем его в последнее выделение 
                    data.lastSelectedElements.push(target); 
                    //Объеденяем основное выделение с последним 
                    unionArr(data.selectedElements, data.lastSelectedElements); 
                //А если уже выделен, то убираем из основного выделения 
                }else data.selectedElements.splice(index, 1); 
                //Устанавливаем trigger начала выделения 
                selelem.setTrigger(this, event, 'selelemstart', data); 
                //Устанавливаем trigger конца выделения 
                selelem.setTrigger(this, event, 'selelemend', data); 
            } 
        }, 
          
        //Отслеживаем mousemove и mouseup 
        handler: function(event){ 
            var data = event.data, 
            //Координаты мыши относительно координат элемента 
            coord = mouseCoordRelativeElement(event, this); 
            data.coord.last = coord; 
            data.lastSelectedElements = []; 
            //Выделяем элементы 
            selelem.selectElements(this, data); 
            switch ( event.type ){ 
                case 'mousemove': 
                    //Первый mousemove после mousedown? 
                    if(selelem.firstmove){ 
                        //Значит вызываем пользовательские события начала выделения 
                        selelem.setTrigger(this, event, 'selelemstart', data); 
                        selelem.firstmove = false; 
                    } 
                    //Устанавливаем координаты и размеры блока визуального выделения в соответствии с координатами выделения 
                    selelem.viewVisualSelection(event, this, data); 
                    //Вызываем пользовательские события процесса выделения 
                    selelem.setTrigger(this, event, 'selelem', data); 
                break; 
                //Событие выделения закончилось  
                case 'mouseup': 
                    //Скрываем блок визуального выделения 
                    selelem.stopVisualSelection(this, data.visualSelectionClass); 
                    //Объеденяем последние выделенные элементы с основным массивом 
                    unionArr(data.selectedElements, data.lastSelectedElements); 
                    //Обнавляем данные события 'в' данном элементе 
                    $(this).data(selelem.datakey, data); 
                    //Вызываем пользовательские события конца выделения 
                    selelem.setTrigger(this, event, 'selelemend', data); 
                    //Возвращаем возможность выделения текста 
                    selelem.textselect(true); 
                    //Отслеживать перемещение и момент отпускания теперь не нужно 
                    $(this).off('mousemove mouseup', selelem.handler); 
                break; 
            } 
        }, 
          
        //Вызывается при анбинде события selelem 
        teardown: function(){ 
            var data = $.data( this, selelem.datakey ); 
            //Удаляем данные и анбиндим события только если больше нет обработчиков 
            if ( data.related ) return; 
            $.removeData( this, drag.datakey ); 
            $(this).off('mousedown', selelem.init); 
            $(document).off('keydown', isCtrl); 
        }, 
  
        //Установка данных события и вызов пользовательского с этими данными 
        setTrigger: function(elem, event, type, data){ 
            event.lastSelectedElements = $.extend([], data.lastSelectedElements); 
            if(type == 'selelemend') event.selectedElements = $.extend([], data.selectedElements); 
            event.type = type; 
            $(elem).trigger(event); 
        }, 
          
        //Проверка вхождения элементов в прямоугольник выделения и добавление их в массив 
        selectElements: function(elem, data){ 
            var selectableClass = '.'+data.selectableClass, 
            //Исправляем координаты 
            coord = correctRectCoord(data.coord); 
            //Перебираем все выделяемые элементы в данном 
            $(elem).find(selectableClass).each(function () { 
                //Координаты элемента 
                var elemCoord = getRectElementCoord(this); 
                //Если пересекается с выделением 
                if (intersect(elemCoord, coord)) { 
                    //Добавляем в выделение 
                    data.lastSelectedElements.push(this); 
                } 
            }); 
        }, 
          
        //Контроль выделения текста 
        textselect: function(bool){  
            $(document)[ bool ? "off" : "on" ]("selectstart", function(){ return false; }); 
        }, 
          
        isCtrl:  function(event){ 
            if (event.keyCode == 17) { 
              selelem.ctrl = true; 
              $(document).off('keydown', selelem.isCtrl); 
              $(document).on('keyup', selelem.offCtrl); 
            } 
        }, 
          
        offCtrl: function(event) { 
            if (event.keyCode == 17) { 
              selelem.ctrl = false; 
              $(document).on('keydown', selelem.isCtrl); 
              $(document).off('keyup', selelem.offCtrl); 
            } 
        }, 
          
        initVisualSelection: function(elem, visualSelectionClass) { 
            $(elem).append('<div class="' + visualSelectionClass + '"></div>'); 
            $(elem).find('.'+visualSelectionClass).css({ 
              'position': 'absolute', 
              'display': 'none'
            }); 
        }, 
          
        startVisualSelection: function(elem, data) { 
            $(elem).find('.'+data.visualSelectionClass).css({ 
              'display': 'block', 
              'top': data.coord.first.Y, 
              'left': data.coord.first.X 
            }); 
        }, 
          
        viewVisualSelection: function(event, elem, data) { 
            var coord = correctRectCoord(data.coord), 
            height = coord.last.Y - coord.first.Y, 
            width = coord.last.X - coord.first.X, 
            top = coord.first.Y, 
            left = coord.first.X; 
            $(elem).find('.'+data.visualSelectionClass).css({ 
              'top': top, 
              'left': left, 
              'height': height, 
              'width': width 
            }); 
        }, 
          
        stopVisualSelection: function(elem, visualSelectionClass) { 
            $(elem).find('.'+visualSelectionClass).css({ 
              'display': 'none', 
              'height': '0', 
              'width': '0'
            }); 
        } 
          
    } 
    $special.selelemstart = $special.selelemend = selelem; 
      
    //Координаты мыши относительно элемента 
    function mouseCoordRelativeElement(event, elem) { 
        return { 
          'X': event.pageX - $(elem).offset().left, 
          'Y': event.pageY - $(elem).offset().top 
        }; 
    } 
  
    //Возвращает координаты элемента 
    function getRectElementCoord(elem){ 
        var pos = $(elem).position(), 
        coord = {first:{}, last:{}}; 
        coord.first.X = pos.left; 
        coord.first.Y = pos.top; 
        coord.last.X = pos.left + $(elem).outerWidth(); 
        coord.last.Y = pos.top + $(elem).outerHeight(); 
        return coord; 
    } 
  
    //Проверяет пересечение прямоугольников, 
    //где first - координата левого верхнего угла, а last - правого нижнего 
    function intersect(a, b){ 
        return !(a.first.Y > b.last.Y || a.last.Y < b.first.Y || a.last.X < b.first.X || a.first.X > b.last.X); 
    } 
  
    //Исправляем координаты: если любая из первых больше соответствующей последней,  
    //то поменять местами 
    function correctRectCoord(coord){ 
        var fCoord = $.extend({}, coord.first), 
        lCoord = $.extend({}, coord.last); 
        $.each(lCoord, function(orig, fix){ 
            if (fix < fCoord[orig]) { 
                lCoord[orig] = fCoord[orig]; 
                fCoord[orig] = fix; 
            } 
        });  
        return {first: fCoord, last: lCoord}; 
    } 
  
    //Объеденяем без повторений; результат в A 
    function unionArr(A, B){ 
        for (var i = 0; i < B.length; i++){  
            if (A.indexOf(B[i]) == -1){  
                A.push(B[i]); 
            } 
        }  
  
        return $.extend([], A); 
    } 
})(jQuery)

Пример - http://fiddle.jshell.net/h8G3g/5/
HTML+CSS
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="select_box">
    <div class="elem">не выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
</div>
 
<div class="select_box">
    <div class="elem">не выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
</div>
<div>
    <button id="Button">Кол-во выделенных элементов</button>
</div>
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.select_box{
    border: 1px solid #000;
    width: 200px;
    height: 200px;
    position: relative;
    overflow:hidden;
    display:inline-block;
}
 
.elem{
    float:left;
    clear:left;
    margin-top:10px;
}
 
.selection{
    border:solid 1px rgba(0, 0, 0, 0.4); 
    background:rgba(0, 0, 0, 0.2); 
}
 
.selected{
    outline: 1px solid #000;
}
 
.temp_selected{
    outline: 1px dotted #000;
}
}

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Пример
$('.select_box').on('selelemstart', function(e){
  $(this).find('.selected').removeClass('selected');
});
 
$('.select_box').on('selelem', function(e){
  $(this).find('.temp_selected').removeClass('temp_selected');
    var lastSelectedElements = e.lastSelectedElements;
    for (var i = 0; i < lastSelectedElements.length; i++) {
      $(lastSelectedElements[i]).addClass('temp_selected');
    }
});
 
$('.select_box').on('selelemend', function(e){
    $(this).find('.selected').removeClass('selected');
    $(this).find('.temp_selected').removeClass('temp_selected');
    var selectedElements = e.selectedElements;
    for (var i = 0; i < selectedElements.length; i++) {
      $(selectedElements[i]).addClass('selected');
    }
});
 
$('#Button').click(function(){
    var str = '', i = 0;
    $('.select_box').each(function(){
        str += ++i +'-й блок содержит '+$(this).controlSelectedElements().get().length+' выделенных элементов\n';
    });
    alert(str);
});
Возможности
Бинд событий:
selelemstart - происходит только при первом mousemove после нажатия.
selelem - происходит каждый раз после нажатия, когда водишь мышью.
В объекте события передаются элементы которые были задеты выделением - lastSelectedElements
selelemend - происходит при mouseup.
В объекте события передаются элементы которые были задеты выделением - lastSelectedElements и выделенные элементы selectedElements;

При бинде можно передать дополнительные параметры событию:
selectableClass - класс элемента который можно будет выделить, по умолчанию = 'selectable'.
visualSelectionClass - класс блока визуального выделения, по умолчанию = 'selection'.

Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Создаем обработчики для #block, где выделяемые элементы будут иметь класс .element
$('#block').bind('selelem', {selectableClass: 'element'}, selelemHandler);
$('#block').bind('selelemend', selelemendHandler);
 
function selelemHandler(e){
console.log('---задеты выделением---');
console.log(e.lastSelectedElements);
}
 
function selelemHandler(e){
console.log('---задеты выделением---');
console.log(e.lastSelectedElements);
console.log('---выделенные---');
console.log(e.selectedElements);
}
Управление выделенными элементами:
$(elem).controlSelectedElemets() - возвращает объект контроля над выделением данного элемента.

Объект контроля содержит 3 метода:
.add(array) - добавляет к массиву выделенных элементов
.clear() - очищает массив выделенных элементов
.get() - возвращает массив выделенных элементов
Javascript
1
2
3
4
5
6
$('#button').click(function(){
//создаем объект контроля над выделением конкретного блока
var blockSelection = $('#block').controlSelectedElemets();
//выведет массив выделенных объектов
console.log(blockSelection.get());
});
 Комментарий модератора 
Код должен присутствовать в тексте сообщения. Песочницы — это лишь бонус для демонстрации.


Добавлено через 16 часов 39 минут
Сорри, в посте допустил ошибку.
В примере "бинд событий" одна из функций "selelemHandler" (та которая ниже), естественно должна называться "selelemendHandler".
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//Создаем обработчики для #block, где выделяемые элементы будут иметь класс .element
$('#block').bind('selelem', {selectableClass: 'element'}, selelemHandler);
$('#block').bind('selelemend', selelemendHandler);
 
function selelemHandler(e){
console.log('---задеты выделением---');
console.log(e.lastSelectedElements);
}
 
function selelemendHandler(e){
console.log('---задеты выделением---');
console.log(e.lastSelectedElements);
console.log('---выделенные---');
console.log(e.selectedElements);
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
30.01.2014, 16:24
Ответы с готовыми решениями:

Событие выделения текста
На этом сайте есть такая фишка. Я хочу цитировать чьи-то слова, для этого я...

Событие кликов на странице с iframe
Имеется страница с iframe. На странице установлен обработчик события нажатия ...

Событие при щелчке по кнопке на странице
Доброго всем времени суток, понадобилось написать плагин для гугл хрома, с...

Как словить событие load отдельного элемента на странице?
Есть страничка, часть которой формируется на php. Слева дерево навигации, а...

Нужна критика :)
Всем привет! Дело такое, изучаю php, сравнительно недавно. Начал писать скрипт...

10
Web_Master
111 / 111 / 18
Регистрация: 24.09.2013
Сообщений: 509
31.01.2014, 15:19 2
В обеих блоках при попытке выделить мышкой только 2-й и 3-й блоки автоматически выделяется 4-й блок.
1
Zuenf
0 / 0 / 0
Регистрация: 29.01.2014
Сообщений: 6
31.01.2014, 19:09  [ТС] 3
Web_Master, спасибо, но это не совсем так.
Я делал это для работы с абсолютно позиционированными элементами изначально и не учел выделения элементов с отступами.

Элемент в контексте данного события это и его отступы. Если хоть чуть-чуть задеть отступ элемента он выделится.
А в примере у каждого элемента margin-top:10px, выделить первые 2 можно легко, главное не задеть 3-й с его отступом.

Конечно, это все исправлю вместе с некоторыми другими, более глубокими косяками, не сильно влияющими на работу события отдельно, но влияющими на взаимодействие его с другими событиями.

Добавлено через 13 минут
В итоге нашел немного иной баг.
Позиция элемента определяется левой верхней точкой с учетом отступов, а высота и ширина без учета.
Поправлю на досуге.
0
Web_Master
111 / 111 / 18
Регистрация: 24.09.2013
Сообщений: 509
31.01.2014, 19:53 4
Когда кликаешь мышой на один блок и (не отпуская) тащишь курсор на другой блок, то другой блок не выделяется. Но может вы так задумали? блоки нельзя будет перетаскивать (как на десктопе)?
0
Zuenf
0 / 0 / 0
Регистрация: 29.01.2014
Сообщений: 6
01.02.2014, 00:43  [ТС] 5
Цитата Сообщение от Web_Master Посмотреть сообщение
Когда кликаешь мышой на один блок и (не отпуская) тащишь курсор на другой блок, то другой блок не выделяется.
Это сделано специально, так как потом может последовать событие перетаскивания или др.
Перетаскивание может осуществляться любым сторонним плагином.
Можно например, при событие перетаскивания элемента стороннего плагина, вычислять разницу координат между текущей и начальной и прибавлять к позиции всех выделенных элементов.

И еще я не написал в первом посте, что можно пользоваться кнопкой ctrl для добавления или удаления новых элементов из выделения, казалось очевидным.

Немного поправил пример: http://fiddle.jshell.net/h8G3g/6/

Добавлено через 44 минуты
Исправил баги c отступами.
Обновленный пример: http://fiddle.jshell.net/h8G3g/7/

Обновленный код:
Кликните здесь для просмотра всего текста
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
//Контроль выделения элементов
 ;(function($){
    //Функции контроля над выделенными элементами
    $.fn.controlSelectedElements = function(){
        if($(this).data(selelem.datakey) === undefined) return false;
        var elem = this;
        return {
            add: function(arr){
                console.log(this);
                return unionArr($(elem).data(selelem.datakey).selectedElements, arr);
            },
 
            clear: function(){
                $(elem).data(selelem.datakey).selectedElements = [];
            },
 
            get: function(){
                return $.extend([], $(elem).data(selelem.datakey).selectedElements);
            }
        }
    }
 
    //Событие выделения элементов
    var $event = $.event,
    $special = $event.special,
    selelem = $special.selelem = {
        options: {
            selectableClass: 'selectable',
            visualSelectionClass: 'selection'
        },
        
        datakey: "selelemkey",
 
        //Вызывается при каждом бинде события
        add: function( obj ){ 
            var data = $.data( this, selelem.datakey ),
            opts = obj.data || {};
            //Добавлен новый обработчик события
            data.related ++;
            //Записываем переданные настройки 'в' элемент
            $.each( selelem.options, function( key, def ){
                if ( opts[ key ] !== undefined )
                    data[ key ] = opts[ key ];
            });
        },
 
        //Вызывается при каждом анбинде события
        remove: function(){
            $(this).data(selelem.datakey).related --;
        },
        
        //Вызывается при бинде события selelem
        setup: function(){
            //Если для данного элемента уже создавался обработчик данного события,
            //то больше не надо
            if ($(this).data(selelem.datakey)) return;
            //Инициализируем данные события из стандартных 
            var data = $.extend({}, selelem.options);
            //Тут будут храниться все выделенные элементы
            data.selectedElements = [];
            //Счетчик обработчиков события для данного элемента
            data.related = 0;
            //Создаем блок визуального выделения
            selelem.initVisualSelection(this, data.visualSelectionClass);
            //Записываем данные события 'в' элемент
            $(this).data(selelem.datakey, data);
            //Создаем обработчик нажатия мыши для данного элемента
            $(this).on('mousedown', selelem.init);
            //И так же отлавливаем событие нажатия клавиш для обределения состояния ctrl
            $(document).on('keydown', selelem.isCtrl);
        },
        
        //Нажата мышь на данном элементе
        init: function(event){
            var  target = event.target, 
            //Оределяем координаты мыши относительно данного элемента
            coord = mouseCoordRelativeElement(event, this),
            //и все данные события записанные 'в' элемент.
            data = $(this).data(selelem.datakey);
            //Координаты выделения first - левый верхний угол, last - правый нижний
            data.coord = {first: coord, last: coord};   
            //Позволяет отследить начало выделения(первый mousemove после mousedown)
            selelem.firstmove = true;
            //Мышь нажата не на выделяемом элементе
            if(!$(target).hasClass(data.selectableClass)){
                //Ctrl не нажат -> значит очищаем старые выделенные элементы 
                if(!selelem.ctrl) data.selectedElements = [];
                //Запрещаем выделение текста
                selelem.textselect(false);
                //Делаем блок визуального выделения видимым
                selelem.startVisualSelection(this, data);
                //Теперь можно отслеживать перемещение мыши и момент отпускания 
                $(this).on('mousemove mouseup', data, selelem.handler);
            //Мышь нажата на выделяемом элементе
            }else{//доработать
                //Ищем элемент в уже выделенных
                var index = data.selectedElements.indexOf(target);
                //Ctrl не нажат
                if(!selelem.ctrl){
                    //Элемент уже выделен -> ничего не делаем
                    if(index != -1) return;
                    //Очищаем старые выделенные элементы 
                    data.selectedElements = [];
                }
                //lastSelectedElements - элементы добавленные к основному выделению(data.selectedElements) при последним выделении
                data.lastSelectedElements = [];
                //Если он не был ранее выделен то 
                if(index == -1){
                    //Добавляем его в последнее выделение
                    data.lastSelectedElements.push(target);
                    //Объеденяем основное выделение с последним
                    unionArr(data.selectedElements, data.lastSelectedElements);
                //А если уже выделен, то убираем из основного выделения
                }else data.selectedElements.splice(index, 1);
                //Устанавливаем trigger начала выделения
                selelem.setTrigger(this, event, 'selelemstart', data);
                //Устанавливаем trigger конца выделения
                selelem.setTrigger(this, event, 'selelemend', data);
            }
        },
        
        //Отслеживаем mousemove и mouseup
        handler: function(event){
            var data = event.data,
            //Координаты мыши относительно координат элемента
            coord = mouseCoordRelativeElement(event, this);
            data.coord.last = coord;
            data.lastSelectedElements = [];
            //Выделяем элементы
            selelem.selectElements(this, data);
            switch ( event.type ){
                case 'mousemove':
                    //Первый mousemove после mousedown?
                    if(selelem.firstmove){
                        //Значит вызываем пользовательские события начала выделения
                        selelem.setTrigger(this, event, 'selelemstart', data);
                        selelem.firstmove = false;
                    }
                    //Устанавливаем координаты и размеры блока визуального выделения в соответствии с координатами выделения
                    selelem.viewVisualSelection(event, this, data);
                    //Вызываем пользовательские события процесса выделения
                    selelem.setTrigger(this, event, 'selelem', data);
                break;
                //Событие выделения закончилось 
                case 'mouseup':
                    //Скрываем блок визуального выделения
                    selelem.stopVisualSelection(this, data.visualSelectionClass);
                    //Объеденяем последние выделенные элементы с основным массивом
                    unionArr(data.selectedElements, data.lastSelectedElements);
                    //Обнавляем данные события 'в' данном элементе
                    $(this).data(selelem.datakey, data);
                    //Вызываем пользовательские события конца выделения
                    selelem.setTrigger(this, event, 'selelemend', data);
                    //Возвращаем возможность выделения текста
                    selelem.textselect(true);
                    //Отслеживать перемещение и момент отпускания теперь не нужно
                    $(this).off('mousemove mouseup', selelem.handler);
                break;
            }
        },
        
        //Вызывается при анбинде события selelem
        teardown: function(){
            var data = $.data( this, selelem.datakey );
            //Удаляем данные и анбиндим события только если больше нет обработчиков
            if ( data.related ) return;
            $.removeData( this, drag.datakey );
            $(this).off('mousedown', selelem.init);
            $(document).off('keydown', isCtrl);
        },
 
        //Установка данных события и вызов пользовательского с этими данными
        setTrigger: function(elem, event, type, data){
            event.lastSelectedElements = $.extend([], data.lastSelectedElements);
            if(type == 'selelemend') event.selectedElements = $.extend([], data.selectedElements);
            event.type = type;
            $(elem).trigger(event);
        },
        
        //Проверка вхождения элементов в прямоугольник выделения и добавление их в массив
        selectElements: function(elem, data){
            var selectableClass = '.'+data.selectableClass,
            //Исправляем координаты
            coord = correctRectCoord(data.coord);
            //Перебираем все выделяемые элементы в данном
            $(elem).find(selectableClass).each(function () {
                //Координаты элемента
                var elemCoord = getRectElementCoord(this);
                //Если пересекается с выделением
                if (intersect(elemCoord, coord)) {
                    //Добавляем в выделение
                    data.lastSelectedElements.push(this);
                }
            });
        },
        
        //Контроль выделения текста
        textselect: function(bool){ 
            $(document)[ bool ? "off" : "on" ]("selectstart", function(){ return false; });
        },
        
        isCtrl:  function(event){
            if (event.keyCode == 17) {
              selelem.ctrl = true;
              $(document).off('keydown', selelem.isCtrl);
              $(document).on('keyup', selelem.offCtrl);
            }
        },
        
        offCtrl: function(event) {
            if (event.keyCode == 17) {
              selelem.ctrl = false;
              $(document).on('keydown', selelem.isCtrl);
              $(document).off('keyup', selelem.offCtrl);
            }
        },
        
        initVisualSelection: function(elem, visualSelectionClass) {
            $(elem).append('<div class="' + visualSelectionClass + '"></div>');
            $(elem).find('.'+visualSelectionClass).css({
              'position': 'absolute',
              'display': 'none'
            });
        },
        
        startVisualSelection: function(elem, data) {
            $(elem).find('.'+data.visualSelectionClass).css({
              'display': 'block',
              'top': data.coord.first.Y,
              'left': data.coord.first.X
            });
        },
        
        viewVisualSelection: function(event, elem, data) {
            var coord = correctRectCoord(data.coord),
            height = coord.last.Y - coord.first.Y,
            width = coord.last.X - coord.first.X,
            top = coord.first.Y,
            left = coord.first.X;
            $(elem).find('.'+data.visualSelectionClass).css({
              'top': top,
              'left': left,
              'height': height,
              'width': width
            });
        },
        
        stopVisualSelection: function(elem, visualSelectionClass) {
            $(elem).find('.'+visualSelectionClass).css({
              'display': 'none',
              'height': '0',
              'width': '0'
            });
        }
        
    }
    $special.selelemstart = $special.selelemend = selelem;
    
    //Координаты мыши относительно элемента
    function mouseCoordRelativeElement(event, elem) {
        return {
          'X': event.pageX - $(elem).offset().left,
          'Y': event.pageY - $(elem).offset().top
        };
    }
 
    //Возвращает координаты элемента
    function getRectElementCoord(elem){
        var pos = $(elem).position(),
        coord = {first:{}, last:{}};
        coord.first.X = pos.left + parseInt($(elem).css('margin-left'));
        coord.first.Y = pos.top + parseInt($(elem).css('margin-top'));
        coord.last.X = coord.first.X + $(elem).outerWidth();
        coord.last.Y = coord.first.Y + $(elem).outerHeight();
        return coord;
    }
 
    //Проверяет пересечение прямоугольников,
    //где first - координата левого верхнего угла, а last - правого нижнего
    function intersect(a, b){
        return !(a.first.Y > b.last.Y || a.last.Y < b.first.Y || a.last.X < b.first.X || a.first.X > b.last.X);
    }
 
    //Исправляем координаты: если любая из первых больше соответствующей последней, 
    //то поменять местами
    function correctRectCoord(coord){
        var fCoord = $.extend({}, coord.first),
        lCoord = $.extend({}, coord.last);
        $.each(lCoord, function(orig, fix){
            if (fix < fCoord[orig]) {
                lCoord[orig] = fCoord[orig];
                fCoord[orig] = fix;
            }
        }); 
        return {first: fCoord, last: lCoord};
    }
 
    //Объеденяем без повторений; результат в A
    function unionArr(A, B){
        for (var i = 0; i < B.length; i++){ 
            if (A.indexOf(B[i]) == -1){ 
                A.push(B[i]);
            }
        } 
 
        return $.extend([], A);
    }
})(jQuery)
0
Web_Master
111 / 111 / 18
Регистрация: 24.09.2013
Сообщений: 509
01.02.2014, 13:59 6
Цитата Сообщение от Zuenf Посмотреть сообщение
И еще я не написал в первом посте, что можно пользоваться кнопкой ctrl
Это я обнаружил когда впервые смотрел пример)))

Добавлено через 11 минут
в последнем примере какой-то косяк с выделяемой областью. Когда щелкаешь мышой прежде чем выделить блоки, появляется выделенная область с левой начальной координатой
1
Zuenf
0 / 0 / 0
Регистрация: 29.01.2014
Сообщений: 6
01.02.2014, 17:59  [ТС] 7
Цитата Сообщение от Web_Master Посмотреть сообщение
косяк с выделяемой областью
Обновил пример: http://fiddle.jshell.net/h8G3g/8/
HTML+CSS
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="select_box">
    <div class="elem">не выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
</div>
 
<div class="select_box">
    <div class="elem">не выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
</div>
<div>
    <button id="Button">Кол-во выделенных элементов</button>
</div>
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.select_box{
    border: 1px solid #000;
    width: 200px;
    height: 200px;
    position: relative;
    overflow:hidden;
    display:inline-block;
}
 
.elem{
    float:left;
    clear:left;
    margin-top:10px;
}
 
.selection{
    border:solid 1px rgba(0, 0, 0, 0.4); 
    background:rgba(0, 0, 0, 0.2); 
}
 
.selected{
    outline: 1px solid #000;
}
 
.temp_selected{
    outline: 1px dotted #000;
}
}

SelectElements Event.js
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
//Контроль выделения элементов
 ;(function($){
    //Функции контроля над выделенными элементами
    $.fn.controlSelectedElements = function(){
        if($(this).data(selelem.datakey) === undefined) return false;
        var elem = this;
        return {
            add: function(arr){
                console.log(this);
                return unionArr($(elem).data(selelem.datakey).selectedElements, arr);
            },
 
            clear: function(){
                $(elem).data(selelem.datakey).selectedElements = [];
            },
 
            get: function(){
                return $.extend([], $(elem).data(selelem.datakey).selectedElements);
            }
        }
    }
 
    //Событие выделения элементов
    var $event = $.event,
    $special = $event.special,
    selelem = $special.selelem = {
        options: {
            selectableClass: 'selectable',
            visualSelectionClass: 'selection'
        },
        
        datakey: "selelemkey",
 
        //Вызывается при каждом бинде события
        add: function( obj ){ 
            var data = $.data( this, selelem.datakey ),
            opts = obj.data || {};
            //Добавлен новый обработчик события
            data.related ++;
            //Записываем переданные настройки 'в' элемент
            $.each( selelem.options, function( key, def ){
                if ( opts[ key ] !== undefined )
                    data[ key ] = opts[ key ];
            });
        },
 
        //Вызывается при каждом анбинде события
        remove: function(){
            $(this).data(selelem.datakey).related --;
        },
        
        //Вызывается при бинде события selelem
        setup: function(){
            //Если для данного элемента уже создавался обработчик данного события,
            //то больше не надо
            if ($(this).data(selelem.datakey)) return;
            //Инициализируем данные события из стандартных 
            var data = $.extend({}, selelem.options);
            //Тут будут храниться все выделенные элементы
            data.selectedElements = [];
            //Счетчик обработчиков события для данного элемента
            data.related = 0;
            //Создаем блок визуального выделения
            selelem.initVisualSelection(this, data.visualSelectionClass);
            //Записываем данные события 'в' элемент
            $(this).data(selelem.datakey, data);
            //Создаем обработчик нажатия мыши для данного элемента
            $(this).on('mousedown', selelem.init);
            //И так же отлавливаем событие нажатия клавиш для обределения состояния ctrl
            $(document).on('keydown', selelem.isCtrl);
        },
        
        //Нажата мышь на данном элементе
        init: function(event){
            var  target = event.target, 
            //Оределяем координаты мыши относительно данного элемента
            coord = mouseCoordRelativeElement(event, this),
            //и все данные события записанные 'в' элемент.
            data = $(this).data(selelem.datakey);
            //Координаты выделения first - левый верхний угол, last - правый нижний
            data.coord = {first: coord, last: coord};   
            //Позволяет отследить начало выделения(первый mousemove после mousedown)
            selelem.firstmove = true;
            //Мышь нажата не на выделяемом элементе
            if(!$(target).hasClass(data.selectableClass)){
                //Ctrl не нажат -> значит очищаем старые выделенные элементы 
                if(!selelem.ctrl) data.selectedElements = [];
                //Запрещаем выделение текста
                selelem.textselect(false);
                //Делаем блок визуального выделения видимым
                selelem.startVisualSelection(this, data);
                //Теперь можно отслеживать перемещение мыши и момент отпускания 
                $(this).on('mousemove mouseup', data, selelem.handler);
            //Мышь нажата на выделяемом элементе
            }else{//доработать
                //Ищем элемент в уже выделенных
                var index = data.selectedElements.indexOf(target);
                //Ctrl не нажат
                if(!selelem.ctrl){
                    //Элемент уже выделен -> ничего не делаем
                    if(index != -1) return;
                    //Очищаем старые выделенные элементы 
                    data.selectedElements = [];
                }
                //lastSelectedElements - элементы добавленные к основному выделению(data.selectedElements) при последним выделении
                data.lastSelectedElements = [];
                //Если он не был ранее выделен то 
                if(index == -1){
                    //Добавляем его в последнее выделение
                    data.lastSelectedElements.push(target);
                    //Объеденяем основное выделение с последним
                    unionArr(data.selectedElements, data.lastSelectedElements);
                //А если уже выделен, то убираем из основного выделения
                }else data.selectedElements.splice(index, 1);
                //Устанавливаем trigger начала выделения
                selelem.setTrigger(this, event, 'selelemstart', data);
                //Устанавливаем trigger конца выделения
                selelem.setTrigger(this, event, 'selelemend', data);
            }
        },
        
        //Отслеживаем mousemove и mouseup
        handler: function(event){
            var data = event.data,
            //Координаты мыши относительно координат элемента
            coord = mouseCoordRelativeElement(event, this);
            data.coord.last = coord;
            data.lastSelectedElements = [];
            //Выделяем элементы
            selelem.selectElements(this, data);
            switch ( event.type ){
                case 'mousemove':
                    //Первый mousemove после mousedown?
                    if(selelem.firstmove){
                        //Значит вызываем пользовательские события начала выделения
                        selelem.setTrigger(this, event, 'selelemstart', data);
                        selelem.firstmove = false;
                    }
                    //Устанавливаем координаты и размеры блока визуального выделения в соответствии с координатами выделения
                    selelem.viewVisualSelection(event, this, data);
                    //Вызываем пользовательские события процесса выделения
                    selelem.setTrigger(this, event, 'selelem', data);
                break;
                //Событие выделения закончилось 
                case 'mouseup':
                    //Скрываем блок визуального выделения
                    selelem.stopVisualSelection(this, data.visualSelectionClass);
                    //Объеденяем последние выделенные элементы с основным массивом
                    unionArr(data.selectedElements, data.lastSelectedElements);
                    //Обнавляем данные события 'в' данном элементе
                    $(this).data(selelem.datakey, data);
                    //Вызываем пользовательские события конца выделения
                    selelem.setTrigger(this, event, 'selelemend', data);
                    //Возвращаем возможность выделения текста
                    selelem.textselect(true);
                    //Отслеживать перемещение и момент отпускания теперь не нужно
                    $(this).off('mousemove mouseup', selelem.handler);
                break;
            }
        },
        
        //Вызывается при анбинде события selelem
        teardown: function(){
            var data = $.data( this, selelem.datakey );
            //Удаляем данные и анбиндим события только если больше нет обработчиков
            if ( data.related ) return;
            $.removeData( this, drag.datakey );
            $(this).off('mousedown', selelem.init);
            $(document).off('keydown', isCtrl);
        },
 
        //Установка данных события и вызов пользовательского с этими данными
        setTrigger: function(elem, event, type, data){
            event.lastSelectedElements = $.extend([], data.lastSelectedElements);
            if(type == 'selelemend') event.selectedElements = $.extend([], data.selectedElements);
            event.type = type;
            $(elem).trigger(event);
        },
        
        //Проверка вхождения элементов в прямоугольник выделения и добавление их в массив
        selectElements: function(elem, data){
            var selectableClass = '.'+data.selectableClass,
            //Исправляем координаты
            coord = correctRectCoord(data.coord);
            //Перебираем все выделяемые элементы в данном
            $(elem).find(selectableClass).each(function () {
                //Координаты элемента
                var elemCoord = getRectElementCoord(this);
                //Если пересекается с выделением
                if (intersect(elemCoord, coord)) {
                    //Добавляем в выделение
                    data.lastSelectedElements.push(this);
                }
            });
        },
        
        //Контроль выделения текста
        textselect: function(bool){ 
            $(document)[ bool ? "off" : "on" ]("selectstart", function(){ return false; });
        },
        
        isCtrl:  function(event){
            if (event.keyCode == 17) {
              selelem.ctrl = true;
              $(document).off('keydown', selelem.isCtrl);
              $(document).on('keyup', selelem.offCtrl);
            }
        },
        
        offCtrl: function(event) {
            if (event.keyCode == 17) {
              selelem.ctrl = false;
              $(document).on('keydown', selelem.isCtrl);
              $(document).off('keyup', selelem.offCtrl);
            }
        },
        
        initVisualSelection: function(elem, visualSelectionClass) {
            $(elem).append('<div class="' + visualSelectionClass + '"></div>');
            $(elem).find('.'+visualSelectionClass).css({
              'position': 'absolute',
              'display': 'none',
              'height': 0,
              'width': 0
            });
        },
        
        startVisualSelection: function(elem, data) {
            $(elem).find('.'+data.visualSelectionClass).css({
              'display': 'block',
              'top': data.coord.first.Y,
              'left': data.coord.first.X,
              'height': 0,
              'width': 0
            });
        },
        
        viewVisualSelection: function(event, elem, data) {
            var coord = correctRectCoord(data.coord),
            height = coord.last.Y - coord.first.Y,
            width = coord.last.X - coord.first.X,
            top = coord.first.Y,
            left = coord.first.X;
            $(elem).find('.'+data.visualSelectionClass).css({
              'top': top,
              'left': left,
              'height': height,
              'width': width
            });
        },
        
        stopVisualSelection: function(elem, visualSelectionClass) {
            $(elem).find('.'+visualSelectionClass).css({
              'display': 'none',
              'height': '0',
              'width': '0'
            });
        }
        
    }
    $special.selelemstart = $special.selelemend = selelem;
    
    //Координаты мыши относительно элемента
    function mouseCoordRelativeElement(event, elem) {
        return {
          'X': event.pageX - $(elem).offset().left,
          'Y': event.pageY - $(elem).offset().top
        };
    }
 
    //Возвращает координаты элемента
    function getRectElementCoord(elem){
        var pos = $(elem).position(),
        coord = {first:{}, last:{}};
        coord.first.X = pos.left + parseInt($(elem).css('margin-left'));
        coord.first.Y = pos.top + parseInt($(elem).css('margin-top'));
        coord.last.X = coord.first.X + $(elem).outerWidth();
        coord.last.Y = coord.first.Y + $(elem).outerHeight();
        return coord;
    }
 
    //Проверяет пересечение прямоугольников,
    //где first - координата левого верхнего угла, а last - правого нижнего
    function intersect(a, b){
        return !(a.first.Y > b.last.Y || a.last.Y < b.first.Y || a.last.X < b.first.X || a.first.X > b.last.X);
    }
 
    //Исправляем координаты: если любая из первых больше соответствующей последней, 
    //то поменять местами
    function correctRectCoord(coord){
        var fCoord = $.extend({}, coord.first),
        lCoord = $.extend({}, coord.last);
        $.each(lCoord, function(orig, fix){
            if (fix < fCoord[orig]) {
                lCoord[orig] = fCoord[orig];
                fCoord[orig] = fix;
            }
        }); 
        return {first: fCoord, last: lCoord};
    }
 
    //Объеденяем без повторений; результат в A
    function unionArr(A, B){
        for (var i = 0; i < B.length; i++){ 
            if (A.indexOf(B[i]) == -1){ 
                A.push(B[i]);
            }
        } 
 
        return $.extend([], A);
    }
})(jQuery)
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
$('.select_box').on('selelem', function(e){
  $(this).find('.temp_selected').removeClass('temp_selected');
    var lastSelectedElements = e.lastSelectedElements;
    for (var i = 0; i < lastSelectedElements.length; i++) {
      $(lastSelectedElements[i]).addClass('temp_selected');
    }
});
 
$('.select_box').on('selelemend', function(e){
    $(this).find('.selected').removeClass('selected');
    $(this).find('.temp_selected').removeClass('temp_selected');
    var selectedElements = e.selectedElements;
    for (var i = 0; i < selectedElements.length; i++) {
      $(selectedElements[i]).addClass('selected');
    }
    console.log($(selectedElements[0]).position.top + parseInt($(elem).css('margin-top')));
});
 
$('#Button').click(function(){
    var str = '', i = 0;
    $('.select_box').each(function(){
        str += ++i +'-й блок содержит '+$(this).controlSelectedElements().get().length+' выделенных элементов\n';
    });
    alert(str);
});
И код: https://www.dropbox.com/s/masf1jhjpegl6o2/SelectElements%20Event.js

 Комментарий модератора 
Код должен присутствовать в тексте сообщения. Песочницы — это лишь бонус для демонстрации.
0
Web_Master
111 / 111 / 18
Регистрация: 24.09.2013
Сообщений: 509
01.02.2014, 18:08 8
теперь после того как щелкнуть в любом свободном месте внутри блока, и навести курсор на элемент появляется рамка вокруг этого элемента в обоех блоках. В первом примере такого небыло
0
Zuenf
0 / 0 / 0
Регистрация: 29.01.2014
Сообщений: 6
01.02.2014, 20:00  [ТС] 9
http://fiddle.jshell.net/h8G3g/9/
Javascript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
//Контроль выделения элементов
 ;(function($){
    //Функции контроля над выделенными элементами
    $.fn.controlSelectedElements = function(){
        if($(this).data(selelem.datakey) === undefined) return false;
        var elem = this;
        return {
            add: function(arr){
                console.log(this);
                return unionArr($(elem).data(selelem.datakey).selectedElements, arr);
            },
 
            clear: function(){
                $(elem).data(selelem.datakey).selectedElements = [];
            },
 
            get: function(){
                return $.extend([], $(elem).data(selelem.datakey).selectedElements);
            }
        }
    }
 
    //Событие выделения элементов
    var $event = $.event,
    $special = $event.special,
    selelem = $special.selelem = {
        options: {
            selectableClass: 'selectable',
            visualSelectionClass: 'selection'
        },
        
        datakey: "selelemkey",
 
        //Вызывается при каждом бинде события
        add: function( obj ){ 
            var data = $.data( this, selelem.datakey ),
            opts = obj.data || {};
            //Добавлен новый обработчик события
            data.related ++;
            //Записываем переданные настройки 'в' элемент
            $.each( selelem.options, function( key, def ){
                if ( opts[ key ] !== undefined )
                    data[ key ] = opts[ key ];
            });
        },
 
        //Вызывается при каждом анбинде события
        remove: function(){
            $(this).data(selelem.datakey).related --;
        },
        
        //Вызывается при бинде события selelem
        setup: function(){
            //Если для данного элемента уже создавался обработчик данного события,
            //то больше не надо
            if ($(this).data(selelem.datakey)) return;
            //Инициализируем данные события из стандартных 
            var data = $.extend({}, selelem.options);
            //Тут будут храниться все выделенные элементы
            data.selectedElements = [];
            //Счетчик обработчиков события для данного элемента
            data.related = 0;
            //Создаем блок визуального выделения
            selelem.initVisualSelection(this, data.visualSelectionClass);
            //Записываем данные события 'в' элемент
            $(this).data(selelem.datakey, data);
            //Создаем обработчик нажатия мыши для данного элемента
            $(this).on('mousedown', selelem.init);
            //И так же отлавливаем событие нажатия клавиш для обределения состояния ctrl
            $(document).on('keydown', selelem.isCtrl);
        },
        
        //Нажата мышь на данном элементе
        init: function(event){
            var  target = event.target, 
            //Оределяем координаты мыши относительно данного элемента
            coord = mouseCoordRelativeElement(event, this),
            //и все данные события записанные 'в' элемент.
            data = $(this).data(selelem.datakey);
            //Координаты выделения first - левый верхний угол, last - правый нижний
            data.coord = {first: coord, last: coord};   
            //Позволяет отследить начало выделения(первый mousemove после mousedown)
            selelem.firstmove = true;
            //Мышь нажата не на выделяемом элементе
            if(!$(target).hasClass(data.selectableClass)){
                //Ctrl не нажат -> значит очищаем старые выделенные элементы 
                if(!selelem.ctrl) data.selectedElements = [];
                //Запрещаем выделение текста
                selelem.textselect(false);
                //Делаем блок визуального выделения видимым
                selelem.startVisualSelection(this, data);
                //Теперь можно отслеживать перемещение мыши и момент отпускания 
                $(this).on('mousemove mouseup', data, selelem.handler);
            //Мышь нажата на выделяемом элементе
            }else{//доработать
                //Ищем элемент в уже выделенных
                var index = data.selectedElements.indexOf(target);
                //Ctrl не нажат
                if(!selelem.ctrl){
                    //Элемент уже выделен -> ничего не делаем
                    if(index != -1) return;
                    //Очищаем старые выделенные элементы 
                    data.selectedElements = [];
                }
                //lastSelectedElements - элементы добавленные к основному выделению(data.selectedElements) при последним выделении
                data.lastSelectedElements = [];
                //Если он не был ранее выделен то 
                if(index == -1){
                    //Добавляем его в последнее выделение
                    data.lastSelectedElements.push(target);
                    //Объеденяем основное выделение с последним
                    unionArr(data.selectedElements, data.lastSelectedElements);
                //А если уже выделен, то убираем из основного выделения
                }else data.selectedElements.splice(index, 1);
                //Устанавливаем trigger начала выделения
                selelem.setTrigger(this, event, 'selelemstart', data);
                //Устанавливаем trigger конца выделения
                selelem.setTrigger(this, event, 'selelemend', data);
            }
        },
        
        //Отслеживаем mousemove и mouseup
        handler: function(event){
            var data = event.data,
            //Координаты мыши относительно координат элемента
            coord = mouseCoordRelativeElement(event, this);
            data.coord.last = coord;
            data.lastSelectedElements = [];
            //Выделяем элементы
            selelem.selectElements(this, data);
            switch ( event.type ){
                case 'mousemove':
                    //Первый mousemove после mousedown?
                    if(selelem.firstmove){
                        //Значит вызываем пользовательские события начала выделения
                        selelem.setTrigger(this, event, 'selelemstart', data);
                        selelem.firstmove = false;
                    }
                    //Устанавливаем координаты и размеры блока визуального выделения в соответствии с координатами выделения
                    selelem.viewVisualSelection(event, this, data);
                    //Вызываем пользовательские события процесса выделения
                    selelem.setTrigger(this, event, 'selelem', data);
                break;
                //Событие выделения закончилось 
                case 'mouseup':
                    //Скрываем блок визуального выделения
                    selelem.stopVisualSelection(this, data.visualSelectionClass);
                    //Объеденяем последние выделенные элементы с основным массивом
                    unionArr(data.selectedElements, data.lastSelectedElements);
                    //Обнавляем данные события 'в' данном элементе
                    $(this).data(selelem.datakey, data);
                    //Вызываем пользовательские события конца выделения
                    selelem.setTrigger(this, event, 'selelemend', data);
                    //Возвращаем возможность выделения текста
                    selelem.textselect(true);
                    //Отслеживать перемещение и момент отпускания теперь не нужно
                    $(this).off('mousemove mouseup', selelem.handler);
                break;
            }
        },
        
        //Вызывается при анбинде события selelem
        teardown: function(){
            var data = $.data( this, selelem.datakey );
            //Удаляем данные и анбиндим события только если больше нет обработчиков
            if ( data.related ) return;
            $.removeData( this, drag.datakey );
            $(this).off('mousedown', selelem.init);
            $(document).off('keydown', isCtrl);
        },
 
        //Установка данных события и вызов пользовательского с этими данными
        setTrigger: function(elem, event, type, data){
            event.lastSelectedElements = $.extend([], data.lastSelectedElements);
            if(type == 'selelemend') event.selectedElements = $.extend([], data.selectedElements);
            event.type = type;
            $(elem).trigger(event);
        },
        
        //Проверка вхождения элементов в прямоугольник выделения и добавление их в массив
        selectElements: function(elem, data){
            var selectableClass = '.'+data.selectableClass,
            //Исправляем координаты
            coord = correctRectCoord(data.coord);
            //Перебираем все выделяемые элементы в данном
            $(elem).find(selectableClass).each(function () {
                //Координаты элемента
                var elemCoord = getRectElementCoord(this);
                //Если пересекается с выделением
                if (intersect(elemCoord, coord)) {
                    //Добавляем в выделение
                    data.lastSelectedElements.push(this);
                }
            });
        },
        
        //Контроль выделения текста
        textselect: function(bool){ 
            $(document)[ bool ? "off" : "on" ]("selectstart", function(){ return false; });
        },
        
        isCtrl:  function(event){
            if (event.keyCode == 17) {
              selelem.ctrl = true;
              $(document).off('keydown', selelem.isCtrl);
              $(document).on('keyup', selelem.offCtrl);
            }
        },
        
        offCtrl: function(event) {
            if (event.keyCode == 17) {
              selelem.ctrl = false;
              $(document).on('keydown', selelem.isCtrl);
              $(document).off('keyup', selelem.offCtrl);
            }
        },
        
        initVisualSelection: function(elem, visualSelectionClass) {
            $(elem).append('<div class="' + visualSelectionClass + '"></div>');
            $(elem).find('.'+visualSelectionClass).css({
              'position': 'absolute',
              'display': 'none',
              'height': 0,
              'width': 0
            });
        },
        
        startVisualSelection: function(elem, data) {
            $(elem).find('.'+data.visualSelectionClass).css({
              'display': 'block',
              'top': data.coord.first.Y,
              'left': data.coord.first.X,
              'height': 0,
              'width': 0
            });
        },
        
        viewVisualSelection: function(event, elem, data) {
            var coord = correctRectCoord(data.coord),
            height = coord.last.Y - coord.first.Y,
            width = coord.last.X - coord.first.X,
            top = coord.first.Y,
            left = coord.first.X;
            $(elem).find('.'+data.visualSelectionClass).css({
              'top': top,
              'left': left,
              'height': height,
              'width': width
            });
        },
        
        stopVisualSelection: function(elem, visualSelectionClass) {
            $(elem).find('.'+visualSelectionClass).css({
              'display': 'none',
              'height': '0',
              'width': '0'
            });
        }
        
    }
    $special.selelemstart = $special.selelemend = selelem;
    
    //Координаты мыши относительно элемента
    function mouseCoordRelativeElement(event, elem) {
        return {
          'X': event.pageX - $(elem).offset().left,
          'Y': event.pageY - $(elem).offset().top
        };
    }
 
    //Возвращает координаты элемента
    function getRectElementCoord(elem){
        var pos = $(elem).position(),
        coord = {first:{}, last:{}};
        coord.first.X = pos.left + parseInt($(elem).css('margin-left'));
        coord.first.Y = pos.top + parseInt($(elem).css('margin-top'));
        coord.last.X = coord.first.X + $(elem).outerWidth();
        coord.last.Y = coord.first.Y + $(elem).outerHeight();
        return coord;
    }
 
    //Проверяет пересечение прямоугольников,
    //где first - координата левого верхнего угла, а last - правого нижнего
    function intersect(a, b){
        return !(a.first.Y > b.last.Y || a.last.Y < b.first.Y || a.last.X < b.first.X || a.first.X > b.last.X);
    }
 
    //Исправляем координаты: если любая из первых больше соответствующей последней, 
    //то поменять местами
    function correctRectCoord(coord){
        var fCoord = $.extend({}, coord.first),
        lCoord = $.extend({}, coord.last);
        $.each(lCoord, function(orig, fix){
            if (fix < fCoord[orig]) {
                lCoord[orig] = fCoord[orig];
                fCoord[orig] = fix;
            }
        }); 
        return {first: fCoord, last: lCoord};
    }
 
    //Объеденяем без повторений; результат в A
    function unionArr(A, B){
        for (var i = 0; i < B.length; i++){ 
            if (A.indexOf(B[i]) == -1){ 
                A.push(B[i]);
            }
        } 
 
        return $.extend([], A);
    }
})(jQuery)
//Пример
 
$('.select_box').on('selelem', function(e){
  $(this).find('.temp_selected').removeClass('temp_selected');
    var lastSelectedElements = e.lastSelectedElements;
    for (var i = 0; i < lastSelectedElements.length; i++) {
      $(lastSelectedElements[i]).addClass('temp_selected');
    }
});
 
$('.select_box').on('selelemend', function(e){
    $(this).find('.selected').removeClass('selected');
    $(this).find('.temp_selected').removeClass('temp_selected');
    var selectedElements = e.selectedElements;
    for (var i = 0; i < selectedElements.length; i++) {
      $(selectedElements[i]).addClass('selected');
    }
});
 
$('#Button').click(function(){
    var str = '', i = 0;
    $('.select_box').each(function(){
        str += ++i +'-й блок содержит '+$(this).controlSelectedElements().get().length+' выделенных элементов\n';
    });
    alert(str);
});
Кликните здесь для просмотра всего текста
HTML5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<div class="select_box">
    <div class="elem">не выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
</div>
 
<div class="select_box">
    <div class="elem">не выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
    <div class="elem selectable">выделяемый</div>
</div>
<div>
    <button id="Button">Кол-во выделенных элементов</button>
</div>
CSS
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
.select_box{
    border: 1px solid #000;
    width: 200px;
    height: 200px;
    position: relative;
    overflow:hidden;
    display:inline-block;
}
 
.elem{
    float:left;
    clear:left;
    margin-top:10px;
}
 
.selection{
    border:solid 1px rgba(0, 0, 0, 0.4); 
    background:rgba(0, 0, 0, 0.2); 
}
 
.selected{
    outline: 1px solid #000;
}
 
.temp_selected{
    outline: 1px dotted #000;
}
}

 Комментарий модератора 
Код должен присутствовать в тексте сообщения. Песочницы — это лишь бонус для демонстрации.
0
Web_Master
111 / 111 / 18
Регистрация: 24.09.2013
Сообщений: 509
01.02.2014, 20:52 10
Ну теперь всё работает вроде как надо )) осталось найти применение))
0
kukush
0 / 0 / 0
Регистрация: 11.02.2014
Сообщений: 4
11.02.2014, 16:25 11
не подскажите где нужно изменить что б в при бинде поля в массив записывался ID выбранного пункта
0
11.02.2014, 16:25
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
11.02.2014, 16:25

Нужна критика
Добрый день, сразу скажу что критику на счет дизайна можете оставить при себе...

Нужна критика
Сделал прогу для детей она еще не закончена посоветуйте что здесь лишнее а что...

Нужна критика
собственно хотелось бы узнать насколько граматно написано и можно ли что нить...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
11
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru