Класс выделения элементов. Нужна критика.
Учусь на практике.
Класс получает все выделяемые 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; } } |
Пример возможного использования - http://learn.javascript.ru/play/VT7sZb
Пока делал нашел и исправил несколько косяков. |
Мне кажется такая структура кода странной. Структура убогая и неудобная, но распространённая. Как по мне, так "так верстают только мудаки".:) Есть такая проблема: на любую задачу есть готовое решение, которое немного не подходит. Вот мне нужно выделение, и я думаю: "Надо взять готовое решение!" Нахожу, внедряю и тут мне говорят: "Нужно, чтобы внизу был счётчик выбранных элементов."
- Ну, - думаю я. - Отлично! Это ж класс. Создам дочерний класс с нужным мне функционалом... Хренцы-бубенцы! Наследованием проблему не решить. Тогда многие дают при инициализации передать типа обработчики на "внутренние события" (стандартный для jQ-плагинов паттерн с объектом options). Но такой вариант мне тоже кажется туповатым. Мне кажется, что в решениях, где вся суть в манипуляциях с DOM-элементами, необходимо чтобы на каждую манипуляцию происходило соответствующее событие. Тогда, чтобы добавить счётчик, нужно сделать что-то вроде того: $(".some-container").on({ select: function () { $(".counter", this).html($(".counter", this).html() + 1); }, unselect: function () { $(".counter", this).html($(".counter", this).html() - 1); } }); Это псевдо-быдлокод, чтобы уловить идею.:) |
Про наследование почитаю, не задумывался об этом.
А про собственные события с отдельными названиеми, пробегала мысля(видел как-то давно в исходниках jQuery), может подскажешь где почитать про их создание или как там они делаются? P.S. Спасибо за "наставничество", плюсую :) . |
Zuenf, могу предложить только документацию или гугл.:) С событиями тоже есть проблема: они - глобальные. Так что делать в своём коде событие с именем select - не лучшая мысль, ибо есть уже событие с таким именем. Решить можно с помощью какого-нибудь префикса. Например, у тебя будут имена событий foo_select, foo_startselection и т.д.
|
BallsShaped, ок, спасибо! Будем делать.
|
Сделал в виде события - 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,
странно как-то считает.... |
рони, странно, у меня все нормально:blink:
Буду искать баг. |
Нашел, исправил, обновил пример.
|
Часовой пояс GMT +3, время: 13:46. |