Search code examples
javascriptmobile-safarifamo.us

Famo.us: How to put form fields inside a scrolling view that works on iOS and Safari?


I’m trying to create a scrolling form for a mobile web app using a Famous ScrollView. However, iOS exhibits severe display bugs when scrolling while the keyboard is active. This occurs whether using an InputSurface or embedding an <input> directly in the Surface HTML.

What’s the best way to achieve a scrollable input form that supports Mobile Safari?

(JSFiddle code replicated below; to see the bug, tap on a text field and then attempt to scroll while keyboard is active.)

Famous.loaded(function () {
    var Engine = Famous.Core.Engine;
    var Surface = Famous.Core.Surface;
    var Scrollview = Famous.Views.Scrollview;
    var Transform = Famous.Core.Transform;

    var mainContext = Engine.createContext();

    var scrollview = new Scrollview();
    var surfaces = [];

    scrollview.sequenceFrom(surfaces);

    var inputhtml = '<div><input type="text" value="test: edit me" /></div>';

    for (var i = 0, temp; i < 40; i++) {
        temp = new Surface({
            content: inputhtml,
            size: [undefined, 200],
            properties: {
                backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
                lineHeight: "200px",
                textAlign: "center"
            },
            index: i
        });

        temp.pipe(scrollview);
        surfaces.push(temp);
    }
    mainContext.add(scrollview);
});

Solution

  • The issue is that famous scroll view is not truly scrollable , they animate the group so that it looks smooth , and to add the pull to refresh effect.

    A solution for this : there is no really a direct and neat solution for this as the iOS keyboard animation will always focus into input and on the other hand famous uses a hidden indicator that works like a scroll bar , which conflicts with the iOS focus algorithm , so the only possible solution is that famous changes the way they animate scrollable view.

    Workaround:

    • Fortunately there is workaround , if user will scroll away from input it means they may be not interested in inputting text anymore , so we can blur the target input once the view is scrolled.
    • after lot of searching I didnt really find a rigid event for famous to indicate scroll start event, however mutation observer worked well to achieve the required result.

    Famo.us ScrollView MouseOver - jsFiddle demo

      <link rel="stylesheet" type="text/css" href="/css/result-light.css">
    
    
          <link rel="stylesheet" type="text/css" href="https://rawgit.com/jperl/famous-compiled/e9c12b1fa657820d3f9bad093ea72e8ee2dfec46/dist/lib/famous/core/famous.css">
    
    
    
          <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.js"></script>
          <script type="text/javascript" src="http://rawgit.com/jperl/famous-compiled/e9c12b1fa657820d3f9bad093ea72e8ee2dfec46/dist/src/4f231991.polyfills.js"></script>
    
          <script type="text/javascript" src="https://rawgit.com/jperl/famous-compiled/97f85aaa64af255adfe0407c941e0e859b0759bc/dist/src/804b0adb.main.js"></script>
    
    
      <style>
        input { font-size: 40px; }
      </style>
    
    
    
    <script type="text/javascript">
    var observer;
    window.onload=function(){
    Famous.loaded(function () {
        var Engine = Famous.Core.Engine;
        var Surface = Famous.Core.Surface;
        var Modifier = Famous.Core.Modifier;
        var Scrollview = Famous.Views.Scrollview;
        var Transform = Famous.Core.Transform;
    
        var mainContext = Engine.createContext();
    
        var scrollview = new Scrollview();
        var surfaces = [];
    
        scrollview.sequenceFrom(surfaces);
    
        var inputhtml = '<div><input type="text" value="test: edit me" /></div>';
    
        for (var i = 0, temp; i < 40; i++) {
            temp = new Surface({
                content: inputhtml,
                size: [undefined, 200],
                properties: {
                    backgroundColor: "hsl(" + (i * 360 / 40) + ", 100%, 50%)",
                    lineHeight: "200px",
                    textAlign: "center"
                },
                index: i
            });
    
            temp.pipe(scrollview);
            surfaces.push(temp);
        }
        mainContext.add(scrollview);
    
    });
    $("body").on("focus","input",function(){Observe($(this));});
    $("body").on("blur","input",function(){endObserver();})
    }
    
    
    function Observe(event_input)
    {
      var target = document.querySelector('.famous-group');
    
    // create an observer instance
    observer = new MutationObserver(function(mutations) {
      mutations.forEach(function(mutation) {
        event_input.blur();
        console.log(mutation.oldValue);
      });    
    });
    
    // configuration of the observer:
    var config = { attributes: true, attributeOldValue: true, characterData: true };
    
    // pass in the target node, as well as the observer options
    observer.observe(target, config);
    }
    function endObserver()
    {
      observer.disconnect();
    }
    
    </script>
    
    
    </head>
    <body>
    </body>
    
    </html>
    

    The live example can be found on my page