Skip to content

Instantly share code, notes, and snippets.

@CodeMyUI
Created November 4, 2019 03:10
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save CodeMyUI/bf068e30b26c8503b03b18c9184ed0e3 to your computer and use it in GitHub Desktop.
Save CodeMyUI/bf068e30b26c8503b03b18c9184ed0e3 to your computer and use it in GitHub Desktop.
Accessible drag & drop sortable list

Accessible drag & drop sortable list

I couldn't find a good solution that contained all the things I needed:

  • Accessible
  • Drag & Drop
  • Touch-friendly
  • Sorting (not just drag from box A to box B)
  • Full IE9 support

So I'm toying with this semantic approach. Credit to http://johnny.github.io/jquery-sortable/ for the great sortable plugin.

A Pen by Barry T Smith on CodePen.

License.

<h1>Accessible, drag & drop, touch-friendly, sortable list</h1>
<p>Mouse and touch: drag and drop to reorder. Keyboard and text-based: change the number input and hit enter or the update button.</p>
<form id="sort-it">
<ol>
<li>This is item #1
<label for="custom-number-1">New order:</label>
<input id="custom-number-1" name="custom-number-1" type="number" min="1">
</li>
<li>This is item #2
<label for="custom-number-2">New order:</label>
<input id="custom-number-2" name="custom-number-2" type="number" min="1">
</li>
<li>This is item #3
<label for="custom-number-3">New order:</label>
<input id="custom-number-3" name="custom-number-3" type="number" min="1">
</li>
<li>This is item #4
<label for="custom-number-4">New order:</label>
<input id="custom-number-4" name="custom-number-4" type="number" min="1">
</li>
<li>This is item #5
<label for="custom-number-5">New order:</label>
<input id="custom-number-5" name="custom-number-5" type="number" min="1">
</li>
</ol>
<input type="submit" id="manual-sort" name="manual-sort" value="Update">
</form>
<p>Requires jQuery and this plugin: https://johnny.github.io/jquery-sortable/js/jquery-sortable-min.js</p>
$(function(){
// uses https://johnny.github.io/jquery-sortable/js/jquery-sortable-min.js
$('#sort-it ol').sortable({
onDrop: function(item) {
$(item).removeClass("dragged").removeAttr("style");
$("body").removeClass("dragging");
getInitialOrder('#sort-it li');
}
});
getInitialOrder('#sort-it li');
//bind stuff to number inputs
$('#sort-it ol input[type="number"]').focus(function(){
$(this).select();
}).change(function(){
updateAllNumbers($(this), '#sort-it input');
}).keyup(function(){
updateAllNumbers($(this), '#sort-it input');
});
//bind to form submission
$('#sort-it').submit(function(e){
reorderItems('#sort-it li', '#sort-it ol');
e.preventDefault();
})
}); // end doc ready
function getInitialOrder(obj){
var num = 1;
$(obj).each(function(){
//set object initial order data based on order in DOM
$(this).find('input[type="number"]').val(num).attr('data-initial-value', num);
num++;
});
$(obj).find('input[type="number"]').attr('max', $(obj).length); //give it an html5 max attr based on num of objects
}
function updateAllNumbers(currObj, targets){
var delta = currObj.val() - currObj.attr('data-initial-value'), //if positive, the object went down in order. If negative, it went up.
c = parseInt(currObj.val(), 10), //value just entered by user
cI = parseInt(currObj.attr('data-initial-value'), 10), //original object val before change
top = $(targets).length;
//if the user enters a number too high or low, cap it
if(c > top){
currObj.val(top);
}else if(c < 1){
currObj.val(1);
}
$(targets).not($(currObj)).each(function(){ //change all the other objects
var v = parseInt($(this).val(), 10); //value of object changed
if (v >= c && v < cI && delta < 0){ //object going up in order pushes same-numbered and in-between objects down
$(this).val(v + 1);
} else if (v <= c && v > cI && delta > 0){ //object going down in order pushes same-numbered and in-between objects up
$(this).val(v - 1);
}
}).promise().done(function(){
//after all the fields update based on new val, set their data element so further changes can be tracked
//(but ignore if no value given yet)
$(targets).each(function(){
if($(this).val() !== ""){
$(this).attr('data-initial-value', $(this).val());
}
});
});
}
function reorderItems(things, parent){
for(var i = 1; i <= $(things).length; i++){
$(things).each(function(){
var x = parseInt($(this).find('input').val(), 10);
if(x === i){
$(this).appendTo(parent);
}
});
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="https://johnny.github.io/jquery-sortable/js/jquery-sortable-min.js"></script>
body {font-family: sans-serif;}
input { font-size: 1em; } /* prevent zoom in mobile */
ol {
/* list style is faked with number inputs */
list-style: none;
padding: 0;
}
li {
position: relative;
min-height: 1em;
cursor: move;
padding: .5em .5em .5em 2.5em;
background: #eee;
border: 1px solid #ccc;
margin: .25em 0;
border-radius: .25em;
max-width: 14em;
}
li input {
/* Move these to visually fake the ol numbers */
position: absolute;
width: 1.75em;
left: .25em;
top: .25em;
border: 0;
text-align: center;
background: transparent
}
li label {
/* visually hidden offscreen so it still benefits screen readers */
position: absolute;
left: -9999px;
}
/* sortable plugin styles when dragged */
.dragged {
position: absolute;
opacity: 0.5;
z-index: 2000;
}
li.placeholder {
position: relative;
background: purple;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment