The deviceready Event
If there is one concept in this entire track worth over-explaining, it's deviceready.
Nearly every confusing "why doesn't my plugin work" bug a Cordova beginner hits traces back to
calling a native API before Cordova has finished setting itself up. This page goes deep on why
the event exists, the correct pattern for using it, what breaks if you skip it, and the related
events for handling your app moving to the background and back.
Why deviceready Exists
When a Cordova app launches, the native shell starts a WebView and points it at your
www/index.html — but the WebView loading and rendering your HTML/CSS/JS is only
half the story. Underneath, Cordova also needs to initialize its native bridge
(the communication channel between your JavaScript and native code) and let every installed
plugin finish its own startup work before any plugin API can be safely called. That
initialization takes a small but nonzero amount of time after the page begins loading. The
deviceready event is Cordova's way of telling your JavaScript, unambiguously,
"the bridge and all plugins are now ready — it's safe to call native APIs starting now."
The regular browser event you might reach for instead, window.onload (or jQuery's
$(document).ready()), only tells you that the DOM and its referenced assets have
finished loading — it says nothing about whether Cordova's native bridge is initialized. Those
two things usually finish at different times, which is exactly why Cordova ships its own,
separate event instead of overloading the standard page-load events.
The Correct Pattern
The standard, correct way to wait for Cordova is to attach a listener for
deviceready and put all of your Cordova/plugin-dependent startup code inside the
handler function:
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
// Safe to call Cordova plugin APIs starting here
console.log('Running cordova-' + device.cordova);
console.log('Platform: ' + device.platform + ', model: ' + device.model);
navigator.geolocation.getCurrentPosition(function(position) {
console.log('Lat: ' + position.coords.latitude);
});
}
The third argument to addEventListener, false, is the standard
"useCapture" parameter from the DOM event API — you'll almost always see it passed as
false in Cordova examples, matching normal JavaScript event-listener conventions.
The Wrong Way — and Why It Fails
Compare that to code that calls a plugin API immediately, at the top level of a script, with no event listener at all:
// WRONG: no deviceready listener — this runs immediately on page load
console.log('Platform: ' + device.platform);
navigator.geolocation.getCurrentPosition(function(position) {
console.log(position.coords.latitude);
});
This script runs the instant the browser parses it — which, in a Cordova app, can easily be
before the native bridge has finished initializing. The result is typically one of a
few failure modes: device is undefined (throwing a "Cannot read
property 'platform' of undefined" error), navigator.geolocation silently falls
back to a plain browser geolocation implementation that doesn't match Cordova's plugin behavior,
or the call simply does nothing because the plugin's native side hasn't registered its message
handler yet. These bugs are especially frustrating because they can appear to work sometimes
(on a fast device, if the bridge happens to initialize quickly) and fail other times — a classic
race condition.
What's Safe Before deviceready vs. What Must Wait
| Safe Before deviceready | Must Wait for deviceready |
|---|---|
|
|
The simple rule of thumb: if the API you're calling is part of plain JavaScript or the DOM,
it's safe any time. If it's provided by a Cordova plugin — which almost always means it
hangs off device, navigator (for plugin-provided extensions like
geolocation and camera), or a global object a plugin registers — it must wait for
deviceready.
deviceready Only Fires Once
deviceready fires exactly one time per app load/launch — it marks the one-time
transition from "still initializing" to "fully ready," not a repeating readiness check. If you
need code to run every time the user returns to your app (rather than only on the very first
launch), deviceready is the wrong event to reach for; that's what the pause and
resume events below are for.
Related Lifecycle Events: pause and resume
Two related events let your app react to the user switching away from it and back, which is common on mobile — the user might get a phone call, switch to another app, or lock the screen:
document.addEventListener('pause', onPause, false);
document.addEventListener('resume', onResume, false);
function onPause() {
// App is moving to the background — good place to pause timers, save state
}
function onResume() {
// App is coming back to the foreground
}
pause fires when the app is sent to the background (for example, the user presses
the home button), and resume fires when it comes back to the foreground. These are
commonly used to pause things like media playback, timers, or animations while the app is
hidden, and to refresh time-sensitive data (like re-checking geolocation) when the user returns.