Reorganisable List with JavaScript






4.80/5 (2 votes)
Example of how to make a list whose items can change positions
Introduction
This tip shows how one can make a <ul>
's <li>
s can be reorganised by "swapping" positions between different <li>
s as the mouse is dragged over them.
A working example can be found here (pure JavaScript version) or here (jQuery version).
This currently works on Google Chrome well. I cannot speak for other browsers.
Using the Code
The "swap" is actually done by taking the content of one <li>
and exchanging it with another one. The HTML for this looks as follows:
<ul>
<li>
<span class="item">Kevin</span> <!-- The content -->
<span class="drag"></span> <!-- The drag icon -->
</li>
...
<li>
<span class="item">Roe</span>
<span class="drag"></span>
</li>
</ul>
The phantom list item that follows the mouse as you hold down the mouse and drag is positioned above the <ul>
and positioned absolutely in the CSS;
<div id="phantom">
<span class="item">Amygdala</span>
<span class="drag"></span>
</div>
The JavaScript is configured to listen for a mousedown
event on the drag icon for any list item. This sets a flag to true
so that any mousemove
event triggered any <li>
triggers a "swap" between the <li>
s. Once the mouseup
event which is attached to the <ul>
element is triggered, the flag is negated to false
preventing any other swaps. The code looks like this:
window.onload = function() {
var index = -1; //This is the item index of the item being moved
var over = -1; //This is the new position
var item = ''; //This is the current items content
var swap = ''; //This is the content of the item it will replace
var move = false; //This determines if we can change two items' positions
var container = document.getElementById('container'); //This is the parent of our list
var ul = document.getElementById('container').children[2]; //This is the list of items
var phantom = document.getElementById('phantom'); //This is the opaque copy of the item being moved
phantom.onmousemove = function(e) { e.preventDefault(); } //This prevent the mouse from highlighting stuff
ul.onmouseup = function(e) {
//This function is called when the action of shifting positions is done when dragging items
phantom.style.display = 'none';
move = false;
}
for(var i = 0; i < ul.children.length; i++) {
ul.children[i].children[1].onmousedown = function(e) {
//This event is attached to the drag icon in the list item
move = true;
index = Array.prototype.indexOf.call(ul.children,
e.target.parentElement); //Get the index of the item in question
item = ul.children[index].children[0].innerHTML; //Get it's content as well
phantom.children[0].innerHTML = item; //Give the phantom item the same content
phantom.style.display = 'block'; //Display the phantom item
}
}
for(var i = 0; i < ul.children.length; i++) {
ul.children[i].onmouseover = function(e) { //This function ensures all list item positions can be swapped
if(Array.prototype.indexOf.call(ul.children,
e.target) != -1 && move) { //Ensure you get the list item position and can move
over = Array.prototype.indexOf.call(ul.children, e.target); // The new position
swap = ul.children[over].children[0].innerHTML; //Get the current content of the new position
ul.children[index].children[0].innerHTML = swap; //Change the content of the item in question
ul.children[over].children[0].innerHTML = item; //Change the content of the other item i.e the swap
index = over; // This ensure that your positions swap every time your move the mouse
}
}
}
ul.onmousemove = function(e) { //This function displays the phantom list item appropriately
e.preventDefault();
if(index != -1) {
//Its position is based on the containers offset top and
//it should be some pixels below the cursor hence the 5
var top = e.pageY - parseInt
(document.getElementsByTagName('ul')[0].getBoundingClientRect().top) + 5;
phantom.style.marginTop = top + 'px';
}
}
}
History
Update: 19-11-2013
I made a few small changes to the code to support Internet Explorer 7+. The changes are just work arounds so the logic remains the same. I unfortunately still can't tell what's going on with Mozilla but if I get it, I'll make the necessary changes.