How do I make iScroll5 work when the image is generated from a DB?

433 views Asked by At

I am using iScroll5 in a PhoneGap project. On the index page, user will click on a series of thumbnails generated from a database, then the image ID chosen will be written to localstorage, the page will change, the image ID will be pulled from localstorage and the image displayed.

It works fine if I reference the image directly (not from the DB) this way (as a test):

<body onload="loaded()">

<div id='wrapper'><div id='scroller'>
    <ul><li><a id='output' href='index.html' onclick='returnTo()'></a></li></ul>
</div></div>

<script>
var newWP = document.createElement('img');
newWP.src = '0buggies/0118_buggies/wallpaper-18b2.jpg';
document.getElementById('output').appendChild(newWP);
</script>
</body>

I can pinch/zoom to resize the image for the screen (the main function my app requires), and scroll the image on the X and Y axis, then upon tapping the image, I will be returned to the index page. All of this works.

But if I pull the image out of a database and reference it the following way, all other aspects of the page code being the same, pinch/zoom does not work, though the picture is displayed and I can scroll on X and Y:

// ... DB code here ...
function querySuccess(tx, results) {
    var path = results.rows.item.category + 
        "/" + results.rows.item.subcat + 
        "/" + results.rows.item.filename_lg;
        document.getElementById("output").innerHTML = "<img src='" + path +
    "'>";
 } 

// ... more DB code here ...

<body onload="loaded()">

<div id='wrapper'>  <ul><li><a id='output' href='index.html'
onclick='returnTo()'></a></li></ul> </div>

How do I make iScroll5 work when the image is generated from a DB? I'm using the same CSS and iScroll JS on both pages. (iScroll4 has the same problem as iScroll 5 above.) I am using the SQLite DB plugin (from http://iphonedevlog.wordpress.com/2014/04/07/installing-chris-brodys-sqlite-database-with-cordova-cli-android/ which is my own site).

4

There are 4 answers

2
Kerri Shotts On

Try calling refresh on the scrollbar to get it to recognize the DOM change.

Best to wrap it in a 0-delay setTimeout, like so (Stolen from http://iscrolljs.com/#refresh) :

setTimeout(function () { myScroll.refresh(); }, 0);

If it takes time for the image to load, you'll want to wait until it's loaded entirely, unless you know the dimensions up-front.

3
Cubiq On

When dealing with images loaded dynamically things get a little more complicated. The reason is that the image dimensions are known to the browser only when the image itself has been fully loaded (and not when the img tag has been added to the DOM).

Your best bet is to explicitly declare the image width/height. You'd do this like so:

function querySuccess (results) {
    var path = results.rows.item.category + 
        "/" + results.rows.item.subcat + 
        "/" + results.rows.item.filename_lg;

    var img = document.createElement('img');

    img.width = 100;
    img.height = 100;
    img.src = path;

    document.getElementById('output').appendChild(img);

    // need to refresh iscroll in case the previous img was smaller/bigger than the new one
    iScrollInstance.refresh();
}

If width/height are unknown you could save the image dimensions into the database and retrieve them together with the image path.

function querySuccess (results) {
    var path = results.rows.item.category + 
        "/" + results.rows.item.subcat + 
        "/" + results.rows.item.filename_lg;

    var img = document.createElement('img');

    img.width = results.width;
    img.height = results.height;
    img.src = path;

    document.getElementById('output').appendChild(img);

    // need to refresh iscroll in case the previous img was smaller/bigger than the new one
    iScrollInstance.refresh();
}

If you can't evaluate the image dimensions in any way then you have to wait for the image to be fully loaded and at that point you can perform an iScroll.refresh(). Something like this:

function querySuccess (results) {
    var path = results.rows.item.category + 
        "/" + results.rows.item.subcat + 
        "/" + results.rows.item.filename_lg;

    var img = document.createElement('img');
    img.onload = function () {
        setTimeout(iScrollInstance.refresh.bind(iScrollInstance), 10); // give 10ms rest
    }
    img.onerror = function () {
        // you may want to deal with error404 or connection errors
    }
    img.src = path;

    document.getElementById('output').appendChild(img);
}
1
gro On

Why is the viewport user-scalable prop different on each sample? works=no, broken=yes Just an observation.

3
gro On

fwiw, here are a few things to look into:

  1. Uncomment the deviceReady addListener, as Cordova init really depends on this.
  2. Your loaded() method assigns myScroll a new iScroll, then explicitly calls onDeviceReady(), which then declares var myScroll; -- this seems inherently problematic - rework this.
  3. If 1 & 2 don't help, then I suggest moving queryDB(tx); from populateDB() to successCB() and commenting out the myScroll.refresh()

And just a note, I find that logging to console is less intrusive than using alerts when trying to track down a symptom that seems to be messing with events firing, or timing concerns.