Human’s are natural pioneers. Don’t be afraid to roll your own

So in an effort to become more organized I have begun work on a cross platform “to-do/calendar” web application. The end goal is to have a functional, fast, and easy to use app that is inspired by the impeccably designed Clear app for iOS:
Unfortunately, as an Android user I cannot use this app…but as a Software Engineer I can certainly make my own!
So two days ago I got to work, and messed around with jQuery libraries and web components to try and get the list functionality I was looking for.
Unfortunately, that proved difficult. Both jQueryUI’s and Rubaxa’s Sortable libraries, while excellent in their own right, did not fit what I was looking for. In fact, in my research I found that no drag-n-drop list sorting library or component could handle both the sorting AND the slide-to-perform-action functionality I needed.
That meant no shortcuts, time to roll my own path.
Awesome, lets go pioneering!
Horizontal
As mentioned before, I needed to have a <li> element respond to user input, and have different responses and functions based on if it is horizontal or vertical movement. This meant I needed 3 events, mousedown, mouseup, and mousemove.
$('#todos li').on('mousedown', handleMouseDown);
$('body').on( 'mouseup', handleMouseUp);
$('body').on( 'mousemove', handleMove);When the mouse is down, I need to set a flag that the mouse is down, and record the coordinates of the mouse and the <li> element.
//these variables are declared in an outer scope
function handleMouseDown(evt){
mouseDown = true;
listElement = $(evt.target);
startX = evt.clientX;
startY = evt.clientY;
listElementInitialX = listElement.offset().left;
listElementInitialY = listElement.offset().top;
}
Now I need to handle movement when the mouse is down. I did this by first checking if the mouseDown variable is true, then taking the difference between the x and the y coordinates and seeing if it is greater than a SLIDE_THRESHOLD constant I set at the top of my .js file. I have the constant set to 1/60th of the screens width, which provides a nice balance. Once I that threshold is passed either vertically or horizontally, I set the respective flag and all future movement will be passed to either handleHorizontal() or handleVertical() functions until the mouse button is released.
//SLIDE_THRESHOLD IS SET TO (screen.availWidth) / 60;
function handleMove(evt){
if ( mouseDown ) {
moveX = evt.clientX;
moveY = evt.clientY;
if ( !movingHorizontal && !movingVertical ) {
if ( Math.abs( startX - moveX ) > SLIDE_THRESHOLD ) {
//start moving horizontal
movingHorizontal = true;
movingVertical = false;
}else if ( Math.abs(startY - moveY) > SLIDE_THRESHOLD ) {
//start moving vertically
movingHorizontal = false;
movingVertical = true;
}
} else {
if ( movingHorizontal ) {
handleHorizontal(evt);
} else {
handleVertical(evt);
}
}
}
}Currently, the handleHorizontal() function doesn’t do much, it only slides with the mouse cursor, I plan on adding delete/complete actions in the future.
function handleHorizontal(evt) {
listElement.offset(
{left: listElementInitialX - (startX - moveX)}
);
}Lastly, is the mouse released functionality. Here I add a nice animation to return the element to its starting point, and reset all my flags.
function handleMouseUp(evt){
if ( mouseDown ) {
endX = evt.clientX;
endY = evt.clientY;
listElement.animate({left: 0}, 1000, "easeOutBounce");reset();
}
}

Vertical
Now for the hard part. There’s a reason people use JS Sortable libraries…but fortunately this is a relatively primitive drag-n-drop sort. In fact, the only functionality I need is dragging, dropping, and re-ording of the list elements. So to begin I modified my mouseDown event to record the initial “index” of the list element target. My list elements have a “sort-id” attribute (id goes from 0 to n) pulled from the database, which I access like so:
function handleMouseDown(evt){
...
listElementParent = listElement.parent();
listElementIndex = parseInt(listElementParent.attr("data-sort-id"));
...
}I then modified my handleMove event to set a z-index for the dragged element to 100, and the color to white.
function handleMove(evt){
...
else if ( Math.abs(startY - moveY) > SLIDE_THRESHOLD ) {
movingHorizontal = false;
movingVertical = true;
listElement.css({
"z-index": 100,
"background-color": "white"
});
}
...
}The most complicated code in the project is the handling of vertical movement. Let’s break it down. So to start, I get the vertical distance moved by the cursor divided by the height of each element. I then calculate the nextIndex of the element by subtracting the (vertical distance / ITEM_HEIGHT) from the initial element index. So if my height is 40, and I move 80, I know to move 2 indexes up. I then check to make sure we don’t hit 0 or a number out of bounds, and set the elements css to follow the cursor on the Y axis. Finally, I have a “currIndex” variable that I update whenever the index of the dragged element changes. If the nextIndex is different from the currIndex, I detach the li and place it in the list where it belongs.
function handleVertical(evt) {
var list = $("#todos");
var verticalDistance = (ITEM_HEIGHT *
Math.floor(Math.abs( startY - moveY ) /
ITEM_HEIGHT )); if (startY - moveY < 0 ) {
verticalDistance = -verticalDistance;
}nextIndex = (listElementIndex -
(verticalDistance / ITEM_HEIGHT));
nextIndex = Math.max(nextIndex, 0);
nextIndex = Math.min(nextIndex, list.children().size());
listElement.css({
top: evt.pageY
}); if ( nextIndex != currIndex ) {
currIndex = nextIndex;
listElementParent.detach(); if ( nextIndex == 0 ) {
list.prepend(listElementParent);
}else if ( nextIndex >= list.children().size() ) {
list.append(listElementParent);
} else {
$(list.children()[nextIndex - 1]).after(listElementParent);
}
}
}Finally, we just need to update our mouseUp event. I check if we have been moving vertical, and if so set the z-index back to one, set the location to its natural place within the list, and then loop through all the lists children, setting the sort-id with the updated index value.
function handleMouseUp(evt){
...
if ( movingVertical ) {
var list = $("#todos");
listElement.css({
"z-index": 1,
top: "auto"
}); list.children().each(function(index, elem){
$(elem).attr("data-sort-id", index);
})
}
...
}(I will soon add an AJAX call to save the new sort locations to the database.)

And voila! Sorting is handled!
Conclusion
Both user interactions are working perfectly and it was a bit easier than I thought it would be to make the functionality on my own. I also learned a lot while creating this, which just isn’t the case through the use of libraries.
Sometimes it’s nice and easy to use magic libraries and frameworks, but the best feeling in programming is when you create the magic yourself and satisfy your natural instincts to be a pioneer.
Until next time!
Noteworthy Personal Stuff:
- I got a job after moving up to New York! Check the company out:
http://www.structuredweb.com
- I'm working on setting up and "ricing" an Arch Linux system on a backup laptop I have. Check out this subreddit for examples of what I'm talking about. Seems like fun! Maybe a blogpost in the future!
