Skip to content

Instantly share code, notes, and snippets.

@advitum
Last active August 29, 2015 14:04
Show Gist options
  • Save advitum/7f210568064db5aef320 to your computer and use it in GitHub Desktop.
Save advitum/7f210568064db5aef320 to your computer and use it in GitHub Desktop.
Sorting tables with drag and drop

Sorting tables with drag and drop

We start with a simple HTML table:

<table class="sortable">
	<tbody>
		<tr data-id="1">
			<td>Some content</td>
			<td>More content</td>
		</tr>
		<tr data-id="1">
			<td>Does not matter</td>
			<td>Any table will do</td>
		</tr>
		<tr data-id="1">
			<td>There are only two requirements:</td>
			<td>The table needs to have a tbody-tag. And every row should have an data-id-attribute.</td>
		</tr>
	</tbody>
</table>

Then we add some JavaScript:

$(document).ready(function() {
	$('table.sortable').each(function() {
		$(this).children('thead, tfoot').children('tr').prepend('<th></th>');
		
		var $sortable = $(this).children('tbody');
		$sortable.children('tr').prepend('<td><i class="fa fa-arrows" unselectable="on"></i></td>');
		
		$sortable.on('selectstart', '.fa-arrows', function(e) {
			e.preventDefault();
			return false;
		}).on('mousedown', '.fa-arrows', function(e) {
			e.preventDefault();
			
			var $dragged = $(this).parent().parent();
			
			var $ghost = $dragged.clone();
			$ghost.wrapAll('<table class="ghost"><tbody></tbody></table>');
			$ghost.children('td').each(function() {
				$(this).width($dragged.children().eq($(this).prevAll().size()).width());
			});
			$ghost = $ghost.parent().parent();
			$ghost.css({
				left: $sortable.parent().offset().left + 'px',
				top: $dragged.offset().top + 'px'
			});
			$sortable.parent().after($ghost);
			
			$dragged.addClass('dragged');
			$dragged.data({
				offset: e.pageY - $dragged.offset().top,
				moved: false
			});
			
			$(document).one('mouseup', function(e) {
				e.preventDefault();
				
				$sortable.parent().next('table.ghost').remove();
				$(document).off('mousemove.dragging');
				
				var $dragged = $sortable.children('tr.dragged');
				if($dragged.data('moved')) {
					var positions = [];
					$sortable.children('tr').each(function() {
						positions.push($(this).data('id'));
					});
					$.ajax(document.location.href, {
						data: {
							positions: positions
						},
						type: 'POST'
					});
				}
				
				$dragged.removeClass('dragged').removeData('offset moved').css('top', 'auto');
			});
			
			$(document).on('mousemove.dragging', function(e) {
				e.preventDefault();
				
				var $dragged = $sortable.children('tr.dragged');
				var mouse = e.pageY - $dragged.data('offset');
				
				var $ghost = $sortable.parent().next('table.ghost');
				$ghost.css('top', mouse + 'px');
				
				var $next = $dragged.next('tr:not(.ghost)');
				if($next.size() && $next.offset().top < mouse) {
					$next.after($dragged);
					$dragged.data('moved', true);
				}
				
				var $prev = $dragged.prev('tr:not(.ghost)');
				if($prev.size() && $prev.offset().top > mouse) {
					$prev.before($dragged);
					$dragged.data('moved', true);
				}
			});
		});
	});
});

Optionally some (S)CSS to make things prettier:

table {
	&.sortable, &.ghost {
		.fa-arrows {
			cursor: pointer;
			cursor: -webkit-grab;
			cursor: -moz-grab;
			
			-moz-user-select: none;
			-khtml-user-select: none;
			-webkit-user-select: none;
			user-select: none;
			
			&:active {
				cursor: pointer;
				cursor: -webkit-grabbing;
				cursor: -moz-grabbing;
			}
		}
		
		tbody {
			td:first-child {
				width: 20px;
				text-align: center;
			}
			
			tr.dragged {
				opacity: .5;
			}
		}
		
		& + table.ghost {
			@include box-shadow(0px 0px 2px rgba(0, 0, 0, .5));
			
			position: absolute;
			
			td {
				background: #ffffff;
			}
		}
	}
}

By the way, I am using Font Awesome to show an icon.

Now, when the user drags a row of the table and changes the sorting of the rows, an array containing the row's ids in the new order is send to the current url. There you can simply catch them via PHP for example and save the new sorting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment