photo credit: @mmick66 on Github

Reordering a JavaScript array based on a drag-and-drop interface

Kevin Salter
Kevin Salter’s Blog

--

Recently I’ve been working on a drag-and-drop interface that allows a user to add/remove/reorder a question block to create a final list of questions.

When a question in the list is dragged-and-dropped, it emits an event with many properties. In this case, I’m only interested in the part of the event that looks like {newIndex: 2, oldIndex: 0}. This says that the first item in the list has been dragged into the 3rd position. When this happens, I need to update the list’s data structure to reflect that change.

In my first attempt at solving this, I found myself habitually reaching for loops and side-effecty operations that involved imperatively manipulating items and pushing them into a new array. It looked something like this:

This works, and actually doesn’t take many lines to do, but I was pretty unsatisfied with the code for 3 reasons. For starters, forEach-ing over an array and pushing the new items to an accumulator array is a pattern that I’d rather avoid. Secondly, that first if-statement that pushes both the moved item and the current item in the array being looped over into the accumulator array is pretty clunky and inelegant. And finally, the last if-statement (whose condition is only satisfied when an item has been dragged from the first position to the last position) feels wasteful, and weirdly tacked on at the end.

Ok, so how can I solve all 3 of those problems? By combining the powers of the ES2015 Array Spread Operator with the Array.prototype.slice() method. The spread operator basically acts as a shorthand for mapping over an array and calling concat(), while the slice() method (not to be confused with the splice() method, which actually mutates data) returns a shallow copy of a portion of an array. So, with a little refactoring, here’s the final result:

💥 It’s so much more readable and maintainable, and we’ve shaved off 5 lines in the process! If you’d like to play with an example, I’ve set up this JSBin example which also contains some basic inline tests.

--

--