Question

I'm using jQuery UI's draggables and droppables functionality. To make it work on touch devices, I'm using jQuery UI Touch Punch.

I have a list of draggables that don't have much space around them. On touch devices, I found that the only way to scroll through the list was to use the very narrow spaces around the draggables, which isn't user friendly. I thought about adding buttons to scroll up and down, but I think that would likely feel foreign and clunky to users who are used to scrolling with swipes. I ended up with kind of a hacky workaround, but it has two issues:

  1. It takes a while before the swipe starts to scroll, which might give the false impression that the website is lagging.

  2. There is no kinetic aspect to the scrolling, so it's a lot more work to scroll than usual.

My Question

Can my code be improved to fix the above two issues or should I be taking an entirely different approach to fixing this problem?

The Code

jsFiddle demofull screen result of jsFiddle demo

HTML

<p>Drag the colored squares into the gray area on the right.</p>

<ul>
    <li><span class="red"></span></li>
    <li><span class="orange"></span></li>
    <li><span class="yellow"></span></li>
    <li><span class="green"></span></li>
    <li><span class="blue"></span></li>
    <li><span class="purple"></span></li>
    <li><span class="red"></span></li>
    <li><span class="orange"></span></li>
    <li><span class="yellow"></span></li>
    <li><span class="green"></span></li>
    <li><span class="blue"></span></li>
    <li><span class="purple"></span></li>
    <li><span class="red"></span></li>
    <li><span class="orange"></span></li>
    <li><span class="yellow"></span></li>
    <li><span class="green"></span></li>
    <li><span class="blue"></span></li>
    <li><span class="purple"></span></li>
    <li><span class="red"></span></li>
    <li><span class="orange"></span></li>
    <li><span class="yellow"></span></li>
    <li><span class="green"></span></li>
    <li><span class="blue"></span></li>
    <li><span class="purple"></span></li>
</ul>

<div></div>

CSS

p {
    font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
    margin-bottom: 10px;
}

ul, div {
    float: left;
    height: 440px;
    background-color: #ccc;
    border: 1px solid #000;
}

ul {
    width: 127px;
    overflow-y: scroll;
    list-style: none;
}

li span {
    margin: 9px auto;
}

span {
    display: block;
    width: 100px;
    height: 100px;
    border: 5px solid #000;
}

.red {
    background-color: #f00;
}

.orange {
    background-color: #ff8000;
}

.yellow {
    background-color: #ff0;
}

.green {
    background-color: #0f0;
}

.blue {
    background-color: #00f;
}

.purple {
    background-color: #f0f;
}

div {
    width: 330px;
    margin-left: 20px;
}

div span {
    float: left;
}

JavaScript

// BEGIN: My attempt to fix the scrolling issue on touch devices

if ('ontouchstart' in document.documentElement) {
    $('span').on('dragstart', function(e) {
        if ($(this).hasClass('scroll')) {
            e.preventDefault();
        }
    }).on('mousemove', function(e) {
        if ($(this).hasClass('scroll')) {
            $('ul').scrollTop(scroll_top_on_mousedown + mousedown_coords.pageY - e.pageY);
            return false;
        }

        if (typeof(mousedown_coords) == 'object' && Math.max(
                Math.abs(mousedown_coords.pageX - e.pageX),
                Math.abs(mousedown_coords.pageY - e.pageY)
            ) >= 80
        ) {
            if (e.pageX - mousedown_coords.pageX < 70) {
                $(this).addClass('scroll');
            }
        }
    }).on('mousedown', function(e) {
        mousedown_coords = {
            'pageX': e.pageX,
            'pageY': e.pageY,
        };

        scroll_top_on_mousedown = $('ul').scrollTop();
    }).on('touchend', function() {
        $(this).trigger('mouseup'); // Android fix

        delete window.mousedown_coords;
        $('ul .scroll').removeClass('scroll');
    });
}

// END: My attempt to fix the scrolling issue on touch devices

$('span').draggable({
    distance: 90,
    helper: 'clone',
    containment: $('div')
});

$('div').droppable({
    drop: function(event, ui) {
        if ($(this).children('span').length == 12) {
            $(this).empty();
        }

        $(this).append($(ui.draggable).clone());
    }
});
Was it helpful?

Solution

Unfortunately I think the best answer is to rethink your UI. Forcing a user to scroll over draggable elements is just going to present problems. At first I thought using jQuery's tabhold event would work to delay the draggable event but they don't want to play nice together.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top