DataZombies / jQTouch

jQT extensions jqt.activityIndicator, jqt.bars (with numeric badges), jqt.checkGroup & jqt.segmentedControl.Get updates via Twitter http://twitter.com/HeadDZombie. If you find this fork useful please make a donation via PayPal (http://tinyurl.com/2fpmx27). See below for demo links.
MIT License
159 stars 34 forks source link

Can't scroll to end of content (contains dynamically loaded elements) #32

Closed ghost closed 13 years ago

ghost commented 13 years ago

I'm dynamically adding elements to a page and can't scroll to the end of the content on first load.

If I start my app it constructs a long unordered list from a JSON object that's fetched via AJAX. If I try to scroll to the last item I can't go far enough. I can see there's one or two more elements if I scroll past the "page end" but I can't get the page to stick there (bounces back). Similar thing happens on the detail page I navigate to from the list: It contains a rather big image on top that gets loaded dynamically. I can't scroll to the end of the content there either.

The strange thing is, this effect only happens on the first load. If I come back to the long list from any other screen (like the detail view) everything works as expected. Same on the detail view itself: on the second load the content is fully visible. Also if I load the app and change the orientation of the device to portrait (and/or back to landscape) iScroll works perfectly. (Manually triggering the "turn" event after app load, didn't yield any results either).

It seems like the setPageHeight() call, is fired too soon (it's in the AJAX callback for the list and in the pageAnimationEnd for the detail view) so it can't really get the real height of the content.

Does anybody have a clue as to where this problem could come from?

Note: this only happens on the device (iPhone 3GS and iPhone 4) but not in the simulator. The issue occurred in the simulator as well, but vanished once I built in the setPageHeight() call.

DataZombies commented 13 years ago

Try putting the setPageHeight() in a document.ready function. From jqt.bars.js's Change Log...

To add iScroll to AJAX loaded pages use this at the bottom of the file...

</div>
<script type="text/javascript" charset="utf-8">
$(document).ready(function(){
    jQT.init_iScroll($('#long'));
});
</script>
<div></div>
ghost commented 13 years ago

I already found that piece of code and tried it, didn't change anything :(

pulling my hair out

DataZombies commented 13 years ago

Are you using jQuery's getJSON()? That's asynchronous code, meaning that when the function is executed JavaScript goes of and does something else until the data is received. getJSON() also doesn't report any failures.You might want to roll your own json function. Here's one that I use:

jsonLoader = function (jsonURL) {
  var jsonData = new XMLHttpRequest(), jsonStatus = false;
  jsonData.open("GET", jsonURL, false);
  try {
    jsonData.send(null);
  } catch (err) {
    jsonData = null;
  }
  if (!jsonData || jsonData === null || jsonData.status !== 200) {
    _errorHandler(null, null, 'JSON Error ' + jsonData.status + ' - file "' + jsonURL + '" ' + jsonData.statusText + '.');
  } else {
    _debug('JSON file "' + jsonURL + '" loaded.');
    try {
      jsonData = JSON.parse(jsonData.responseText);
      jsonStatus = true;
    } catch (err) {
      _errorHandler(null, null, err);
      jsonStatus = false;
    }
  }
  return {
    jsonData: jsonData,
    jsonStatus: jsonStatus
  };
};
DataZombies commented 13 years ago

Here's _debug()

_debug = function (a) {
    if (debug) {
        console.log(a);
    }
};

You can use _debug() instead of _errorHandler()

ghost commented 13 years ago

Yes, I am using getJSON. I was under the impression that the callback of that function would be the right place the implement my code, but I'll try your version and see if it offers any improvement. Thank you!

ghost commented 13 years ago

No, that didn't change anything. It was a good guess, but I don't think that the problem is in the JSON loading after all, since the issue also occurs on the details screen, which uses data from the initial getJSON call but there's no AJAX call on pageAnimationStart. I only substitute the empty tags' contents (with data that's already in the memory) and set the 'src' attribute of the empty image tag on top.

ghost commented 13 years ago

I can't help but getting the impression that this might be an iScroll bug and not an error on my side... I've looked at the output of setPageHeight's console messages. No matter if the content is displayed correctly or not, the output of window.innerHeight and $wrapper.height is always the same (306px and 261px).

I'm running out of ideas where to look for the solution here! Maybe I'm on the completely wrong track here and it's got nothing to do with setPageHeight() any other ideas as to where this issue could originate?

DataZombies commented 13 years ago

Have you tried not initializing a tabbar on the page (just to make sure it's not my fault [I thought they fixed it!] )? Try poking around http://groups.google.com/group/iscroll, http://groups.google.com/group/jqtouch, https://github.com/cubiq/iscroll/issues & https://github.com/senchalabs/jQTouch/issues

ghost commented 13 years ago

Allright, I'll try to extract only the iScroll part since I don't use the tabbar anyway. Thanks for the tip! :)

...but gonna do it tomorrow - it's been too long already today ;)

ghost commented 13 years ago

Ok, now I tried several things including completely stripping out the jqtouch bars extension and implementing iScroll by hand. No change at all :(

But I think I narrowed the problem down to the following: The bug seems to occur because of the images I'm loading. On the first screen, the long list, there is an image on each list element that needs to be loaded from the server (and gets sized down a bit by CSS) and on the details screen theres a (much) bigger version of the items image on top. iScroll sets the inner height after the DOM ready event, but the image loading sometimes finishes after this event, so the height calculation is incorrect.

I tried calling setPageHeight with a timeout of 1 second, which seems to work, but you can't scroll properly before this happens (eg: scrollpane jumps back to start on setPageHeight after 1 second), so this isn't exactly practical either.

Is there any event I can bind to that says that the browser has finished loading and rendering all content?

DataZombies commented 13 years ago

The two-watt light bulb just lit up above my head. Look at the iScroll google group for dynamically loading images: you have to hard code the height.

ghost commented 13 years ago

Wow, setting a fixed image size in CSS really seemed to do it! phew (I started to get a bit crazy over that thing!) Now my images just don't stretch to 100% width in landscape, but at least there are no apparent UI bugs.

Thank you, for your mighty support! :)

DataZombies commented 13 years ago

So I guess you should hard code width, too?

:-D

ghost commented 13 years ago

Yea, those images now have both width and height set in pixels. Works for the moment ;)

DataZombies commented 13 years ago

If you're adding the image dynamically can't you just pass the IMG tag with the dimensions in it?

ghost commented 13 years ago

I could, but what would be the advantage over setting the dimensions in CSS?

DataZombies commented 13 years ago

If you pass the dimensions in with the IMG you can scale it mathematically: find the px for 100% width and apply that percentage to the height.