05.01.2014, 03:42
|
|
Кандидат Javascript-наук
|
|
Регистрация: 27.01.2012
Сообщений: 134
|
|
Класс выделения элементов. Нужна критика.
Учусь на практике.
Класс получает все выделяемые html-элементы(с классом selectClass) между координатами mousedown и mouseup. Существует возможность нажатия ctrl и соответственно добавления элементов в уже существующее выделение.
Хочется узнать, что сделано криво, что не красиво, а что вообще не правильно.
Если есть какие-то функции, существующие, чтобы облегчить какие-либо ситуации в моем примере, буду рад узнать о них.
//Класс Select
function Select( selectBox, selectClass, visualSelectionClass){
var selectBox = selectBox || document; //Контейнер в котором возможно выделение
var selectClass = selectClass || '.selectable'; //Клас элемента который можно выделить
var visualSelectionClass = visualSelectionClass || '.selection'; //Класс для блока визуального выделения
var coord = {}; //Координаты выделенной области
var ctrl = false; //Состояние клавиши CTRL
var selectedElements = []; //Выделенные элементы
//Координаты мыши относительно элемента
function mouseCoordRelativeElement(event, elem){
return {'X':event.pageX - $(elem).offset().left, 'Y':event.pageY - $(elem).offset().top};
}
//----------------------Нажатие CTRL------------------------
function isCtrl(e){
if( e.keyCode == 17 ){
ctrl = true;
$(document).unbind( 'keydown', isCtrl );
$(document).bind( 'keyup', offCtrl );
}
}
function offCtrl(e){
if( e.keyCode == 17 ){
ctrl = false;
$(document).bind( 'keydown', isCtrl );
$(document).unbind( 'keyup', offCtrl );
}
}
//----------------------------------------------------------
//--------------Выделение и добавление в массив-------------
function startSelect(e){
var t = e.target
var c = mouseCoordRelativeElement(e, selectBox);
//Клик был не на элементе(на пустом пространстве) -> начинаем выделение;
if ( !$(t).hasClass( selectClass.substring(1) ) ){
coord.x1 = c.X;
coord.y1 = c.Y;
//Начало показа визуального выделения;
startVisualSelection();
}else{//Клик был на элементе;
//Если он не входит в массив выбранных элементов
if(selectedElements.indexOf(t) == -1){
if ( !ctrl ) selectedElements = [];
//Запиcываем его в массив выделенных элементов;
selectedElements.push(t);
}
}
return false;
}
function endSelect(e){
if(coord.x1 === undefined) return;
var c = mouseCoordRelativeElement(e, selectBox);
coord.x2 = c.X;
coord.y2 = c.Y;
//По идее координаты начала выделения < координат конца
//Cоответственно если координаты mouseup < координаты начала выделения(mousedown), то координаты начала должны стать координатами конца
if( c.X < coord.x1 ){
coord.x2 = coord.x1;
coord.x1 = c.X;
}
if( c.Y < coord.y1 ){
coord.y2 = coord.y1;
coord.y1 = c.Y;
}
//Проверка вхождения выделяемых элементов в область выделения и запись их в массив выделенных элементов
selectElements();
//Остановка визуального выделения;
stopVisualSelection();
coord = {};
}
function selectElements(){
if( !ctrl ) selectedElements = [];
//Идем по всем выделяемым элементам
$(selectClass).each(function(){
var y = $(this).position().top + $(this).outerHeight()/2;
var x = $(this).position().left + $(this).outerWidth()/2;
//Если центральная точка элемента находится внутри выделения и его еще нет в масииве то...
if( x>coord.x1 && y>coord.y1 && x<coord.x2 && y<coord.y2 && selectedElements.indexOf(this) == -1 ){
//...добавляем в массив
selectedElements.push(this);
}
});
}
//---------------------------------------------------
//----------------Визуальная часть-------------------
function initVisualSelection(){
$(selectBox).append('<div class="'+visualSelectionClass.substring(1)+'"></div>');
$(visualSelectionClass).css({'position':'absolute', 'display':'none'});
}
function startVisualSelection(){
$(visualSelectionClass).css({'display':'block', 'top':coord.y1, 'left':coord.x1});
$(selectBox).bind( 'mousemove', viewVisualSelection );
}
function viewVisualSelection(e){
var c = mouseCoordRelativeElement(e, selectBox);
var height = c.Y - coord.y1;
var width = c.X - coord.x1;
var top = coord.y1;
var left = coord.x1;
if( height < 0 ){
top = c.Y;
height *= -1;
}
if( width < 0 ){
left = c.X;
width *= -1;
}
$(visualSelectionClass).css({'top':top, 'left':left,'height':height, 'width':width});
}
function stopVisualSelection(){
$(visualSelectionClass).css({'display':'none','height':'0', 'width':'0'});
$(selectBox).unbind( 'mousemove', viewVisualSelection );
}
//------------------------------------------
this.init = function(){
$(document).bind( 'keydown', isCtrl );
$(selectBox).bind( 'mousedown', startSelect );
$(selectBox).bind( 'mouseup', endSelect );
//Инициализация визуального выделения;
initVisualSelection();
}
//Возвращает массив выбраных елементов;
this.getSelectedElements = function(){
return selectedElements;
}
}
Последний раз редактировалось Zuenf, 05.01.2014 в 13:50.
|
|
05.01.2014, 13:47
|
|
Кандидат Javascript-наук
|
|
Регистрация: 27.01.2012
Сообщений: 134
|
|
Пример возможного использования - http://learn.javascript.ru/play/VT7sZb
Пока делал нашел и исправил несколько косяков.
|
|
05.01.2014, 15:07
|
Профессор
|
|
Регистрация: 14.09.2012
Сообщений: 162
|
|
Мне кажется такая структура кода странной. Структура убогая и неудобная, но распространённая. Как по мне, так "так верстают только мудаки". Есть такая проблема: на любую задачу есть готовое решение, которое немного не подходит. Вот мне нужно выделение, и я думаю: "Надо взять готовое решение!" Нахожу, внедряю и тут мне говорят: "Нужно, чтобы внизу был счётчик выбранных элементов."
- Ну, - думаю я. - Отлично! Это ж класс. Создам дочерний класс с нужным мне функционалом...
Хренцы-бубенцы! Наследованием проблему не решить.
Тогда многие дают при инициализации передать типа обработчики на "внутренние события" (стандартный для jQ-плагинов паттерн с объектом options). Но такой вариант мне тоже кажется туповатым.
Мне кажется, что в решениях, где вся суть в манипуляциях с DOM-элементами, необходимо чтобы на каждую манипуляцию происходило соответствующее событие. Тогда, чтобы добавить счётчик, нужно сделать что-то вроде того:
$(".some-container").on({
select: function () {
$(".counter", this).html($(".counter", this).html() + 1);
},
unselect: function () {
$(".counter", this).html($(".counter", this).html() - 1);
}
});
Это псевдо-быдлокод, чтобы уловить идею.
|
|
05.01.2014, 16:39
|
|
Кандидат Javascript-наук
|
|
Регистрация: 27.01.2012
Сообщений: 134
|
|
Про наследование почитаю, не задумывался об этом.
А про собственные события с отдельными названиеми, пробегала мысля(видел как-то давно в исходниках jQuery), может подскажешь где почитать про их создание или как там они делаются?
P.S. Спасибо за "наставничество", плюсую .
Последний раз редактировалось Zuenf, 05.01.2014 в 16:41.
|
|
05.01.2014, 17:30
|
Профессор
|
|
Регистрация: 14.09.2012
Сообщений: 162
|
|
Zuenf, могу предложить только документацию или гугл. С событиями тоже есть проблема: они - глобальные. Так что делать в своём коде событие с именем select - не лучшая мысль, ибо есть уже событие с таким именем. Решить можно с помощью какого-нибудь префикса. Например, у тебя будут имена событий foo_select, foo_startselection и т.д.
|
|
05.01.2014, 17:40
|
|
Кандидат Javascript-наук
|
|
Регистрация: 27.01.2012
Сообщений: 134
|
|
BallsShaped, ок, спасибо! Будем делать.
|
|
29.01.2014, 16:35
|
|
Кандидат Javascript-наук
|
|
Регистрация: 27.01.2012
Сообщений: 134
|
|
Сделал в виде события - https://www.dropbox.com/s/masf1jhjpe...nts%20Event.js
Пример - http://fiddle.jshell.net/h8G3g/5/
И опять же, если вам не трудно, посмотрите, что сделано не правильно/не логично.
Буду рад услышать, все, что приходит на ум по коду и его оформлению.
Возможности
Бинд событий:
selelemstart - происходит только при первом mousemove после нажатия.
selelem - происходит каждый раз после нажатия, когда водишь мышью.
В объекте события передаются элементы которые были задеты выделением - lastSelectedElements
selelemend - происходит при mouseup.
В объекте события передаются элементы которые были задеты выделением - lastSelectedElements и выделенные элементы selectedElements;
При бинде можно передать дополнительные параметры событию:
selectableClass - класс элемента который можно будет выделить, по умолчанию = 'selectable'.
visualSelectionClass - класс блока визуального выделения, по умолчанию = 'selection'.
//Создаем обработчики для #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);
}
Управление выделенными элементами:
$(elem) .controlSelectedElemets() - возвращает объект контроля над выделением данного элемента.
Объект контроля содержит 3 метода:
.add(array) - добавляет к массиву выделенных элементов
.clear() - очищает массив выделенных элементов
.get() - возвращает массив выделенных элементов
$('#button').click(function(){
//создаем объект контроля над выделением конкретного блока
var blockSelection = $('#block').controlSelectedElemets();
//выведет массив выделенных объектов
console.log(blockSelection.get());
});
Последний раз редактировалось Zuenf, 30.01.2014 в 16:28.
|
|
29.01.2014, 17:45
|
|
Профессор
|
|
Регистрация: 27.05.2010
Сообщений: 33,126
|
|
Zuenf,
странно как-то считает....
Последний раз редактировалось рони, 24.05.2014 в 14:09.
|
|
29.01.2014, 17:52
|
|
Кандидат Javascript-наук
|
|
Регистрация: 27.01.2012
Сообщений: 134
|
|
рони, странно, у меня все нормально
Буду искать баг.
|
|
29.01.2014, 17:59
|
|
Кандидат Javascript-наук
|
|
Регистрация: 27.01.2012
Сообщений: 134
|
|
Нашел, исправил, обновил пример.
|
|
|
|