{"id":579,"date":"2017-11-05T22:19:34","date_gmt":"2017-11-05T22:19:34","guid":{"rendered":"http:\/\/www.netexl.com\/blog\/?p=579"},"modified":"2026-04-02T10:17:42","modified_gmt":"2026-04-02T10:17:42","slug":"responsive-bootstrap-and-jquery-game-template-for-mobile","status":"publish","type":"post","link":"https:\/\/www.netexl.com\/blog\/responsive-bootstrap-and-jquery-game-template-for-mobile\/","title":{"rendered":"Responsive Bootstrap and JQuery Game Template for Mobile"},"content":{"rendered":"<p>For mobile web app templates, <a href=\"https:\/\/jquerymobile.com\/\" target=\"_blank\">Jquery Mobile<\/a> has been a popular choice but it comes with its own set of issues when we have multiple pages in the game since it makes ajax calls to load the pages and if there are specific things which are required to be handled on each page then it becomes even more complex to handle those scenarios. The alternate is to have a simple Bootstrap and JQuery based template with multiple pages which is simple and works just like a website. The problem here is that the pages are loaded again and again whenever we navigate from one page to another. This is not an ideal scenario\u00a0for mobile web apps\u00a0since all external libraries are required to be initialized on all pages which\u00a0is not required\u00a0and unnecessary work for processor. For example,\u00a0if you compiled your web app using PhoneGap and\u00a0are using a plugin to play audio. You need to initialized the audio plugin on all pages wherever you\u00a0need to\u00a0use the plugin. In ideal scenario, the plugin should only be initialized once when app is first opened and should be available on all pages of your app\u00a0which is why the preferred approach on mobile is to have single HTML file.<\/p>\n<div style=\"height:250px;\"><script async src=\"\/\/pagead2.googlesyndication.com\/pagead\/js\/adsbygoogle.js\"><\/script>\r\n<ins class=\"adsbygoogle\"\r\n     style=\"display:inline-block;width:300px;height:250px\"\r\n     data-ad-client=\"ca-pub-0810934084049672\"\r\n     data-ad-slot=\"8927903874\"><\/ins>\r\n<script>\r\n(adsbygoogle = window.adsbygoogle || []).push({});\r\n<\/script><\/div>\n<p>In this alternate approach\u00a0we use a single page and show\/hide the sections where each section contains a page. This is pretty much how JQuery Mobile works but in a more elementary way\u00a0giving you full control over everything you need to handle.<\/p>\n<p>First we will\u00a0define and initialize\u00a0 a helper class to manage storage to keep track of config changes and other game related data using browser\/mobile cache<\/p>\n<pre class=\"lang:default decode:true\">function LocalStorageManager() {\r\n    this.gameConfigKey = \"my-gameConfig\";\r\n    this.gameParamsKey = \"my-gameParams\";\r\n    var supported = this.localStorageSupported();\r\n    this.storage = supported ? window.localStorage : window.fakeStorage;\r\n}\r\n\r\nLocalStorageManager.prototype = {\r\n    localStorageSupported: function () {\r\n        var testKey = \"test\";\r\n        try {\r\n            var storage = window.localStorage;\r\n            storage.setItem(testKey, \"1\");\r\n            storage.removeItem(testKey);\r\n            return true;\r\n        } catch (error) {\r\n            return false;\r\n        }\r\n    },\r\n    getGameParams: function () {\r\n        var paramsJSON = this.storage.getItem(this.gameParamsKey);\r\n        return paramsJSON ? JSON.parse(paramsJSON) : { };\r\n    },\r\n    setGameParams: function (gameParams) {\r\n        this.storage.setItem(this.gameParamsKey, JSON.stringify(gameParams));\r\n    },\r\n    clearGameParams: function () {\r\n        this.storage.removeItem(this.gameParamsKey);\r\n    },\r\n    getGameConfig: function () {\r\n        var configJSON = this.storage.getItem(this.gameConfigKey);\r\n        return configJSON ? JSON.parse(configJSON) : { playSounds: true };\r\n    },\r\n    setGameConfig: function (gameConfig) {\r\n        this.storage.setItem(this.gameConfigKey, JSON.stringify(gameConfig));\r\n    },\r\n    clearGameConfig: function () {\r\n        this.storage.removeItem(this.gameConfigKey);\r\n    },\r\n};\r\nvar localStorageManager = new LocalStorageManager();\r\n<\/pre>\n<p>Let us now look at the HTML code<\/p>\n<pre class=\"lang:default decode:true\">&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n    &lt;title&gt;Game Template&lt;\/title&gt;\r\n    &lt;meta http-equiv=\"Content-type\" content=\"text\/html; charset=utf-8\"&gt;\r\n    &lt;meta name=\"viewport\" content=\"width=device-width, minimum-scale=1, initial-scale=1, user-scalable=no\"&gt;\r\n    &lt;style&gt;\r\n        \/* following two viewport lines are equivalent to meta viewport statement above, and is needed for Windows *\/\r\n        \/* see http:\/\/www.quirksmode.org\/blog\/archives\/2014\/05\/html5_dev_conf.html and http:\/\/dev.w3.org\/csswg\/css-device-adapt\/ *\/\r\n        @-ms-viewport { width: 100vw ; min-zoom: 100% ; zoom: 100% ; }          @viewport { width: 100vw ; min-zoom: 100% zoom: 100% ; }\r\n        @-ms-viewport { user-zoom: fixed ; min-zoom: 100% ; }                   @viewport { user-zoom: fixed ; min-zoom: 100% ; }\r\n        \/*@-ms-viewport { user-zoom: zoom ; min-zoom: 100% ; max-zoom: 200% ; }   @viewport { user-zoom: zoom ; min-zoom: 100% ; max-zoom: 200% ; }*\/\r\n    &lt;\/style&gt;\r\n\r\n    &lt;link rel=\"stylesheet\" href=\"css\/bootstrap.min.css\"&gt;\r\n    &lt;link rel=\"stylesheet\" href=\"css\/custom.css\"&gt;\r\n    &lt;link rel=\"stylesheet\" href=\"css\/jquery-confirm.min.css\"&gt;\r\n    &lt;script src=\"js\/jquery.min.js\"&gt;&lt;\/script&gt;\r\n    &lt;script src=\"js\/bootstrap.min.js\"&gt;&lt;\/script&gt;\r\n    &lt;script src=\"js\/jquery-confirm.min.js\"&gt;&lt;\/script&gt;\r\n    &lt;script src=\"js\/storage.js\"&gt;&lt;\/script&gt;\r\n\r\n    &lt;script src=\"js\/init-app.js\"&gt;&lt;\/script&gt;\t\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n&lt;div id=\"page1\" style=\"display: none;\"&gt;\r\n    &lt;div class=\"container-fluid\"&gt;\r\n        &lt;div class=\"row\"&gt;\r\n            &lt;div class=\"col-xs-12\"&gt;\r\n                &lt;img id=\"logo\" class=\"img-responsive margin-auto\" src=\"images\/logo.png\" alt=\"logo\" \/&gt;\r\n\t\t\t&lt;\/div&gt;\r\n\t\t&lt;\/div&gt;\r\n\t\t&lt;div class=\"row\"&gt;\r\n            &lt;div class=\"col-xs-12\"&gt;\r\n\t\t\t\t&lt;div class=\"hot-container\"&gt;\r\n\t\t\t\t\t&lt;a href=\"#\" onclick=\"openPage(3)\" class=\"btn btn-green\"&gt;Play&lt;\/a&gt;\r\n\t\t\t\t&lt;\/div&gt;\t\r\n\t\t\t\t&lt;div class=\"hot-container\"&gt;\r\n\t\t\t\t\t&lt;a href=\"#\" onclick=\"openPage(2)\" class=\"btn btn-red\"&gt;Rules&lt;\/a&gt;\r\n\t\t\t\t&lt;\/div&gt;\t\r\n\t\t\t\t&lt;div class=\"hot-container\"&gt;\r\n\t\t\t\t\t&lt;a href=\"#\" class=\"btn btn-voilet\"&gt;Leaderboard&lt;\/a&gt;\r\n\t\t\t\t&lt;\/div&gt;\t\r\n\t\t\t\t&lt;div class=\"hot-container\"&gt;\r\n\t\t\t\t\t&lt;a href=\"#\" class=\"btn btn-blue\"&gt;Rate it&lt;\/a&gt;\r\n\t\t\t\t&lt;\/div&gt;\t\r\n\t\t\t\t&lt;div class=\"hot-container\"&gt;\r\n\t\t\t\t\t&lt;a href=\"#\" class=\"btn btn-blue\"&gt;Share&lt;\/a&gt;\r\n\t\t\t\t&lt;\/div&gt;\t\r\n            &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/div&gt;\r\n\r\n&lt;div id=\"page2\" style=\"display: none;\"&gt;\r\n&lt;div class=\"container-fluid\"&gt;\r\n        &lt;div class=\"row\"&gt;\r\n            &lt;div class=\"col-xs-2\"&gt;&lt;a href=\"#\" onclick=\"openPage(1)\"&gt;&lt;span class=\"glyphicon glyphicon-circle-arrow-left\"&gt;&lt;\/span&gt;&lt;\/a&gt;&lt;\/div&gt;\r\n            &lt;div class=\"col-xs-8\"&gt;\r\n                &lt;p class=\"select-head\"&gt;How to Play?&lt;\/p&gt;\r\n            &lt;\/div&gt;\r\n            &lt;div class=\"col-xs-12\"&gt;\r\n                &lt;div id=\"player-instructions\" class=\"player-inst\"&gt;\r\n                    &lt;h4&gt;GAMEPLAY&lt;\/h4&gt;\r\n                    &lt;p&gt;\r\n                        Your Game Description!!\r\n                    &lt;\/p&gt;\r\n                &lt;\/div&gt;\r\n            &lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/div&gt;\r\n\r\n&lt;div id=\"page3\" style=\"display: none;\"&gt;\r\n    &lt;div class=\"container-fluid\"&gt;\r\n        &lt;div id=\"gametable\" style=\"margin:auto;\"&gt;\r\n\t\t\t&lt;div class=\"row\" style=\"margin-top:10px;\"&gt;\r\n\t\t\t\t&lt;div class=\"col-xs-2\" class=\"margin-auto\" style=\"height:40px;\/*background-color:rgba(255, 255, 255, 0.5);*\/\"&gt;\r\n\t\t\t\t\t &lt;a href=\"#\" onclick=\"openPage(1)\"&gt;&lt;span class=\"glyphicon glyphicon-home\"&gt;&lt;\/span&gt;&lt;\/a&gt;\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t&lt;div class=\"col-xs-2\" class=\"margin-auto\" style=\"height:40px;\"&gt;\r\n\t\t\t\t\t &lt;a id=\"newbutton\" href=\"#\"&gt;&lt;span class=\"glyphicon glyphicon-repeat\"&gt;&lt;\/span&gt;&lt;\/a&gt;\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t&lt;div class=\"col-xs-5\" class=\"margin-auto\" style=\"height:40px;\"&gt;\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t\t&lt;div class=\"col-xs-2\" class=\"margin-auto\" style=\"height:40px;\"&gt;\r\n\t\t\t\t\t &lt;a href=\"#\" id=\"audiobutton\"&gt;&lt;span class=\"glyphicon glyphicon-volume-up\"&gt;&lt;\/span&gt;&lt;\/a&gt;\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t&lt;\/div&gt;\t\r\n\t\t\t&lt;div class=\"row\"&gt;\r\n\t\t\t\t&lt;div class=\"col-xs-12\" id=\"gamearea\"&gt;\r\n\t\t\t\t&lt;br\/&gt;&lt;br\/&gt;My Game\r\n\t\t\t\t&lt;\/div&gt;\r\n\t\t\t&lt;\/div&gt;\r\n        &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n    &lt;!-- \/page --&gt;\r\n&lt;\/div&gt;\r\n\r\n&lt;!-- Game Code --&gt;\r\n&lt;script src=\"js\/game.js\"&gt;&lt;\/script&gt;\r\n\r\n&lt;script&gt;\r\nvar pageCount = 3;\r\nfunction openPage(id){\r\n\tvar gameParams = localStorageManager.getGameParams();\r\n\tgameParams.currentPage = gameParams.currentPage? id: 1;\r\n\tlocalStorageManager.setGameParams(gameParams);\r\n\tfor(var i = 1; i &lt;= pageCount; i++){\r\n\t\t$('#page'+i).hide();\t\t\r\n\t}\r\n\t$('#page'+id).show();\r\n}\r\nfunction openGame(id){\r\n\tvar gameParams = localStorageManager.getGameParams();\r\n\tgameParams.currentPage = id;\r\n\tlocalStorageManager.setGameParams(gameParams);\r\n\tfor(var i = 1; i &lt;= pageCount; i++){\r\n\t\t$('#page'+i).hide();\t\t\r\n\t}\r\n\t$('#page'+id).show();\r\n\r\n\tstartGame();\r\n\t\r\n    var config = localStorageManager.getGameConfig();\r\n    if (config &amp;&amp; config.playSounds){\r\n\t\t$('#audiobutton span').removeClass(\"glyphicon-volume-off\");\r\n        $('#audiobutton span').addClass(\"glyphicon-volume-up\");     \r\n    } else {\r\n        $('#audiobutton span').removeClass(\"glyphicon-volume-up\");\r\n        $('#audiobutton span').addClass(\"glyphicon-volume-off\");         \r\n    }\r\n}\r\n\r\n$(window).on('load', function () {\r\n\tvar gameParams = localStorageManager.getGameParams();\r\n\tif(!gameParams.currentPage){\r\n\t\tgameParams.currentPage = 1;\r\n\t}\r\n\t\r\n\tif(gameParams.currentPage == 3){\r\n\t\topenGame(gameParams.currentPage);\r\n\t} else {\r\n\t\topenPage(gameParams.currentPage);\r\n\t}\r\n\tlocalStorageManager.setGameParams(gameParams);\r\n\tattachEvents();\r\n});\r\n&lt;\/script&gt;\r\n\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p>We have defined all pages in its own DIV and set display to none for all DIVs. When\u00a0we load the app for the first time, current page id can be set to the first page\u00a0and saved to the cache. For all further visits we will\u00a0set it to the last opened page id and then load that page.<\/p>\n<p>There are two different methods for opening the page; OpenPage and OpenGame; OpenPage is a general purpose page redirect with no additional handling whereas OpenGame might need some additional work so we have separate methods to handle these scenarios. You can define additional pages and respective methods.<\/p>\n<p>The\u00a0template can be downloaded from <a href=\"https:\/\/github.com\/ngdevr\/jQueryGameTemplate\/\" target=\"_blank\">github<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>For mobile web app templates, Jquery Mobile has been a popular choice but it comes with its own set of issues when we have multiple pages in the game since it makes ajax calls to load the pages and if there are specific things which are required to be handled on each page then it becomes even more complex to handle those scenarios. The alternate is to have a simple Bootstrap and JQuery based template with multiple pages which is simple and works just like a website. The problem here is that the pages are loaded again and again whenever we navigate from one page to another. This is not an ideal scenario\u00a0for mobile web apps\u00a0since all external libraries are required to be initialized on all pages which\u00a0is not required\u00a0and unnecessary work for processor. For example,\u00a0if you compiled your web app using PhoneGap and\u00a0are using a plugin to play audio.[&#8230;]<\/p>\n","protected":false},"author":5,"featured_media":1554,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[18,17,19],"tags":[],"class_list":["post-579","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-bootstrap","category-jquery","category-mobile"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/579","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/comments?post=579"}],"version-history":[{"count":4,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/579\/revisions"}],"predecessor-version":[{"id":629,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/posts\/579\/revisions\/629"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/media\/1554"}],"wp:attachment":[{"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/media?parent=579"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/categories?post=579"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.netexl.com\/blog\/wp-json\/wp\/v2\/tags?post=579"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}