During the page transition, if we scroll down the first page it will jump to the top before transitioning to the another page. This action is not an error nor is there a perfect solution for it. The issue is this, to animate the transition to the another page the viewport has to be at the top of the page. This way a current page and the page we are transitioning to are vertically lined up.
 
If you were half-way down a long list on one page and the page you are transferring to is only a few hundred pixels tall, a current page would animate off the screen properly. But the new page would not be visible as it would be above the viewport.
 
Two viable solutions exist, we’ll discuss both of them.
 
 

Note: If this tutorial was helpful, need further clarification, something is not working or do you have a request for another Ionic post? Furthermore, if you don't like something about this blog, if something is bugging you, don't like how I'm doing stuff here, again leave me a comment below. I'm here to help you, I expect the same from you. Feel free to comment below, subscribe to my blog, mail me to dragan.gaic@gmail.com, or follow and mention me on twitter (@gajotres). Thanks and have a nice day!

PS. If you want my help, if possible (even if it takes you some time to do that), create a working example I can play with. Use Plunker for AngularJS based questions or jsFiddle for jQuery/jQuery Mobile based questions.


 

Table of Contents

iScroll and its jQuery Mobile derivate iScrollview

 
I have already talked about iScroll and iScrollview. iScroll is a javascript framework that can scroll content in a window within a web browser with very similar behavior to native scrolling on mobile devices such as iPhone and Android. This means you can scroll a window within the browser using native-like scroll bars and physics.
 
That is also a solution to our current problem. Because of iScroll implementation pages will occupy 100% of page height, no matter how far listview is scrolled. At the same time, this also explains why on return listview will still stay in a same position.
 
Of course, in case you want to implement this solution you should pick iScrollview implementation. You would still be able to implement iScroll, but it would take you much more time.
 
I will not show you a working example in this article because I already done that in one of my previous articles. It can be found here: Using iScroll with jQuery Mobile
 

Silent scroll

 
Official documentation: http://jquerymobile.com/demos/1.1.0-rc.1/docs/api/methods.html
 
This jQuery Mobile functionality is also the reason we have this problem in the first place. Before a page transition starts, the original page is silently scrolled to the top.
 
We can play around that, let’s say we have a long listview. When we select listview, its position must be remembered. To achieve this, we must store page height position before page changes. In that case, when we return to the previous page we could use pageshow event to scroll silently to the remembered position.
 
//scroll to Y 100px
$.mobile.silentScroll(100);
 

Example

 
Let’s take a look at this simple example:
 
Demo
 

Embedded working example:

 
Prevent scroll to the top
 
Note: Scroll to the bottom than click any listview element. Now go back and see what will happen.
 

HTML:

<!DOCTYPE html>
<html>
<head>
    <title>jQM Complex Demo</title>
    <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; minimum-scale=1.0; user-scalable=no; target-densityDpi=device-dpi"/>
    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" /> 
    <script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
    <script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>     
</head>
<body>
    <div data-role="page" id="index">
        <div data-theme="a" data-role="header">
            <h3>
                First Page
            </h3>
            <a href="#second" class="ui-btn-right">Next</a>
        </div>

        <div data-role="content">
            <ul data-role="listview" data-theme="a" id="test-list">
                <li><a href="#">item1</a></li>
                <li><a href="#">item2</a></li>
                <li><a href="#">item3</a></li>
                <li><a href="#">item4</a></li>
                <li><a href="#">item5</a></li>
                <li><a href="#">item6</a></li>                
                <li><a href="#">item7</a></li>
                <li><a href="#">item8</a></li>
                <li><a href="#">item9</a></li>
                <li><a href="#">item10</a></li>
                <li><a href="#">item11</a></li>
                <li><a href="#">item12</a></li>
                <li><a href="#">item13</a></li>
                <li><a href="#">item14</a></li>
                <li><a href="#">item15</a></li>
                <li><a href="#">item16</a></li>
                <li><a href="#">item17</a></li>
                <li><a href="#">item18</a></li>
                <li><a href="#">item19</a></li>
                <li><a href="#">item20</a></li>
                <li><a href="#">item21</a></li>
                <li><a href="#">item22</a></li>
                <li><a href="#">item23</a></li>
                <li><a href="#">item24</a></li>
                <li><a href="#">item25</a></li>
                <li><a href="#">item26</a></li>
                <li><a href="#">item27</a></li>                
            </ul>
        </div>

        <div data-theme="a" data-role="footer" data-position="fixed">

        </div>
    </div> 
    <div data-role="page" id="second">
        <div data-theme="a" data-role="header">
            <h3>
                Second Page
            </h3>
            <a href="#index" class="ui-btn-left">Back</a>
        </div>

        <div data-role="content">

        </div>

        <div data-theme="a" data-role="footer" data-position="fixed">

        </div>
    </div>  
</body>
</html>   

Javascript:

$(document).on('pagebeforeshow', '#index', function(){  
    $('#test-list li').off('click').on('click', function(){   
        storePosition.topCoordinate =  $(this).offset().top;
        $.mobile.changePage('#second');
    });    
});

$(document).on('pageshow', '#index', function(){      
    if(storePosition.topCoordinate !== null) {
        $.mobile.silentScroll(storePosition.topCoordinate);
    }
});

var storePosition = {
    topCoordinate : null
}
 

Who Am I?

Between working as a senior Java developer in one of the largest insurance companies in the world and traveling, in my free time, I work as a professional mobile development adviser. I'm also a major jQuery Mobile supporter back at StackOverflow and a forum moderator at the official Ionic Framework forum.

Blogs worth reading

If you're here looking for information related to the Ionic Framework, you will also like these blogs:






  • robert

    In your example, the top of page 1 shows for a few milliseconds prior to scrolling to the saved position. Can you suggest a way to make the transition look seamless from page 2 back to the saved position in page 1?

  • jacky

    the second method “Silent scroll” doesn’t work at all.

    try to click few links and you will be found the back button will mess up the pages.

    • First of “doesn’t work at all” has completely different meaning (“doesn’t work at all” == “not working at all”), it works just fine, you are commenting about completely different problem. In this case it is related to multiple event binding (click event), easily solvable with .off(‘click’).

  • eldy

    this is not seamless like ionic tab.. any better solution?

  • JayJay

    I had the same problem and fixed it with a dirty little trick:
    on “beforePageChange” I shift the page up by the same number of pixels, the page is scrolled down:

    $(document).on(“pagebeforechange”, function (e, data) {
    if (data.options.stopRecursion != 1) {

    // prevent infinite recursion:
    data.options.stopRecursion = 1;

    // prevent pageChange before hiding current Page:
    e.preventDefault();

    // get current ScrollPos and move PageDiv y pixel to top
    // and scroll at the same time to the top
    var y = $(document).scrollTop();
    $(data.options.fromPage).css(“margin-top”, “-” + y + “px”);
    $(document).scrollTop(0);

    //trigger changePage manualy – now Transition-Procedure can scrollTop
    //it will have no effect : we are already at Top 😉
    $.mobile.changePage(data.toPage, data.options);
    }
    });

    // in case of navigating back without page-reload
    // remove page-div displacement:
    $(document).on(“pagehide”, function (e, data) {
    console.log(data);
    $(data.prevPage).css(“margin-top”, “”);
    })