gadicc / meteor-famous-views

Famous, the Meteor Way (with Reactive Blaze Templates/Views)
http://famous-views.meteor.com/
GNU Lesser General Public License v3.0
332 stars 27 forks source link

ScrollViews should cancel the native scrolling of the browser. #160

Open trusktr opened 9 years ago

trusktr commented 9 years ago

Hi @gadicc, I thought I'd open a new issue specifically for this. We'd started discussing it at https://github.com/gadicc/meteor-famous-views/issues/138#issuecomment-63769902.

Basically, in vanilla famo.us, the body won't scroll while you scroll a Famo.us ScrollView. In Famous-views, this isn't the case. Here's the vanilla famo.us behavior:

http://fiddle.jshell.net/ZCMaL/4/show/light/

Those two scroll views are the same height, but one is offset so it's content goes beyond the boundaries of the page. When you scroll the offset scroll view, the body doesn't scroll.

I have the following code:

JavaScript:

var M = Meteor;
var Items = new Mongo.Collection('items');

if (M.isClient) {
    var T = Template;

    Logger.setLevel("famous-views", "info");

    T.body.helpers({
        items: function() {
            return Items.find();
        }
    });

    T.title.rendered = function() {
        console.log('title template rendered', FView.from(this).parent.modifier == FView.from(this).parent._modifier);
    };
}

if (M.isServer) {
    Items.find().forEach(function(item) {
        Items.remove(item._id);
    });

    if (Items.find({}).count() == 0) { // initial items
        for (var i=3; i>0; i--) {
            Items.insert({foo: "Oh yeah"+i});
        }
    }

    M.startup(function () {
        // code to run on server at startup
    });
}

Jade:

head
  title awesome
  meta(name="viewport" content="width=device-width, maximum-scale=1, user-scalable=no")

body
  +famousContext(style="width:500px; height:500px")
    +Scrollview
      +Surface(template='title' size='[undefined,200]')
      +famousEach(items)
        +Surface(template='item' size="[undefined,200]")
  +famousContext(style="width:500px; height:500px")
    +Scrollview
      +Surface(template='title' size='[undefined,200]')
      +famousEach(items)
        +Surface(template='item' size="[undefined,200]")

template(name="item")
  p This is #{foo}.

template(name="title")
  h1 Scrollview Example

I'm using famous-views 0.1.27-pre.4*.

The result is that when you scroll one of the scroll views (make sure your browser is short enough to cut one of them off) then the body will scroll at the same time.

I noticed that if you add the famous-root classes to the <body> and <html> elements, then the problem goes away. This is exactly what vanilla Famo.us is doing. Even if I put the contexts onto divs, Famo.us is still adding famous-root to the body and html. Unfortunately, this has the undesirable effect of making the body unscrollable when you actually have other content beside famo.us contexts.

I'd consider this to be a bug of vanilla Famo.us itself, specifically with the janky scroll view. Other things should not scroll while you scroll the scroll view (but what about with touch when you reach the end of the scroll view)? It's a tricky situation because it means there will have to be code to detect when the scrollview can't scroll further and then stop preventing propagation of the events to that outside things can scroll. o.O

I hope that this gets fixed in Mixed Mode.

Let me see what Famous-angular does...

trusktr commented 9 years ago

I tried to describe the problem in a couple issues on famous: https://github.com/Famous/famous/issues/491 https://github.com/Famous/famous/issues/492

trusktr commented 9 years ago

In the jsfiddle I posted, the famous-root class gets added automatically when you call createContext on any element. It might not be using the latest Famo.us. Let me double check with the latest.

trusktr commented 9 years ago

Confirmed, Famo.us 0.3.1 has the same behavior: https://trusktr.io/scrollViewTest When the contexts get created on the divs, famous-root gets inevitably added to the body and html elements. This seems like undesirable behavior for cases when your app isn't a whole-page famo.us app.

gadicc commented 9 years ago

Yeah, I see what you mean. Weird though; even on the jsfiddle I see the same behaviour of the entire page scrolling at the same time as the scrollview. Maybe because of how jsfiddle affects the page... because even if the scrollview goes off the page, with famous-root I shouldn't see a scrollbar at all, and thus, not see this issue.

I think famo.us outside of appmode is maybe less commonly used / less tested - for now. but yes, I think if not in appmode (or actually, either way), it should cancel these events. are they events and can they be cancelled? i've never come across it before.

and yes, i also thought it was odd behaviour to always set appmode. famous-views breaks from this default behaviour, disables appmode by default, and manually sets it up when needed. see the top and bottom of https://github.com/gadicc/meteor-famous-views/blob/b0752cae7c2fcc0b8cc7d4f532c8202c8171dd01/lib/famous-views.js#L45-L66 and https://github.com/gadicc/meteor-famous-views/blob/b0752cae7c2fcc0b8cc7d4f532c8202c8171dd01/lib/famousContext.js#L90-L95.

gadicc commented 9 years ago

From http://stackoverflow.com/a/1460020/1839099:

Sorry, as far as I'm aware it is impossible to cancel any kind of scroll event.

Both W3 and MSDN say:

Cancelable No Bubbles No

from http://stackoverflow.com/questions/5802467/prevent-scrolling-of-parent-element it sounds like it's possible and there are some events, but when none of the things in that thread that I tested worked / solved the problem and it sounds like cross browser is very problematic.

trusktr commented 9 years ago

There must be a way to cancel the events. If you scroll on the first map demo at http://leafletjs.com/ you'll see the map zooms, and the page doesn't scroll. I've personally never had to cancel scroll events for any of my projects, but that map example right there proves it can be done.

trusktr commented 9 years ago

I just tried it with famous-angular in the Famo.us University, and scrolling a ScrollView doesn't scroll the whole page.

gadicc commented 9 years ago

can you paste the code here?

trusktr commented 9 years ago

@gadicc Try loading up the Famo.us University, one of the Angular lessons, then for the JavaScript paste this:

  angular.module('famous-university', ['famous.angular'])
      .controller('ScrollCtrl', ['$scope', '$famous', function($scope, $famous) {
        var EventHandler = $famous['famous/core/EventHandler'];
        $scope.views = [{color: 'red'}, {color: 'blue'}, {color: 'green'}, {color: 'yellow'}, {color: 'orange'}];
        $scope.myEventHandler = new EventHandler();
    }]);

and for the HTML:

<fa-app ng-controller="ScrollCtrl" style="width:50%; left: 50%; margin-left: 10px">
  <!-- fa-scroll-view receives all events from $scope.myEventHandler, and decides how to handle them -->
  <fa-scroll-view fa-pipe-from="myEventHandler">
      <fa-view ng-repeat="view in views">
        <fa-modifier fa-size="[undefined, 160]">
        <!-- All events on fa-surfaces (click, mousewheel) are piped to $scope.myEventHandler -->
           <fa-surface fa-background-color="view.color"
                        fa-pipe-to="myEventHandler">
           </fa-surface>
          </fa-modifier>
      </fa-view>
  </fa-scroll-view>
</fa-app>

<fa-app ng-controller="ScrollCtrl" style="width:50%">
  <!-- fa-scroll-view receives all events from $scope.myEventHandler, and decides how to handle them -->
  <fa-scroll-view fa-pipe-from="myEventHandler">
      <fa-view ng-repeat="view in views">
        <fa-modifier fa-size="[undefined, 160]">
        <!-- All events on fa-surfaces (click, mousewheel) are piped to $scope.myEventHandler -->
           <fa-surface fa-background-color="view.color"
                        fa-pipe-to="myEventHandler">
           </fa-surface>
          </fa-modifier>
      </fa-view>
  </fa-scroll-view>
</fa-app>

Try scrolling one of the scroll views, then try scrolling in the dark areas outside the scroll views. They are scroll independently. Maybe you can do what Famous-angular is doing.

trusktr commented 9 years ago

Was already on it. :D

gadicc commented 9 years ago

I think I see what's going on here. Notice that when you flick up, the document scrollbar still does a lot of stuff. Just the other scrollview doesn't move. I think I recall when investigating the context stuff for the other issue that they used position: fixed for some stuff, but I wasn't sure why and I don't think I put this in. Now I guess I have the answer. But what about for non famous components then??

trusktr commented 9 years ago

Trying scrolling slowly to make sure your mouse is on top of the colored squares. For me, there's no scrolling of the body when I scroll one of the ScrollViews. I'm in Chrome. When the last square in a scroll view goes above the mouse, then the body should start scrolling because at that point the mouse pointer is no longer on top of any surface, and since the other ScrollView (still at it's original position) has content that goes below the end of the body (make sure your window is small enough) then the body will scroll. But if you scroll both scroll views so that both of their contents are not past the end of the body, then the body will no longer scroll. That's actually the behavior we should be expecting.

Let me update the example by putting content above and below to illustrate.

trusktr commented 9 years ago

Alright, try this:

  angular.module('famous-university', ['famous.angular'])
      .controller('ScrollCtrl', ['$scope', '$famous', function($scope, $famous) {
        var EventHandler = $famous['famous/core/EventHandler'];
        $scope.views = [{color: 'red'}, {color: 'blue'}, {color: 'green'}, {color: 'yellow'}, {color: 'orange'}];
        $scope.myEventHandler = new EventHandler();
    }]);
<p>paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph </p>
<p>paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph </p>
<fa-app
  ng-controller="ScrollCtrl"
  style="overflow: hidden; height: 200px;">
  <!-- fa-scroll-view receives all events from $scope.myEventHandler, and decides how to handle them -->
  <fa-scroll-view fa-pipe-from="myEventHandler">
      <fa-view ng-repeat="view in views">
        <fa-modifier fa-size="[undefined, 160]">
        <!-- All events on fa-surfaces (click, mousewheel) are piped to $scope.myEventHandler -->
           <fa-surface fa-background-color="view.color"
                        fa-pipe-to="myEventHandler">
           </fa-surface>
          </fa-modifier>
      </fa-view>
  </fa-scroll-view>
</fa-app>
<p>paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph </p>
<p>paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph </p>

If you hover on the paragraph text, you can scroll the whole body. Hover in the ScrollView and you can scroll just the ScrollView.

gadicc commented 9 years ago

Oops, I was noticing the scroll bar change as the document got bigger because of the excess content underneath due to the flick, so nevermind that.

Yeah, you're totally right, they've solved it, we'll have to figure out how :>

gadicc commented 9 years ago

I guess like they said that they don't pipe the surface events directly to the scrollview, "fa-scroll-view receives all events from $scope.myEventHandler, and decides how to handle them", I'll have to take a look at the source when I get a chance.

trusktr commented 9 years ago

Oh, interesting. Here's a simpler example, just surfaces in the scroll view:

<p>paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph </p>
<p>paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph </p>
<fa-app
  ng-controller="ScrollCtrl"
  style="overflow: hidden; height: 200px;">
  <!-- fa-scroll-view receives all events from $scope.myEventHandler, and decides how to handle them -->
  <fa-scroll-view fa-pipe-from="myEventHandler">
     <fa-surface
       fa-size="[undefined, 160]"
       ng-repeat="view in views"
       fa-background-color="view.color"
       fa-pipe-to="myEventHandler">
     </fa-surface>
  </fa-scroll-view>
</fa-app>
<p>paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph </p>
<p>paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph </p>

Yep, it doesn't work without the event handler. Notice also that I had to set overflow:hidden on it, as well as a height, otherwise the content wouldn't be clipped, and without a height the container would be 0px tall (you've already got that taken are of. :)