Альтернатива .sortable — новое решение сортировки списка на jQuery

в 14:31, , рубрики: javascript, jquery, sortable, метки: ,

Существует масса решений для сортировки списков, например tableDTD, tableSorter, jQuery.sortable и прочих, которые имеют широкий функционал, но при этом — большой код.
Моё решение использует jQuery, имеет компактный код (42 строки без комментариев) и предусматривает перемешивание элементов (относительно позиционированных) в пределах родительского контейнера.
Весь ход мыслей прокомментирован в JavaScript.

Посмотреть в действии на jsFiddle (некомментированный код).

HTML-разметка

<!DOCTYPE html>
<html><head>

	<meta charset='utf-8'>
	<link type='text/css' rel='stylesheet' href='style.css'>
	<script type='text/javascript' src='https://code.jquery.com/jquery-2.0.3.min.js'></script>
	<script type='text/javascript' src='actions.js'></script>

</head><body>

<div class="container">
	<div class="drop">1</div>
	<div class="drop">2</div>
	<div class="drop">3</div>
	<div class="drop">4</div>
	<div class="drop">5</div>
</div>
<input class="focus-out">

</body></html>

style.css

.container {width:200px;}
.drop {position:relative; top:0px; height:16px; cursor:default; z-index:1;}
.focus-out {position:absolute; width:2px; height:2px; left:-10px; top:-10px; background:none;}

actions.js

$(document).ready(function(){

	// нажали на любом элементе списка
	$('.drop').mousedown(function(e){
		var drop = $(this);
		// верхняя граница контейнера
		var posParentTop = drop.parent().position().top;
		// нижняя граница контейнера
		var posParentBottom = posParentTop + drop.parent().height();
		// координаты исходного положения элемента
		var posOld = drop.position().top;
		// коррекция относительно позиции курсора при нажатии
		var posOldCorrection = e.pageY - posOld;
		// поднимаем нажатый элемент по z-оси
		drop.css({'z-index':2, 'background-color':'#eeeeee'});
		// перетягиваем элемент
		var mouseMove = function(e){
			// получаем новые динамические координаты элемента
			var posNew = e.pageY - posOldCorrection;
			// если элемент перетянут выше верхней границы контейнера
			if (posNew < posParentTop){
				// устанавливаем позицию элемента, равную позиции родителя
				drop.offset({'top': posParentTop});
				// отменяем выделение, переместив фокус в спрятанный (но не скрытый через css) input
				$('.focus-out').focus();
			// если элемент перетянут ниже нижней границы контейнера
			} else if ((posNew + drop.height()) > posParentBottom){
				// устанавливаем позицию элемента, равную позиции родителя + высоте родителя - высоте элемента
				drop.offset({'top': posParentBottom - drop.height()});
				// отменяем выделение
				$('.focus-out').focus();
			// если элемент в пределах контейнера
			} else {
				// устанавливаем новую высоту (элемент перемещается за курсором)
				drop.offset({'top': posNew});
				// если элемент перемещен вверх на собственную высоту
				if (posOld - posNew > drop.height() - 2){
					// меняем элемент с предыдущим в DOM
					drop.insertBefore(drop.prev());
					// обнуляем позицию
					drop.css({'top':0});
					// снова получаем координаты исходного и текущего положения
					posOld = drop.position().top;
					posNew = e.pageY - posOldCorrection;
					posOldCorrection = e.pageY - posOld;
				// если элемент перемещен вниз на собственную высоту
				} else if (posNew - posOld > drop.height() - 2){
					// меняем элемент со следующим в DOM
					drop.insertAfter(drop.next());
					drop.css({'top':0});
					posOld = drop.position().top;
					posNew = e.pageY - posOldCorrection;
					posOldCorrection = e.pageY - posOld;
				}
				// отменяем выделение
				$('.focus-out').focus();
			}
		};
		// отпускаем клавишу мыши
		var mouseUp = function(){
			// завершаем выполнение функции
			$(document).off('mousemove', mouseMove).off('mouseup', mouseUp);
			// плавно возвращаем наш элемент на ближайшее освободившееся место
			drop.animate({'top':0}, 100);
			// возвращаем z-позицию на уровень остальных элементов
			setTimeout(function(){
				drop.css({'z-index':1, 'background-color':'transparent'});
			}, 100);
		};
		// подключаем выполнение функций перемещения и отпускания клавиши мыши
		// и завершаем выполнение функции, если нажата правая клавиша мыши
		$(document).on('mousemove', mouseMove).on('mouseup', mouseUp).on('contextmenu', mouseUp);
	});

});

Нерешённый вопрос: снимать выделение без использования спрятанного input (это простой путь, но совсем не феншуй). Кто что думает по поводу user-select?

Автор: rafaylik

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js