Native browser touch drag using overflow scroll

Mobiles and Tablets allow you to touch/drag scroll pages. This is a great interaction and the animation looks amazing. How can we get this functionality for desktop browsers using JavaScript? A look at some libraries and how to write your own.


There are several libraries out there which can enable this for desktop browsers, one of the best being iScroll. iScroll is great, but it has one main disadvantage. Content is moved to an absolutely positioned div to allow the scroll animation which means it's different from the mobile and tablet versions.

Looking around there doesn't seem to be a solution which allows you to scroll the native browser scrollbars directly with an animation... well it must be possible... so lets make our own!

First we want to identify the drag target, or fall back to use the document body instead, note that IE and firefox use documentElement instead of body to scroll a page:

if (id) {
    el = document.getElementById(id);
} else {
    if (isIE || isFirefox) {
        el = document.documentElement;
    } else {
        el = document.body;
    }
}

Next we need to add the event listeners for mouse down, move and up to the target element:

function addEvent(name, el, func) {
    if (el.addEventListener) {
        el.addEventListener(name, func, false);
    } else if (el.attachEvent) {
        el.attachEvent('on' + name, func);
    } else {
        el[name] = func;
    }
}
addEvent('mousedown', el, onMouseDown);
addEvent('mousemove', el, onMouseMove);
addEvent('mouseup', el, onMouseUp);

The mouse down function has a check for IE to ensure the event is captured and prevents dragging if the image is the target:

function onMouseDown(e) {
    if (!e) { e = window.event; }
    if (e.target && e.target.nodeName === 'IMG') {
        e.preventDefault();
    } else if (e.srcElement && e.srcElement.nodeName === 'IMG') {
        e.returnValue = false;
    }
    startx = e.clientX + el.scrollLeft;
    starty = e.clientY + el.scrollTop;
    diffx = 0;
    diffy = 0;
    drag = true;
}

The mouse move function updates the values only if the user is holding down the mouse button:

function onMouseMove(e) {
    if (drag === true) {
        if (!e) { e = window.event; }
        diffx = (this.startx - (e.clientX + el.scrollLeft));
        diffy = (this.starty - (e.clientY + el.scrollTop));
        el.scrollLeft += diffx;
        el.scrollTop += diffy;
    }
}

And the mouse up function has the magic, it takes the move values and gradually slows down the values using request animation frame to make a smooth animation:

onMouseUp: function (e) {
    if (!e) { e = window.event; }
    drag = false;
    var start = 1,
        animate = function () {
            var step = Math.sin(start);
            if (step <= 0) {
                window.cancelAnimationFrame(animate);
            } else {
                el.scrollLeft += diffx * step;
                el.scrollTop += diffy * step;
                start -= 0.02;
                window.requestAnimationFrame(animate);
            }
        };
    animate();
}

To support requestAnimationFrame in older browsers you will need to include a shim script such as this one:

And an example of the script working (IE8+, Firefox, Safari, Chrome):

https://kmturley.github.io/touch-scroll/