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> <!--
<span class="drag"></span> <!--
</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;
var over = -1;
var item = '';
var swap = '';
var move = false;
var container = document.getElementById('container');
var ul = document.getElementById('container').children[2];
var phantom = document.getElementById('phantom');
phantom.onmousemove = function(e) { e.preventDefault(); }
ul.onmouseup = function(e) {
phantom.style.display = 'none';
move = false;
}
for(var i = 0; i < ul.children.length; i++) {
ul.children[i].children[1].onmousedown = function(e) {
move = true;
index = Array.prototype.indexOf.call(ul.children,
e.target.parentElement);
item = ul.children[index].children[0].innerHTML;
phantom.children[0].innerHTML = item;
phantom.style.display = 'block';
}
}
for(var i = 0; i < ul.children.length; i++) {
ul.children[i].onmouseover = function(e) {
if(Array.prototype.indexOf.call(ul.children,
e.target) != -1 && move) {
over = Array.prototype.indexOf.call(ul.children, e.target);
swap = ul.children[over].children[0].innerHTML;
ul.children[index].children[0].innerHTML = swap;
ul.children[over].children[0].innerHTML = item;
index = over;
}
}
}
ul.onmousemove = function(e) {
e.preventDefault();
if(index != -1) {
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.