PhoneGap app only running with PhoneGap Desktop and Mobile app

My AppStudio camera app, built with PhoneGap, always stops with Uncaught TypError: Cannot read property addeventlistener of null. Whatever I tried did not help. Today I opened the same app from the unzipped apk with PhoneGap Desktop and PhoneGap (Android) App. Runs perfectly. No addeventlistener error, takes photo and seems to save in persistent storage on Android device.
Some people say reason is that loading javascript before html is ready could be the reason. Tried some suggested solutions. None worked.
Any ideas why the app runs with PhoneGap Desktop and not with the same files from installed apk?

The suggestion that some people made about loading javascript before html is a possibility.

Do you have an addEventListener function in your code? When is it called?

Your error message looks like its caused by either the DOM or Cordova not being initialized (I can’t tell without code).

In general the flow is:

  1. load in your external .js files via the script tag
  2. use the script tag right after where you load the external ,js files to set two handlers:
    A) DOMContentLoaded
    B) deviceready
  3. don’t initialize anything until these two events fire.
    A) When the DOMContentLoaded fires, then and only then has all of the .js and html been loaded into the DOM tree and parsed. At this stage it’s considered usable.
    B) When the deviceready event fires, then and only then can you safely access any cordova plugin functions.

Lastly, I suggest that you load cordova.js after the following code.

<script>
window.addEventListener('DOMContentLoaded', (event) => {
    console.log('DOM fully loaded and parsed');
});
// onDeviceReady is a function
document.addEventListener("deviceready", onDeviceReady, false);
</script>

Thank you @Leader and @PPetree,
deviceready and DOMContentLoaded already in use. Possibly not working because the combination of these two handlers in my code is not efficient:

const ready = "cordova" in window ? "deviceready" : "DOMContentLoaded";
document.addEventListener(ready, app.init); 

I will try a few further combinations with your suggestions and tell the result.

There is a bit of a trick to it. Like I said, load your .js first but don’t initialize anything. Then set the event handlers and then load in cordova.js. (If you load it first, you could miss the device ready event).

This is the external app.js code (not mine, only adopted/adapted):

const app = {
  tempURL: null,
  permFolder: null,
  oldFile: null,
  permFile: null,
  KEY: "OLDfileNAMEkey",
   init: () => {
    setTimeout(function() {
      console.log("File system/plugin is ready");
      app.addListeners();
      //create the folder where we will save files
      app.getPermFolder();
    }, 2000);
  },
  addListeners: () => {
    document.getElementById("btnCam").addEventListener("click", app.takePic);
    document.getElementById("btnFile").addEventListener("click", app.copyImage);
    /* const el = document.getElementById("btnCam");
     el.addEventListener("click", app.takePic);
     const elf = document.getElementById("btnFile");
     elf.addEventListener("click", app.copyImage);*/
  },
  getPermFolder: () => {
    let path = cordova.file.dataDirectory;
    //save the reference to the folder as a global app property
    resolveLocalFileSystemURL(
      path,
      dirEntry => {
        //create the permanent folder
        dirEntry.getDirectory(
          "images",
          { create: true },
          permDir => {
            app.permFolder = permDir;
            console.log("Created or opened", permDir.toInternalURL());  // statt .nativeURL
            //check for an old image from last time app ran
            app.loadOldImage();
          },
          err => {
            console.warn("failed to create or open permanent image dir");
          }
        );
      },
      err => {
        console.warn("We should not be getting an error yet");
      }
    );
  },
  loadOldImage: () => {
    //check localstorage to see if there was an old file stored
    let oldFilePath = localStorage.getItem(app.KEY);
    //if there was an old file then load it into the imgFile
    if (oldFilePath) {
      resolveLocalFileSystemURL(
        oldFilePath,
        oldFileEntry => {
          app.oldFileEntry = oldFileEntry;
          let img = document.getElementById("imgFile");
          img.src = oldFileEntry.nativeURL;
        },
        err => {
          console.warn(err);
        }
      );
    }
  },
  takePic: ev => {
    ev.preventDefault();
    ev.stopPropagation();
    let options = {
      quality: 80,
      destinationType: Camera.DestinationType.FILE_URI,
      sourceType: Camera.PictureSourceType.CAMERA,
      allowEdit: true,
      encodingType: Camera.EncodingType.JPEG,
      mediaType: Camera.MediaType.PICTURE,
      targetWidth: 400,
      targetHeight: 400
    };
    console.log(options);
    navigator.camera.getPicture(app.gotImage, app.failImage, options);
  },
  gotImage: uri => {
    app.tempURL = uri;
    document.getElementById("imgCamera").src = uri;
  },
  failImage: err => {
    console.warn(err);
  },
  copyImage: ev => {
    ev.preventDefault();
    ev.stopPropagation();
    //copy the temp image to a permanent location
    let fileName = Date.now().toString() + ".jpg";

    resolveLocalFileSystemURL(
      app.tempURL,
      entry => {
        //we have a reference to the temp file now
        console.log(entry);
        console.log("copying", entry.name);
        console.log(
          "copy",
          entry.name,
          "to",
          app.permFolder.nativeURL + fileName
        );
        //copy the temp file to app.permFolder
        entry.copyTo(
          app.permFolder,
          fileName,
          permFile => {
            //the file has been copied
            //save file name in localstorage
            let path = permFile.nativeURL;
            localStorage.setItem(app.KEY, path);
            app.permFile = permFile;
            console.log(permFile);
            console.log("add", permFile.nativeURL, "to the 2nd image");
            document.getElementById("imgFile").src = permFile.nativeURL;
            //delete the old image file in the app.permFolder
            if (app.oldFile !== null) {
              app.oldFile.remove(
                () => {
                  console.log("successfully deleted old file");
                  //save the current file as the old file
                  app.oldFile = permFile;
                },
                err => {
                  console.warn("Delete failure", err);
                }
              );
            }
          },
          fileErr => {
            console.warn("Copy error", fileErr);
          }
        );
      },
      err => {
        console.error(err);
      }
    );
  }
};

// suggestion PPetree
// window.addEventListener('DOMContentLoaded', (event) => {
//    console.log('DOM fully loaded and parsed');
// });
// onDeviceReady is a function
// document.addEventListener("deviceready", onDeviceReady, false);

const ready = "cordova" in window ? "deviceready" : "DOMContentLoaded";
document.addEventListener(ready, app.init);

That’s all the camera manipulation code. Those event listeners are just watching for button clicks.

I don’t see anything that waits for the deviceready event and without that, you can’t run any of the other stuff.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no, viewport-fit=cover">
<title>sample</title>
<link rel="stylesheet" href="css/stylesheet.css">

<script src="js/jquery-3.4.1.min.js"></script>
<script src="js/jquery.touch.js"></script>

<script src="js/debug.js"></script>
<script src="js/appinfo.js"></script>
<script src="js/userinfo.js"></script>
<script src="js/indexdb.js"></script>

<script>
document.addEventListener("deviceready", onDeviceReady, false);
window.addEventListener(‘DOMContentLoaded’, (event) => {
  console.log(‘DOM fully loaded and parsed’);
  // we can now manipulate pages, elements, titles, buttons etc.
  // this doesn't mean all the css has been applied or that the page
  // is visible but everything is now parsed and loaded into the DOM
  $("#title").text("DOM is now ready to be used");
});

function onDeviceReady()
{
  // initialize the camera here
  // all that stuff in app.js should be put into a function
  // and that function called here.
}
</script>

<script src="cordova.js"></script>
</head>
<body>
<h2 id="title">DOM is not ready!</h2>
</body>
</html>

Thanks a lot. Convincing. Next morning …

I am so dumb that I get more errors than before.

  1. Apparently I do not understand how to edit the html directly within AppStudio. I guessed that I was meant to add the script tags with the html property of form1. If this indeed is the correct way, how can I get it that the script sections are inserted within the head tags and the h2 within body? I get errors and strange screen displays before I can see results in index.html when I open the app with Chrome and debugger.
  2. I thought I understood that cordova.js / phonegap.js were included in the apk file only after PhoneGap build and that I did not have to care about access to cordova.js by own code?? Or will the the script tag with cordova.js just use this file after PGBuild made it available?
    Sorry for these questions.

It’s a learning process, don’t beat yourself up.

cordova.js must be included in the head of your html but NOT in your www folder. Phonegap Build will automatically include the correct version in the apk. If you don’t include it in your head then PGB will still include it but none of the cordova functionality will be available to you.

I think @ghenne automatically includes it.

No luck yet. I changed a few double quotes / apostrophes that had caused errors here. As far as I can imagine this leaves the problem with where to put the cordova.js tag.

  1. ExtraHeaders: Error file not found
  2. Form1 HTML property: Error message and text “…join” appears, covering the upper part of the controls.
  3. App global properties Script property: Error message (as expected)
    PhoneGap built app shows splash screen, then white screen without any contents, just as before.
    I did put the app object from app.js original inside a function and call the function as suggested. Could it be that just calling the appfunc() function is not correct / sufficient? I cannot see that Chrome debugger goes inside (appfunc() and) app …

Got it working today, just with the original code. When I had everything fine, without error messages, the problem with a blank / empty display stayed. I only guess - did some more changes - that display finally showed the working controls was made possible by adding to CSP and to default-src: ‘unsafe-inline’.
Topic my be closed. Thanks again.

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.