{"id":2529,"date":"2015-02-23T13:00:02","date_gmt":"2015-02-23T11:00:02","guid":{"rendered":"http:\/\/www.webcodegeeks.com\/?p=2529"},"modified":"2018-06-22T13:18:43","modified_gmt":"2018-06-22T10:18:43","slug":"html5-offline-web-application-example","status":"publish","type":"post","link":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/","title":{"rendered":"HTML5 Offline Web Application Example"},"content":{"rendered":"<p>In this example we will talk about writing an Offline Web Application. Offline ? Sounds weird, doesn&#8217;t it ?<\/p>\n<p>HTML5 allows us to create a &#8220;list&#8221; of files accessible after the browser is disconnected.<\/p>\n<p>The browser will always have to download the main HTML file and all the files in this &#8220;list&#8221; at least once.<\/p>\n<p>Let&#8217;s see how this works.<br \/>\n&nbsp;<br \/>\n&nbsp;<br \/>\n&nbsp;<br \/>\n[ulp id=&#8217;tgm4cmEWcKUikZNn&#8217;]<\/p>\n<h2>1. The Manifest<\/h2>\n<p>The &#8220;list&#8221; is called the <em>manifest file<\/em>. This manifest is declared with an attribute of the <code>&lt;html&gt;<\/code> Tag like this:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&amp;lt;!DOCTYPE html&amp;gt;\r\n&amp;lt;html manifest=&amp;quot;cache.manifest&amp;quot;&amp;gt;\r\n&amp;lt;body&amp;gt;\r\n\r\n&amp;lt;\/body&amp;gt;\r\n&amp;lt;\/html&amp;gt;\r\n<\/pre>\n<p>The filename does not matter, but as a convention you may use names like : <em>manifest.appcache<\/em>, <em>myapplicaiton.manifest<\/em>, &#8230;<\/p>\n<p>The important thing is the <em>Content-Type<\/em> return by the server, it must be: <code>text\/cache-manifest<\/code>.<\/p>\n<blockquote><p>In Apache you will have to add a directive: <code>AddType text\/cache-manifest .manifest<\/code> to force the server to serve all <em>.manifest<\/em> file with the <code>text\/cache-manifest<\/code> <em>Content-Type<\/em>.<\/p><\/blockquote>\n<blockquote><p>In NGINX, you will have to edit the <code>mime.types<\/code> file and add the following line: <code>text\/cache.manifest\u00a0\u00a0\u00a0\u00a0\u00a0manifest;<\/code><\/p><\/blockquote>\n<blockquote><p>For IIS, yous will have to add the mime type. Have a look at the answer on this <a href=\"http:\/\/stackoverflow.com\/questions\/8159136\/application-cache-manifest\">StackOverflow<\/a>.<\/p><\/blockquote>\n<p>And its first line must be: <code>CACHE MANIFEST<\/code>. For example:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nCACHE MANIFEST\r\n# This is a comment\r\nindex.html\r\ncss\/style.css\r\njs\/script.js\r\n<\/pre>\n<p>This file will tell the browser to keep three files in cache: <em>index.html<\/em>, <em>style.css<\/em> and <em>script.js<\/em>.<\/p>\n<p>So now, if you are disconnected and you refresh your browser, you will always be able to use your web page.<\/p>\n<h2>2. Manifest Sections<\/h2>\n<p>We did not see it in the previous section, but the <em>manifest<\/em> file is structured with sections:<\/p>\n<ul>\n<li><code>CACHE<\/code>: Files listed in this section are explicitly cached after they are downloaded.<\/li>\n<li><code>NETWORK<\/code>: Files listed in this section are explicitly <strong>NOT<\/strong> cached.<br \/>\n<blockquote><p>&#8220;All requests to such resources bypass the cache, even if the user is offline&#8221;<\/p><\/blockquote>\n<\/li>\n<li><code>FALLBACK<\/code>: This section will specifies fallback pages the browser will use when a resource is not accessible. Entries in this section lines of mapping urls: URL_REQUESTED URL_FALLBACK.<\/li>\n<\/ul>\n<blockquote><p><strong>Note <\/strong>: All the files listed outside any sections are considered listed in the explicit <code>CACHE<\/code> section.<\/p><\/blockquote>\n<p>So let see a more complex <code>Manifest file<\/code>:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nCACHE MANIFEST\r\n# This is a comment\r\nCACHE:\r\nindex.html\r\ncss\/style.css\r\njs\/script.js\r\n\r\nNETWORK:\r\n# All requests to the Api need a network connection\r\n\/api\r\n\r\nFALLBACK:\r\nadd.html offline.html\r\n<\/pre>\n<p>This manifest file will caches 3 files, tell the browser to always try to download resources inside <em>\/api<\/em>, and if the <em>add.html<\/em> file is requested, the display the <em>offline.html<\/em> file.<\/p>\n<p>One side effect using application cache is that the browser won&#8217;t try to download updates cached files until the manifest file itself is updated &#8230;<\/p>\n<p>This can be very disturbing during development, or when files are frequently updated.<\/p>\n<p>To solve this problem we can add a <em>version number<\/em> in a comment line at the top of the <code>manifest<\/code> file to force the browser to update its cache.<\/p>\n<p>For example:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nCACHE MANIFEST\r\n# V1.0.1 &amp;lt;-- update the version to force the browser to update its cache.\r\n\r\n# This is a comment\r\nCACHE:\r\nindex.html\r\ncss\/style.css\r\njs\/script.js\r\n\r\nNETWORK:\r\n# All requests to the Api need a network connection\r\n\/api\r\n\r\nFALLBACK:\r\nadd.html offline.html\r\n<\/pre>\n<h2>3. Application Cache API<\/h2>\n<p>The HTML5 Application Cache comes with a JavaScript Api, that will allow us (developers) to interact with the cache.<\/p>\n<p>We can access the Application cache by the <code>window.applicationCache<\/code> object.<\/p>\n<h3>3.1 Events<\/h3>\n<p>Here are the events introduced by the Application Cache:<\/p>\n<ul>\n<li><code>checking<\/code>: Fired when the user agent is looking for updates, or is trying to download the manifest file for the first time. (This is always the first event in the sequence.)<\/li>\n<li><code>error<\/code>: The manifest file is not accessible (404), or any other errors when trying to downlod the manifest.<\/li>\n<li><code>noupdate<\/code>: The manifest did not change &#8230; nothing to update.<\/li>\n<li><code>downloading<\/code>: The manifest was found or updated, so the browser starts to download resources listed in it.<\/li>\n<li><code>progress<\/code>: Fired during download progress.<\/li>\n<li><code>updateready<\/code>: Fired when resources in the manifest have been redownloaded.<\/li>\n<li><code>cached<\/code>: Fired when resources have been cached. (Last event in sequence.)<\/li>\n<\/ul>\n<h3>3.2 Properties<\/h3>\n<p>The <code>window.applicationCache<\/code> object has only one property: <code>status<\/code><br \/>\nThis property returns the current status of the <code>applicationCache<\/code>.<br \/>\nThe value of the property is one of this constant:<\/p>\n<ul>\n<li><code>UNCACHED<\/code>: <strong>0<\/strong> A special value that indicates that an application cache object is not fully initialized.<\/li>\n<li><code>IDLE<\/code>: <strong>1<\/strong> The application cache is not currently in the process of being updated.<\/li>\n<li><code>CHECKING<\/code>: <strong>2<\/strong> The manifest is being fetched and checked for updates.<\/li>\n<li><code>DOWNLOADING<\/code>: <strong>3<\/strong> Resources are being downloaded to be added to the cache, due to a changed resource manifest.<\/li>\n<li><code>UPDATEREADY<\/code>: <strong>4<\/strong> There is a new version of the application cache available. There is a corresponding updateready event, which is fired instead of the cached event when a new update has been downloaded but not yet activated using the swapCache() method.<\/li>\n<li><code>OBSOLETE<\/code>: <strong>5<\/strong> The application cache group is now obsolete.<\/li>\n<\/ul>\n<h3>3.3 Methods<\/h3>\n<p>Here are the methods of the <code>window.applicationCache<\/code> object.<\/p>\n<ul>\n<li><code>update()<\/code>: Invoke the cache download process, not really necessary, the browser, will generally take care of updating the cache.<\/li>\n<li><code>abort()<\/code>: Cancel the cache download process.<\/li>\n<li><code>swapCache()<\/code>: Switches to the most recent application cache, if there is a newer one. If there isn&#8217;t, throws an InvalidStateError exception.<\/li>\n<\/ul>\n<h2>4. The online \/ offline events<\/h2>\n<p>The API also add two events for Online status detection. These events are: <code>online<\/code> and <code>offline<\/code>.<\/p>\n<p>Let see a simple example:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nwindow.addEvenListener('online', function(){\r\n    console.log('online');\r\n}, false)\r\nwindow.addEvenListener('offline', function(){\r\n    console.log('offline');\r\n}, false)\r\n<\/pre>\n<p>Now we know the necessary for a &#8220;real world&#8221; example &#8230;<\/p>\n<h2>5. A Working Example<\/h2>\n<p>As an example we can imagine a simple web application, to manage your <em>todo list<\/em>.<br \/>\nYou must not be able to add a task when you&#8217;re offline, we will do that by disabling submit button.<\/p>\n<blockquote><p><strong>NOTE<\/strong>: The example will use the <a href=\"http:\/\/zeptojs.com\/\" target=\"_blank\" rel=\"noopener\">Zepto.js<\/a> library to facilitate, the ajax calls and DOM manipulation..<\/p><\/blockquote>\n<h3>5.1. Project structure<\/h3>\n<p>So the project structure will be:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n \/\r\n --\/css\/\r\n --\/css\/style.css\r\n --\/js\/\r\n --\/js\/script.js\r\n --\/server\/server.php\r\n --\/server\/tasks.json\r\n --\/index.html\r\n<\/pre>\n<p>The goal is to write an application that will allow you to <em>add<\/em> \/ <em>remove<\/em> \/ <em>edit<\/em> tasks, tasks will be saved in a static json file (<code>tasks.json<\/code>) on the server (simulated here by the <code>server.php<\/code> file).<\/p>\n<h3>5.2 The server.php file<\/h3>\n<blockquote><p>As I said this will <em>simulate<\/em> a server, so <strong>do not use this file in production<\/strong>, this is only for testing purpose.<\/p><\/blockquote>\n<pre class=\"brush: php; title: ; notranslate\" title=\"\">\r\n\/\/ We we only manage POST Requests\r\nif ($_SERVER&#x5B;'REQUEST_METHOD'] === 'POST') {\r\n    $message = array();\r\n    $file  = dirname(__FILE__).&amp;quot;\/tasks.json&amp;quot;;\r\n    $content = file_get_contents($file);\r\n    $tasks = json_decode($content, true);\r\n    switch($_POST&#x5B;'action']){\r\n        case &amp;quot;list&amp;quot;:\r\n            \/\/ create the response\r\n            $message&#x5B;&amp;quot;type&amp;quot;] = &amp;quot;OK&amp;quot;;\r\n            $message&#x5B;&amp;quot;data&amp;quot;] = $tasks;\r\n            break;\r\n        case &amp;quot;add&amp;quot;:\r\n            \/\/ create the task\r\n            $task = array('id'=&amp;gt; uniqid(), &amp;quot;task&amp;quot;=&amp;gt;$_POST&#x5B;&amp;quot;task&amp;quot;]);\r\n            \/\/ push it to the array pf tasks\r\n            array_push($tasks, $task);\r\n\r\n            \/\/ save the file :\r\n            file_put_contents($file, json_encode($tasks));\r\n\r\n            \/\/ create the response\r\n            $message&#x5B;&amp;quot;type&amp;quot;] = &amp;quot;OK&amp;quot;;\r\n            $message&#x5B;&amp;quot;data&amp;quot;] = $task;\r\n\r\n            break;\r\n        case &amp;quot;remove&amp;quot;:\r\n            \/\/ Get the task id\r\n            $id = $_POST&#x5B;&amp;quot;id&amp;quot;];\r\n            $tmpTasks = array();\r\n            $found = false;\r\n            foreach($tasks as $task){\r\n                if( $task&#x5B;&amp;quot;id&amp;quot;] !== $id ){\r\n                    array_push($tmpTasks, $task);\r\n                } else {\r\n                    $found = true;\r\n                }\r\n            }\r\n            if( $found ){\r\n                \/\/ Ok task was deleted\r\n                $message&#x5B;&amp;quot;type&amp;quot;] = &amp;quot;OK&amp;quot;;\r\n                $message&#x5B;&amp;quot;data&amp;quot;] = $id;\r\n                file_put_contents($file, json_encode($tmpTasks));\r\n            } else {\r\n                $message&#x5B;&amp;quot;type&amp;quot;] = &amp;quot;KO&amp;quot;;\r\n                $message&#x5B;'message'] = &amp;quot;Task No Found&amp;quot;;\r\n            }\r\n            break;\r\n        case &amp;quot;update&amp;quot;:\r\n        case &amp;quot;edit&amp;quot;:\r\n            \/\/ Get the task id\r\n            $id = $_POST&#x5B;&amp;quot;id&amp;quot;];\r\n            $taskPosted = $_POST&#x5B;&amp;quot;task&amp;quot;];\r\n            $tmpTasks = array();\r\n            $found = false;\r\n            foreach($tasks as $task){\r\n                if( $task&#x5B;&amp;quot;id&amp;quot;] === $id ){\r\n                    $task&#x5B;&amp;quot;id&amp;quot;] = $id;\r\n                    $task&#x5B;&amp;quot;task&amp;quot;] = $taskPosted;\r\n                    $found = true;\r\n                }\r\n                array_push($tmpTasks, $task);\r\n            }\r\n            if( $found ){\r\n                \/\/ Ok task was deleted\r\n                $message&#x5B;&amp;quot;type&amp;quot;] = &amp;quot;OK&amp;quot;;\r\n                $message&#x5B;&amp;quot;data&amp;quot;] = array('id'=&amp;gt;$id, 'task'=&amp;gt;$taskPosted);\r\n                file_put_contents($file, json_encode($tmpTasks));\r\n            } else {\r\n                $message&#x5B;&amp;quot;type&amp;quot;] = &amp;quot;KO&amp;quot;;\r\n                $message&#x5B;'message'] = &amp;quot;Task No Found&amp;quot;;\r\n            }\r\n            break;\r\n        case 'reset':\r\n            \/\/ save the file :\r\n\r\n            file_put_contents($file, json_encode(array()));\r\n            $message&#x5B;&amp;quot;type&amp;quot;] = &amp;quot;OK&amp;quot;;\r\n            $message&#x5B;&amp;quot;data&amp;quot;] = &amp;quot;&amp;quot;;\r\n            break;\r\n        default:\r\n            $message = array(\r\n                &amp;quot;type&amp;quot;=&amp;gt;&amp;quot;ERR&amp;quot;,\r\n                &amp;quot;message&amp;quot; =&amp;gt;&amp;quot;Action unknown : &amp;quot;.$_POST&#x5B;&amp;quot;action&amp;quot;].&amp;quot;.&amp;quot;\r\n            );\r\n            break;\r\n    }\r\n    \/\/ Print the result\r\n    echo json_encode($message);\r\n} else {\r\n    $message = array(\r\n        &amp;quot;type&amp;quot;=&amp;gt;&amp;quot;ERR&amp;quot;,\r\n        &amp;quot;message&amp;quot; =&amp;gt;&amp;quot;Request should be post, nothing to do ...&amp;quot;\r\n    );\r\n    echo json_encode($message);\r\n}\r\n<\/pre>\n<p>This little php script allow you to use some <em>actions<\/em> to manage the task list, some of the <em>actions<\/em> needs some extra post datas, here is the API definition:<\/p>\n<table border=\"1\" width=\"100%\">\n<thead>\n<tr>\n<th width=\"10%\">action<\/th>\n<th width=\"20%\">parameters<\/th>\n<th width=\"70%\">description<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td><code>list<\/code><\/td>\n<td>none<\/td>\n<td>Get the full list of tasks.<\/td>\n<\/tr>\n<tr>\n<td><code>add<\/code><\/td>\n<td>task : STRING<\/td>\n<td>Add the task, will generate a unique id, and return a task :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n            {\r\n                &amp;quot;type&amp;quot;:&amp;quot;OK&amp;quot;,\r\n                &amp;quot;data&amp;quot;:{\r\n                    &amp;quot;id&amp;quot;:&amp;quot;54e390fbc8823&amp;quot;,\r\n                    &amp;quot;task&amp;quot;:&amp;quot;rere&amp;quot;\r\n                }\r\n            }\r\n            <\/pre>\n<\/td>\n<\/tr>\n<tr>\n<td><code>remove<\/code><\/td>\n<td>id : INT<\/td>\n<td>Remove the task identified by the ID, will return the id as message, or an error message :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n            {\r\n                &amp;quot;type&amp;quot;:&amp;quot;OK&amp;quot;,\r\n                &amp;quot;data&amp;quot;:&amp;quot;54e390fbc8823&amp;quot;\r\n            }\r\n            <\/pre>\n<p>or<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n            {\r\n                &amp;quot;type&amp;quot;:&amp;quot;KO&amp;quot;,\r\n                &amp;quot;message&amp;quot;:&amp;quot;Task No Found&amp;quot;\r\n            }\r\n            <\/pre>\n<\/td>\n<\/tr>\n<tr>\n<td><code>update<\/code> or <code>edit<\/code><\/td>\n<td>id : INT<br \/>\ntask : STRING<\/td>\n<td>Update the task identified by the ID with the Task sent, will return the id as message, or an error message :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n            {\r\n                &amp;quot;type&amp;quot;:&amp;quot;OK&amp;quot;,\r\n                &amp;quot;data&amp;quot;:&amp;quot;54e390fbc8823&amp;quot;\r\n            }\r\n            <\/pre>\n<p>or<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n            {\r\n                &amp;quot;type&amp;quot;:&amp;quot;KO&amp;quot;,\r\n                &amp;quot;message&amp;quot;:&amp;quot;Task No Found&amp;quot;\r\n            }\r\n            <\/pre>\n<\/td>\n<\/tr>\n<tr>\n<td><code>reset<\/code><\/td>\n<td>none<\/td>\n<td>Reset the task list (empty list), return an empty message :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n            {\r\n                &amp;quot;type&amp;quot;:&amp;quot;OK&amp;quot;,\r\n                &amp;quot;message&amp;quot;:&amp;quot;&amp;quot;\r\n            }\r\n            <\/pre>\n<\/td>\n<\/tr>\n<tr>\n<td><code>default<\/code><\/td>\n<td>none<\/td>\n<td>Should not append, return an error message :<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n            {\r\n                &amp;quot;type&amp;quot;:&amp;quot;KO&amp;quot;,\r\n                &amp;quot;message&amp;quot;:&amp;quot;Task No Found&amp;quot;\r\n            }\r\n            <\/pre>\n<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h3>5.3 The main HTML file<\/h3>\n<p>Here is the <code>index.html<\/code> source file:<\/p>\n<pre class=\"brush: xml; title: ; notranslate\" title=\"\">\r\n&amp;lt;!DOCTYPE html&amp;gt;\r\n&amp;lt;html manifest=&amp;quot;app.manifest&amp;quot;&amp;gt;\r\n&amp;lt;head lang=&amp;quot;en&amp;quot;&amp;gt;\r\n    &amp;lt;meta charset=&amp;quot;UTF-8&amp;quot;&amp;gt;\r\n    &amp;lt;title&amp;gt;Task List Example&amp;lt;\/title&amp;gt;\r\n    &amp;lt;link href=&amp;quot;css\/style.css&amp;quot; rel=&amp;quot;stylesheet&amp;quot; \/&amp;gt;\r\n&amp;lt;\/head&amp;gt;\r\n&amp;lt;body&amp;gt;\r\n&amp;lt;div id=&amp;quot;wrapper&amp;quot;&amp;gt;\r\n    &amp;lt;form id=&amp;quot;taskform&amp;quot;&amp;gt;\r\n        &amp;lt;input type=&amp;quot;hidden&amp;quot; id=&amp;quot;task_id&amp;quot; value=&amp;quot;-1&amp;quot; \/&amp;gt;\r\n        &amp;lt;input type=&amp;quot;text&amp;quot; id=&amp;quot;task&amp;quot; placeholder=&amp;quot;Add a Task ...&amp;quot; \/&amp;gt;\r\n        &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Add&amp;quot; \/&amp;gt;\r\n    &amp;lt;\/form&amp;gt;\r\n    &amp;lt;ul id=&amp;quot;tasklist&amp;quot;&amp;gt;&amp;lt;\/ul&amp;gt;\r\n    &amp;lt;button id=&amp;quot;reset&amp;quot;&amp;gt;Clear All Tasks&amp;lt;\/button&amp;gt;\r\n&amp;lt;\/div&amp;gt;\r\n\r\n&amp;lt;script src=&amp;quot;js\/zepto.min.js&amp;quot;&amp;gt;&amp;lt;\/script&amp;gt;\r\n&amp;lt;script src=&amp;quot;js\/script.js&amp;quot;&amp;gt;&amp;lt;\/script&amp;gt;\r\n&amp;lt;\/body&amp;gt;\r\n&amp;lt;\/html&amp;gt;\r\n<\/pre>\n<p>Here is the <code>app.manifest<\/code> file:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\nCACHE MANIFEST\r\n# version 1\r\n# This is a comment\r\nCACHE:\r\nindex.html\r\ncss\/style.css\r\njs\/script.js\r\njs\/zepto.min.js\r\n\r\nNETWORK:\r\nserver\/server.php\r\n\r\n<\/pre>\n<h3>5.4 The JavaScript<\/h3>\n<p>I won&#8217;t show the full JS file as you can download it, I will only explain some parts of the code.<\/p>\n<p>First of all we need to detect if the browser is <strong>onLine<\/strong> or <strong>offLine<\/strong>, as the whole code is cached (via the app.manifest file), the application can be opened without being onLine (don&#8217;t need to do the first request if it was done once before).<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nif(navigator.onLine){\r\n    \/\/ browser is online so I can load the list from the server\r\n    server.list(init);\r\n    \/\/ set the connection status to true\r\n    setConnected(true);\r\n} else {\r\n    \/\/ Not online, set the connection status to false\r\n    setConnected(false);\r\n}\r\n<\/pre>\n<p>Here is the function <code>setConnected<\/code><\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\nfunction setConnected(isConnected){\r\n    var buttons = 'input&#x5B;type=&amp;quot;submit&amp;quot;], button';\r\n    if( isConnected ){\r\n        $(buttons).removeAttr('disabled', 'disabled');\r\n    } else {\r\n        $(buttons).attr('disabled', 'disabled');\r\n    }\r\n}\r\n<\/pre>\n<p>This function only enable or disable the buttons.<\/p>\n<p>Now we need to listen to the <code>online<\/code> and <code>offline<\/code> events to manage the connection status:<\/p>\n<pre class=\"brush: jscript; title: ; notranslate\" title=\"\">\r\n$(window).bind('online', function(){\r\n    \/\/ If the list was never loaded\r\n    if( ! loaded ){\r\n        \/\/ we load the list from the server\r\n        server.list(init);\r\n    }\r\n    \/\/ set the connection status to true\r\n    setConnected(true);\r\n});\r\n$(window).bind('offline', function(){\r\n    \/\/ set the connection status to false\r\n    setConnected(false);\r\n});\r\n<\/pre>\n<p>I think the above code, is documented enough to understand it.<\/p>\n<p>Here is what the application looks like in &#8220;online&#8221; mode:<\/p>\n<figure id=\"attachment_2535\" aria-describedby=\"caption-attachment-2535\" style=\"width: 785px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/02\/online.png\"><img decoding=\"async\" class=\"wp-image-2535 size-full\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/02\/online.png\" alt=\"HTML5 Offline Web Application Example OnLine\" width=\"785\" height=\"526\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/02\/online.png 785w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/02\/online-300x201.png 300w\" sizes=\"(max-width: 785px) 100vw, 785px\" \/><\/a><figcaption id=\"caption-attachment-2535\" class=\"wp-caption-text\">HTML5 Offline Web Application Example OnLine<\/figcaption><\/figure>\n<p>Here is what the application looks like in &#8220;offline&#8221; mode:<\/p>\n<figure id=\"attachment_2534\" aria-describedby=\"caption-attachment-2534\" style=\"width: 781px\" class=\"wp-caption aligncenter\"><a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/02\/offline.png\"><img decoding=\"async\" class=\"size-full wp-image-2534\" src=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/02\/offline.png\" alt=\"HTML5 Offline Web Application Example OffLine\" width=\"781\" height=\"524\" srcset=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/02\/offline.png 781w, https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/02\/offline-300x201.png 300w\" sizes=\"(max-width: 781px) 100vw, 781px\" \/><\/a><figcaption id=\"caption-attachment-2534\" class=\"wp-caption-text\">HTML5 Offline Web Application Example OffLine<\/figcaption><\/figure>\n<h2>6. Download<\/h2>\n<div class=\"download\"><strong>Download<\/strong><br \/>\nYou can download the full source code of this example here: <a href=\"http:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2015\/02\/html5_offline_web_application_example.zip\"><strong>HTML5 Offline Web Application Example<\/strong><\/a><\/div>\n","protected":false},"excerpt":{"rendered":"<p>In this example we will talk about writing an Offline Web Application. Offline ? Sounds weird, doesn&#8217;t it ? HTML5 allows us to create a &#8220;list&#8221; of files accessible after the browser is disconnected. The browser will always have to download the main HTML file and all the files in this &#8220;list&#8221; at least once. &hellip;<\/p>\n","protected":false},"author":46,"featured_media":914,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[12],"tags":[107,103],"class_list":["post-2529","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-html5","tag-cache","tag-offline"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>HTML5 Offline Web Application Example - Web Code Geeks - 2026<\/title>\n<meta name=\"description\" content=\"Interested to learn about Offline Web Application in HTML5? Check out our Example on how to create a &quot;list&quot; of files accessible after the browser is offline\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"HTML5 Offline Web Application Example - Web Code Geeks - 2026\" \/>\n<meta property=\"og:description\" content=\"Interested to learn about Offline Web Application in HTML5? Check out our Example on how to create a &quot;list&quot; of files accessible after the browser is offline\" \/>\n<meta property=\"og:url\" content=\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/\" \/>\n<meta property=\"og:site_name\" content=\"Web Code Geeks\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/webcodegeeks\" \/>\n<meta property=\"article:published_time\" content=\"2015-02-23T11:00:02+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2018-06-22T10:18:43+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg\" \/>\n\t<meta property=\"og:image:width\" content=\"150\" \/>\n\t<meta property=\"og:image:height\" content=\"150\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"Remi Goyard\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@mimiz33\" \/>\n<meta name=\"twitter:site\" content=\"@webcodegeeks\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Remi Goyard\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"12 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/\"},\"author\":{\"name\":\"Remi Goyard\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/ae15332f888fcdc376a8d4e03180e242\"},\"headline\":\"HTML5 Offline Web Application Example\",\"datePublished\":\"2015-02-23T11:00:02+00:00\",\"dateModified\":\"2018-06-22T10:18:43+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/\"},\"wordCount\":2420,\"commentCount\":1,\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg\",\"keywords\":[\"cache\",\"Offline\"],\"articleSection\":[\"HTML5\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/\",\"name\":\"HTML5 Offline Web Application Example - Web Code Geeks - 2026\",\"isPartOf\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg\",\"datePublished\":\"2015-02-23T11:00:02+00:00\",\"dateModified\":\"2018-06-22T10:18:43+00:00\",\"description\":\"Interested to learn about Offline Web Application in HTML5? Check out our Example on how to create a \\\"list\\\" of files accessible after the browser is offline\",\"breadcrumb\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#primaryimage\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg\",\"width\":150,\"height\":150},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/www.webcodegeeks.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"HTML5\",\"item\":\"https:\/\/www.webcodegeeks.com\/category\/html5\/\"},{\"@type\":\"ListItem\",\"position\":3,\"name\":\"HTML5 Offline Web Application Example\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#website\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"name\":\"Web Code Geeks\",\"description\":\"Web Developers Resource Center\",\"publisher\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/www.webcodegeeks.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#organization\",\"name\":\"Exelixis Media P.C.\",\"url\":\"https:\/\/www.webcodegeeks.com\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"contentUrl\":\"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png\",\"width\":864,\"height\":246,\"caption\":\"Exelixis Media P.C.\"},\"image\":{\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/\"},\"sameAs\":[\"https:\/\/www.facebook.com\/webcodegeeks\",\"https:\/\/x.com\/webcodegeeks\"]},{\"@type\":\"Person\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/ae15332f888fcdc376a8d4e03180e242\",\"name\":\"Remi Goyard\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/caa839a88ba9d485eec370ff8e020c314f10fb2e4ddedb3424a4bf92198259b2?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/caa839a88ba9d485eec370ff8e020c314f10fb2e4ddedb3424a4bf92198259b2?s=96&d=mm&r=g\",\"caption\":\"Remi Goyard\"},\"description\":\"I'm a senior web architect. Passionated by new technologies, programming, devops tools, and agile methodologies. I really enjoy to coach, or teach, people who wants to learn programming, from beginners to skilled programmers. Involved in local developer communities, Java User Group, PHP User Group, or Javascript User Group, i like to share with others about experiences, news or business. Coding is FUN !\",\"sameAs\":[\"http:\/\/www.mimiz.fr\/\",\"http:\/\/fr.linkedin.com\/in\/remigoyard\/en\",\"https:\/\/x.com\/@mimiz33\"],\"url\":\"https:\/\/www.webcodegeeks.com\/author\/remi-goyard\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"HTML5 Offline Web Application Example - Web Code Geeks - 2026","description":"Interested to learn about Offline Web Application in HTML5? Check out our Example on how to create a \"list\" of files accessible after the browser is offline","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/","og_locale":"en_US","og_type":"article","og_title":"HTML5 Offline Web Application Example - Web Code Geeks - 2026","og_description":"Interested to learn about Offline Web Application in HTML5? Check out our Example on how to create a \"list\" of files accessible after the browser is offline","og_url":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/","og_site_name":"Web Code Geeks","article_publisher":"https:\/\/www.facebook.com\/webcodegeeks","article_published_time":"2015-02-23T11:00:02+00:00","article_modified_time":"2018-06-22T10:18:43+00:00","og_image":[{"width":150,"height":150,"url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg","type":"image\/jpeg"}],"author":"Remi Goyard","twitter_card":"summary_large_image","twitter_creator":"@mimiz33","twitter_site":"@webcodegeeks","twitter_misc":{"Written by":"Remi Goyard","Est. reading time":"12 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#article","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/"},"author":{"name":"Remi Goyard","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/ae15332f888fcdc376a8d4e03180e242"},"headline":"HTML5 Offline Web Application Example","datePublished":"2015-02-23T11:00:02+00:00","dateModified":"2018-06-22T10:18:43+00:00","mainEntityOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/"},"wordCount":2420,"commentCount":1,"publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg","keywords":["cache","Offline"],"articleSection":["HTML5"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/","url":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/","name":"HTML5 Offline Web Application Example - Web Code Geeks - 2026","isPartOf":{"@id":"https:\/\/www.webcodegeeks.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#primaryimage"},"image":{"@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#primaryimage"},"thumbnailUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg","datePublished":"2015-02-23T11:00:02+00:00","dateModified":"2018-06-22T10:18:43+00:00","description":"Interested to learn about Offline Web Application in HTML5? Check out our Example on how to create a \"list\" of files accessible after the browser is offline","breadcrumb":{"@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#primaryimage","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2014\/10\/html5-logo.jpg","width":150,"height":150},{"@type":"BreadcrumbList","@id":"https:\/\/www.webcodegeeks.com\/html5\/html5-offline-web-application-example\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/www.webcodegeeks.com\/"},{"@type":"ListItem","position":2,"name":"HTML5","item":"https:\/\/www.webcodegeeks.com\/category\/html5\/"},{"@type":"ListItem","position":3,"name":"HTML5 Offline Web Application Example"}]},{"@type":"WebSite","@id":"https:\/\/www.webcodegeeks.com\/#website","url":"https:\/\/www.webcodegeeks.com\/","name":"Web Code Geeks","description":"Web Developers Resource Center","publisher":{"@id":"https:\/\/www.webcodegeeks.com\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/www.webcodegeeks.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/www.webcodegeeks.com\/#organization","name":"Exelixis Media P.C.","url":"https:\/\/www.webcodegeeks.com\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/","url":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","contentUrl":"https:\/\/www.webcodegeeks.com\/wp-content\/uploads\/2022\/06\/exelixis-logo.png","width":864,"height":246,"caption":"Exelixis Media P.C."},"image":{"@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/logo\/image\/"},"sameAs":["https:\/\/www.facebook.com\/webcodegeeks","https:\/\/x.com\/webcodegeeks"]},{"@type":"Person","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/ae15332f888fcdc376a8d4e03180e242","name":"Remi Goyard","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/www.webcodegeeks.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/caa839a88ba9d485eec370ff8e020c314f10fb2e4ddedb3424a4bf92198259b2?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/caa839a88ba9d485eec370ff8e020c314f10fb2e4ddedb3424a4bf92198259b2?s=96&d=mm&r=g","caption":"Remi Goyard"},"description":"I'm a senior web architect. Passionated by new technologies, programming, devops tools, and agile methodologies. I really enjoy to coach, or teach, people who wants to learn programming, from beginners to skilled programmers. Involved in local developer communities, Java User Group, PHP User Group, or Javascript User Group, i like to share with others about experiences, news or business. Coding is FUN !","sameAs":["http:\/\/www.mimiz.fr\/","http:\/\/fr.linkedin.com\/in\/remigoyard\/en","https:\/\/x.com\/@mimiz33"],"url":"https:\/\/www.webcodegeeks.com\/author\/remi-goyard\/"}]}},"_links":{"self":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/2529","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/users\/46"}],"replies":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/comments?post=2529"}],"version-history":[{"count":0,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/posts\/2529\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media\/914"}],"wp:attachment":[{"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/media?parent=2529"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/categories?post=2529"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.webcodegeeks.com\/wp-json\/wp\/v2\/tags?post=2529"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}