Make HTML5 draggable items scroll the page?
Asked Answered
C

4

29

I'm using the HTML5 attribute draggable = "true" on some of my divs on my webpage. I want it so that when you drag one of these items to the bottom of the page, it scrolls the page down and when you drag it to the top, it scrolls the page up.

I will eventually make a playlist on my sidebar, and since it will not always be on view depending on where you're looking on the page, the page needs to scroll when you're dragging.

My page is here and you can try dragging the pictures of the posts around. On Chrome, it automatically lets me scroll down when I drag to the bottom, but not up. On Firefox, it doesn't automatically let me scroll either direction. Any help?

Here's a simple jsfiddle to get you started. On Chrome you should be able to drag the Google icon down and have it scroll the page down, but not going up.

Catabasis answered 15/9, 2013 at 6:46 Comment(8)
well you could make a javascript/jquery solution but i'll wait with posting it since I'd like to know if there's some other wayHolotype
@Holotype why not let the OP decide if your method suits him? :)Numbers
@Numbers cause you think that anyone implementing a draggable function inside a window would have thought that out and made a solution for it.Holotype
@Holotype do you have a good solution using javascript or jquery? I would've thought the browswers would be smart enough to know to do this automatically, but I guess not...Catabasis
you can use the event listener that is provided in the html5 documentation then use scrollTo together with pageY to make it scrollHolotype
I tried to attach a dragover listener on the whole document that fires fine when I am dragging one of my elements, but I can't find the current y position from either event.pageY or event.target.pageY. Do you have any ideas? Update jsfiddle: jsfiddle.net/dFPVr/19Catabasis
Just a hint for Chrome: it let you scroll up the page if you place your cursor right below the upper end of your viewpoint (aka under the fav or address bar)Inman
Update in 2022: Firefox as of v97 automatically scrolls down while dragging, at least in your fiddle. Safari 15.3 does not, however.Ninny
M
43

here is a code that will scroll-up or scroll-down your page while you are dragging something. Just placing your dragging object at top or bottom of the page. :)

    var stop = true;
    $(".draggable").on("drag", function (e) {

        stop = true;

        if (e.originalEvent.clientY < 150) {
            stop = false;
            scroll(-1)
        }

        if (e.originalEvent.clientY > ($(window).height() - 150)) {
            stop = false;
            scroll(1)
        }

    });

    $(".draggable").on("dragend", function (e) {
         stop = true;
    });

    var scroll = function (step) {
        var scrollY = $(window).scrollTop();
        $(window).scrollTop(scrollY + step);
        if (!stop) {
            setTimeout(function () { scroll(step) }, 20);
        }
    }
Mccollum answered 13/3, 2015 at 17:19 Comment(5)
Hi and welcome to StackOverflow! When answering a question it's often helpful to include some explanation to go along with an answer (as opposed to just a block code). Thanks!Noeminoesis
I modified this for horizontal scrolling within a container element... change the x's to y's, but be sure to get the $(element).offset().left position as a left edge.Carson
This response and associated upvotes is a little confusing. The drag event does not expose any drag related coordinates during an HTML5 drag. I don't understand how this can work without that data...Charteris
It's an elegant answer, encourages me to stick with native html5 dragged, could you update it for the x axis?Gardas
@kamelkev. It does work, but only on mobile devices, when using this on desktop e.originalEvent.clientY is always 0Yates
K
1

I have made a simple JavaScript drag and drop class. It can automatically scroll up or down the page while dragging. See this jsfiddle. Also avaliable at my github page. Dragging at a high speed is not recommended now. I need to work out that.

Code below is a part of the library.

var autoscroll = function (offset, poffset, parentNode) {
  var xb = 0;
  var yb = 0;
  if (poffset.isBody == true) {
    var scrollLeft = poffset.scrollLeft;
    var scrollTop = poffset.scrollTop;
    var scrollbarwidth = (document.documentElement.clientWidth - document.body.offsetWidth); //All
    var scrollspeed = (offset.right + xb) - (poffset.right + scrollbarwidth);
    if (scrollspeed > 0) {
      this.scrollLeft(parentNode, scrollLeft + scrollspeed);
    }
    scrollspeed = offset.left - (xb);
    if (scrollspeed < 0) {
      this.scrollLeft(parentNode, scrollLeft + scrollspeed);
    }
    scrollspeed = (offset.bottom + yb) - (poffset.bottom);
    if (scrollspeed > 0) {
      this.scrollTop(parentNode, scrollTop + scrollspeed);
    }
    scrollspeed = offset.top - (yb);
    if (scrollspeed < 0) {
      this.scrollTop(parentNode, scrollTop + scrollspeed);
    }
  } else {
    var scrollLeft = offset.scrollLeft;
    var scrollTop = offset.scrollTop;
    var scrollbarwidth = parentNode.offsetWidth - parentNode.clientWidth; //17
    var scrollbarheight = parentNode.offsetHeight - parentNode.clientHeight; //17
    var scrollspeed = (offset.right + xb) - (poffset.right - scrollbarwidth);
    if (scrollspeed > 0) {
      this.scrollLeft(parentNode, scrollLeft + scrollspeed);
    }
    scrollspeed = offset.left - (xb + poffset.left);
    if (scrollspeed < 0) {
      this.scrollLeft(parentNode, scrollLeft + scrollspeed);
    }
    scrollspeed = (offset.bottom + scrollbarheight + yb) - (poffset.bottom);
    if (scrollspeed > 0) {
      this.scrollTop(parentNode, scrollTop + scrollspeed);
    }
    scrollspeed = offset.top - (yb + poffset.top);
    if (scrollspeed < 0) {
      this.scrollTop(parentNode, scrollTop + scrollspeed);
    }
  }
};
Keynesianism answered 20/10, 2017 at 13:3 Comment(0)
P
1

Here is the javascript version of AngularPlayers answer, I added horizontal support. I noticed both the JQuery solution and Javascript solution have a bug on mobile safari that allows the page to infinitely grow when the bounce effect from overscrolling happens.

The purpose of VerticalMaxed and HorizontalMaxed is to check that the scroll bars are not maxed before scrolling again. This prevents the page from growing during overscroll bounce.

var stopX = true;
var stopY = true;

document.addEventListener('drag', function(e) {
    if (event.target.classList.contains('draggable')) {
        stopY = true;
                    
        // Handle Y
        if (e.clientY < 150) {
            stopY = false;
            scroll(0,-1)
        }

        if ((e.clientY > ( document.documentElement.clientHeight - 150)) && !VerticalMaxed()) { 
            stopY = false;
            scroll(0,1) 
        }
                
        // Handle X
        stopX = true;
        if (e.clientX < 150) {
            stopX = false;
            scroll(-1,0)
        }

        if ((e.clientX > ( document.documentElement.clientWidth - 150)) && !HorizontalMaxed()) { 
            stopX = false;
            scroll(1,0)
        }
    }

});

document.addEventListener('dragend', function(e) {
    if (event.target.classList.contains('draggable')) {
        stopY = true;
        //stopY = true;
        stopX = true;
    }
});

// On drag scroll, prevents page from growing with mobile safari rubber-band effect
var VerticalMaxed = function(){ return (window.innerHeight + window.scrollY) >= document.body.offsetHeight}
var HorizontalMaxed = function(){ return (window.pageXOffset) > (document.body.scrollWidth - document.body.clientWidth);}

var scroll = function (stepX, stepY) {
    var scrollY = document.documentElement.scrollTop || document.body.scrollTop;
    var scrollX = document.documentElement.scrollLeft || document.body.scrollLeft;
    window.scrollTo((scrollX + stepX), (scrollY + stepY));
                
    if (!stopY || !stopX) {
        setTimeout(function () { scroll(stepX, stepY) }, 20);
    }
}
Parik answered 29/6, 2022 at 19:55 Comment(0)
E
0

was trying to make it smoother, found that requestAnimationFrame works good for that. based on the answer of AngularPlayer

var MaxScrollSpeed = 20;
var scrollDelta = 0;
var scrollingTimer = null;

$(".draggable").on("drag", function (e) {

    if (e.originalEvent.clientY < 150) {
        scrollDelta = Math.max(-MaxScrollSpeed, e.originalEvent.clientY - 150);
    } else if (e.originalEvent.clientY > ($(window).height() - 150)) {
        scrollDelta = Math.min(MaxScrollSpeed, e.originalEvent.clientY - ($(window).height() - 150));
    } else {
        scrollDelta = 0;
    }

    if (scrollDelta !== 0 && !scrollingTimer) {
        scrollingTimer = requestAnimationFrame(scroll);
    }

});

$(".draggable").on("dragend", function (e) {
    scrollDelta = 0;
    if (scrollingTimer) {
        cancelAnimationFrame(scrollingTimer);
    }
    scrollingTimer = null;
});

var scroll = function() {
    if (scrollDelta !== 0) {
        window.scrollBy({left: 0, top: scrollDelta, behavior: 'instant'});
    }
    scrollDelta = 0;
    scrollingTimer = null;
}
Elman answered 7/3, 2024 at 1:39 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.