Search code examples
javascripteventsyuidrag

Pairing a Javascript drag event with a scroll event


I'm trying to build a Javascript UI for speeches that mimics the right-hand miniview of the Sublime text editor. After loading some text into a main div, I'm cloning that text in a much narrower div to the right and setting the font-size and line-height to very small values.

Here's the working demo on jsFiddle using an old State of the Union address: http://jsfiddle.net/chriswilsondc/NYSSa/

The yellow div dynamically resizes to represent the visible portion of the text in the lefthand view, as you would expect. I then attach a drag event to that yellow box that mimics the behavior of the scroll bar in the left-hand pane. I need these the native functionality of the scroll bar to the left to bind to the drag behavior of the box on the right. I'm using the YUI drag module, though my question is more general (I think).

Right now, this is a straightforward (if sloppy) matter of manually moving one side when the other is activated.

//right-hand side
dd.on("drag:drag", function(e) {
    console.log("dragged", e);
    var percentScrolled = Y.one("#pane").get('offsetTop') / ht_aer;
    //scroll the left side
    Y.one("#text").set("scrollTop", percentScrolled * ht_text_comp);
    //parallax scrolling of small-text version
    Y.one(".aerial .complete").setStyle("top", -ht_aer_comp * percentScrolled);
});


//left-hand side
Y.one("#text").on("scroll", function(e) {
    console.log("scrolled", e);
    var percentScrolled = e.currentTarget.get('scrollTop') / ht_text_comp;
    //parallax scrolling of small-text version
    Y.one(".aerial .complete").setStyle("top", -ht_aer_comp * percentScrolled);
    //manual drag 
    Y.one("#pane").setStyle('top', ht_aer * percentScrolled);
});

If you watch the console log, however, you see that there's a backfiring event: Programmatically changing the scroll bar by dragging the div causes the "scroll" event to fire, thus attempting to programmatically fire the drag event we originally invoked with the mouse. I'm a bit surprised it doesn't fire infinitely. This seems unwise.

Is there a way to only fire an event if it's invoked by a manual action? Or a better way to link these two disparate events? I don't mind if the solution doesn't use YUI--it's just the framework I'm used to. Thanks.


Solution

  • @Riateche explained why it doesn't create an infinite loop of events. But you may still want the scroll event not to fire during dragging to avoid potential visual glitches. So basically the idea is to stop listening to the scroll event when you start dragging and resume listening to it after done dragging.

    In YUI you can use the drag:start and drag:end events. Something like this:

    function scrollHandler() {
      // ...
    }
    
    var scrollNode = Y.one('#foo');
    scrollNode.on('scroll', scrollHandler);
    
    dd.on('drag:start', function () {
      scrollNode.detach('scroll', scrollHandler);
    });
    
    dd.on('drag:end', function () {
      scrollNode.on('scroll', scrollHandler);
    });