Свобода выбора — области выделения в таблицах aka Excel style

Опубликовано Опубликовано в рубрике HTML, jQuery, PHP

Многие из вас, дорогие читатели, наверняка сталкивались с сервисом google docs, MS Excel, а также табличной версткой =) Одно из направлений современного использования таблиц в HTML — это удобный вывод структурированной информации для работы с ней. Но что если нас заинтересовал какой-то определенный кусок? Можно «накликать» нужные ячейки мышью, но что если у вас есть календарь, из которого надо выбрать, скажем, будни? Одним из удобных способов выбора будет создание пользователем области выделения
Все бы хорошо, да вот только при попытке растянуть привычный прямоугольник по таблице ничего хорошего не выйдет — браузер будет выделять текст по целый линиям.

Нам в очередной раз поможет jQuery.

Но для начала подготовимся и определим структуру. Сгенерируем таблицу на PHP

"); for($j=0; $j < 7; $j++) { $chislo=$chislo+1; echo(""); } echo(""); } ?>
".$chislo."

Ключевым моментом здесь является наименование класса. Каждая колонка относится к классу rc, но унифицируется классом row_col_i_j, где i — номер столбца ячейки, а j — номер строки. Вся таблица обернута в div для определения области выделения.

Визуально определять область выделения будет отдельный div (#select_zone). Его статичную обертку мы поместим поверх div-а с таблицей.

Теперь опишем стили. Для примера их будет немного:

td
{
	background: orange;
}
.selected
{
	background: blue;
}
		
#clickable
{
	z-index: 998;
}
		
#select_zone
{
	width: 0;
	height: 0;
	z-index: 999;
	border: 1px dashed #ff002f;
}

В основном мы определяем div-ы области выделения в верхние слои верстки.

Думаю, достаточно красоты — теперь можно приступить к логике.

Алгоритм работы будет таков:
1. Определяем точку нажатия клавиши мыши.
2. Создаем div выделения.
3. По окончании растягивания фиксируем точку отжатия клавиши мыши.
4. Считаем размеры области.

А теперь в код и по порядку. Определим глобальные переменные, которые понадобятся для работы скрипта:

  var
    x1, //стартовая координата по x
    x2, //финишная координата по x
    y1, //стартовая координата по y
    y2, //финишная координата по y
    trig, //триггер рисования области выделения
    start, //объект со стартовой ячейкой
    finish, //объект с финишной ячейкой
    r, //ширина области выделения
    t; //высота области выделения

Теперь опишем первое событие — нажатие (НО НЕ КЛИК!) мыши. Это .mousedown — «опущенная» клавиша мыши:

 $(document).mousedown(function(e){
	x1 = e.pageX;
	y1 = e.pageY;
				
	start = document.elementFromPoint(x1, y1);
	
	$('.rc').removeClass('selected');
					
	trig = 1;
		$('#clickable').append("
"); $('#select_zone').css('width', 0); $('#select_zone').css('height', 0); $('#select_zone').css('position', 'absolute'); $('#select_zone').css('left', x1); $('#select_zone').css('top', y1); });

По нажатию мы считываем координаты курсора на странице. Затем помещаем в объект верхний DOM-элемент, находящийся по считанным координатам. Замечу, что при наложении многих слоев код не сработает, если таблица не будет самым верхним слоем. Теперь чистим выделенные ячейки, если таковые имелись ранее — удаляем у них класс selected. Выставляем триггер рисования в 1 — это значит, что клавиша зажата и рисовать можно.

После этого добавляем в div-обертку наш div с областью выделения. Определяем ему абсолютное позиционирование с координатами левого верхнего угла, соответствующими координатам нажатия клавиши мыши.

Опишем событие отпускания клавиши (mouseup). Буду описывать его по порядку. Для начала подготовимся к вычислениям:

$(document).mouseup(function(e){
			$('#select_zone').remove();
			
			x2 = e.pageX;
			y2 = e.pageY;
			trig = 0;
			finish = document.elementFromPoint(x2, y2);
			
			f_y = finish.parentNode.rowIndex;
			f_x = finish.cellIndex;
				
			s_y = start.parentNode.rowIndex;
			s_x = start.cellIndex;

Мы удаляем div области выделения, запоминаем координаты отжатия клавиши мыши и определяем соответствующий им DOM-элемент.

Объекты start и finish относятся к классу HTMLTableCellElement. Они имеют специфические свойства — индексы в таблице.

Теперь рассчитаем номера ячеек. .cellIndex возвращает номер ячейки в ряду, начиная от 0. Чтобы получить номер столбца, обращаемся к родительскому элементу — tr. .parentNode.rowIndex вернет номер соответствующего столбца, начиная от 0.

Само выделение может проходить в четырех направлениях: по диагонали направо вниз, по диагонали налево вниз, по диагонали направо вверх, по диагонали налево вверх. Каждому из этих направлений будет соответствовать свой расчет координат div. В каждом случае мы будем использовать цикл для выделения всех ячеек, попавших в область.

if(s_x <= f_x && s_y <= f_y)
	{
		t = f_x - s_x;
		r = f_y - s_y;
				
		for(var i = s_y; i <= s_y+r; i++)
			{
				for(var j = s_x; j <= s_x+t; j++)
					{
						$('.row_col_'+i+'_'+j).addClass('selected');
					}
			}
	}
else if(s_x <= f_x && s_y >= f_y)
	{
		t = f_x - s_x;
		r = s_y - f_y;
				
		for(var i = f_y; i <= f_y+r; i++)
			{
				for(var j = s_x; j <= s_x+t; j++)
					{
						$('.row_col_'+i+'_'+j).addClass('selected');
					}
			}
	}
else if(s_x >= f_x && s_y >= f_y)
	{
		t = s_x - f_x;
		r = s_y - f_y;
		
		for(var i = f_y; i <= f_y+r; i++)
			{
				for(var j = f_x; j <= f_x+t; j++)
					{
						$('.row_col_'+i+'_'+j).addClass('selected');
					}
			}
	}
else if(s_x >= f_x && s_y <= f_y)
	{
		t = s_x - f_x;
		r = f_y - s_y;
		
		for(var i = s_y; i <= s_y+r; i++)
			{
				for(var j = f_x; j <= f_x+t; j++)
					{
						$('.row_col_'+i+'_'+j).addClass('selected');
					}
			}
	}
});

После этого надо описать поведение div-а выделения при перемещениях мышки. Ведь этот div должен рисоваться в реальном времени! Здесь мы применим событие .mousemove() и считаем текущие координаты курсора. После этого сравним их с начальной координатой рисования области. В зависимости от этого сравнения мы будем рисовать div выделения в соответствующую сторону:

$(document).mousemove(function(e){
	if(e.clientX>x1 && e.clientY >y1)
		{
			$('#select_zone').css('width', (e.clientX-x1)+'px');
			$('#select_zone').css('height', (e.clientY-y1)+'px');
		}
	else if(e.clientXx1 && e.clientY y1)
		{
			$('#select_zone').css('left', e.clientX);
			$('#select_zone').css('top', y1);
			$('#select_zone').css('width', (x1-e.clientX)+'px');
			$('#select_zone').css('height', (e.clientY-y1)+'px');
		}				
});

Вот и вся логика! А теперь можно посмотреть демо-пример того, что получилось после наших хитрых манипуляций!

Демо-пример
Демо-пример

По-хорошему, можно накрутить еще много доработок - выделения "пятен" при зажатом CTRL, окрашивание вместе с областью... Но каркас будет тем же!

За сим на сегодня все!

Безошибочного Вам кода!

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *