In my Site, am seeing pageinit being called during browser's forward button selection. Is this correct. Will this not bind the registered events twice.
First.html
<!DOCTYPE HTML>
<html>
<head>
<title>Test1</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.js"></script>
<script src="../scripts/common.js"></script>
</head>
<body>
<div data-role="page" id="first">
<h1 style="background: red">Swipe 1</h1>
Click
</div>
</body>
</html>
Second.html
<!DOCTYPE html>
<html>
<head>
<title>Test2</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.css" />
<script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.3.1/jquery.mobile-1.3.1.min.js"></script>
<script src="../scripts/common.js"></script>
</head>
<body>
<div data-role="page" id="second">
<div data-role="header">
<h1>jQuery Mobile</h1>
</div>
<div data-role="content">
Click
</div>
<div data-role="footer">
<h4>www.jboss.org/developer</h4>
</div>
</div>
</body>
</html>
third.html
<!DOCTYPE html>
<html>
<head>
<title>jQuery Mobile Template</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.css" />
<script type="text/javascript" src="http://code.jquery.com/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js"></script>
</head>
<body>
<div data-role="page" id="third">
<div data-role="header">
<h1>jQuery Mobile</h1>
</div>
<div data-role="content">
<ul id="listOfItems" data-role="listview" data-inset="true" data-filter="true">
<li>One
</li>
<li>Two
</li>
<li>Three
</li>
</ul>
</div>
<div data-role="footer">
<h4>www.jboss.org/developer</h4>
</div>
</div>
</body>
</html>
common.js
$(document).on('pageinit', "#first", function (event) {
console.log("first");
});
$(document).on('pageinit', "#second", function (event) {
console.log("second");
});
$(document).on('pageinit', "#third", function (event) {
console.log("third");
});
Can you check the above code and let me know why pageinit is called everytime.
Intro
Before we can talk about page events we need to talk about jQuery Mobile page architecture. jQuery Mobile applications can be created in two different ways, multi-page template or several HTML pages.
Different jQuery Mobile page architectures
Multi-page template
In this kind of template everything is loaded into the DOM. No matter how many times user transitions from one page to another previous page will stay loaded into the DOM. While this is the best solution for smooth transition between pages it can burden the DOM. This is usually not a problem on desktop browsers but mobile browsers can suffer, particularly on a low end devices.
Multi HTML template
This template solution requires several HTML pages. It is an excellent option for large mobile app construction but there's a catch. Unlike multi-page template each time page is visited it will loaded into the DOM. On slower devices it can cause transition problems particularly if destination page is a large one. This can be solved with page prefetching where jQuery Mobile loads the target page in the background after the primary page has loaded and the pagecreate event has triggered. BUT there's another large problem and this is not sufficiently described in jQuery Mobile documentation. During the page transition previous page is going to be removed from the DOM. This is done to prevent content accumulation inside the DOM. This can also be prevented with another attribute called:
data-dom-cache="true"
or by initializing global parameter:
$.mobile.page.prototype.options.domCache = true;
How page architecture impacts page events
jQuery Mobile has several page events (more about it can be found HERE or HERE). They are here to cover full page load / transition process. Advanced jQuery Mobile users know to use pageinit event instead of jQuery classic document ready event. Just like document ready, pageinit is here to trigger only once during the page initialization.
But there's a catch, pageinit will trigger only once during the page initialization inside the DOM. If page is removed from the DOM (in multi HTML template) and loaded again pageinit will trigger again.
In a nutshell each page DOM initialization will trigger new page events initialization. Like mentioned previously this can be prevented with page cashing.
Impact of page cashing
This is almost a tabu theme for jQuery Mobile developers so let me tell something, there's no need to be afraid of page cashing. Believe me, jQuery Mobile is robust framework and it usually plays well on every available platform. Huge number of complains related to jQuery Mobile are not directly related to its large DOM size requirements so don't worry about cashing.
That doesn't mean you should fill the DOM like a drunken sailor. Every app development must be planed in advance. Mostly used pages must permanently stay inside the DOM. Everything else should be used ad hoc (loaded / removed when needed).
EDITED:
It seems that when using a multipage template approach (single html with all pages included) then pageinit is fired once since the pages are loaded in DOM and remain there when transitioning between pages.
However when using multiple pages and navigating though Ajax you will notice that the pages contain an attribute data-external-page="true" except the first page. The first page is always cached in DOM even if you're using domCache false. But the pages except the first page are loaded and cleaned out after you leave them. That's why pageinit is fired on back/forward button clicks.
The domCache sets whether to keep the page in the DOM after the user has navigated away from it. You can read about it on jQM docs. By default the domCache option's value is false.
Adding:
$(document).on('mobileinit', function () {
$.mobile.page.prototype.options.domCache = true;
});
between jQuery js and jQuery Mobile js will cause the pages to be cache in DOM. Therefore pageinit will not fire.
An alternative is to add data-dom-cache="true" on your anchor.
The drawback of using domCache is that the DOM can be large, resulting in memory issues. In a such approach the DOM management is left on the developer.
At last make sure that you're using the same jQ, jQM versions in your pages. The first 2 pages are using jQ 1.9.1, jQm 1.3.1 and the third page is using jQ 1.7.1, jQM 1.1.0. This is irrelevant with the specific issue but it worth to mention it.
I hope this helps.
Related
I have a jQuery Mobile page with a dynamically generated collapsible. It works great, except if I leave the page then return to it. When that happens, the collapsible appears twice. A refresh fixes the issue but that's obviously not a good solution. Others on here have described similar symptoms, but their solutions didn't work for me. I'm hoping someone can help me out. I think I may be misusing pageinit...
I'm using jQuery 1.9.1 with jQuery Mobile 1.3.1. I'm using Codiqa to generate my pages, hence why I'm on jQuery 1.3.1. I generated the code such that every page is a separate html file.
Here's my code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Characters | PCT</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<link href="css/codiqa.ext.css" rel="stylesheet">
<link href="css/jquery.mobile-1.3.1.css" rel="stylesheet">
<script src="js/jquery-1.9.1.js"></script>
<script src="js/jquery.mobile-1.3.1.js"></script>
<script src="js/codiqa.ext.js"></script>
<script src="http://www.parsecdn.com/js/parse-1.2.15.min.js"></script>
<script src="js/pct.js"></script>
</head>
<body>
<div data-role="page" data-control-title="Characters" id="characters"
class="character_page">
<script>
$(document).on("pageinit", "#characters", function() {
var charQuery = new Parse.Query("Character");
charQuery.equalTo("user", Parse.User.current());
charQuery.find({
success: function(results) {
for (var j = 0; j < results.length; j++) {
var object = results[j];
var charName = object.get('charName');
var charNumActions = object.get('charNumActions');
var charSpd = object.get('charSpd');
$("#char_list").append("<div data-role='collapsible'><h4> " +
charName + "</h4><p><strong>Number of Actions:</strong> " +
charNumActions + "</p><p><strong>Spd:</strong> " + charSpd + "</p>
<a data-role='button' class='edit_button'
href='edit_character.html?charname=" + charName + "' data-icon='edit'
data-iconpos='left'>Edit</a><a data-role='button'
id='delete_char_button' class='delete_button' data-inline='true'
data-icon='delete' data-iconpos='left'>Delete</a></div>");
$("#char_list").collapsibleset("refresh");
$(".edit_button").buttonMarkup();
$(".delete_button").buttonMarkup();
}
}
});
});
</script>
<div data-theme="a" data-role="header">
Thank you in advance for taking the time to look at this.
Update:
Based on your edited OP and comment below, when using single page model, functions/JS code that is related to a specific page should be placed inside that page's div.
jQuery Mobile uses Ajax to load pages, it loads all libraries and code of first loaded page's head tag, and neglects the rest as it loads only data-role="page" div.
<div data-role="page" id="characters">
<script>
<!-- place code here -->
</script>
</div>
Because you bind to pageinit without specifying a page. The code will be executed whenever a page is initiated.
You should bind it to page id to fire once only.
$(document).on("pageinit", "#page_id", function () {
// code
});
I see some others (e.g. this post) have had trouble using external javascript scripts in JQuery Mobile - as of today I have joined their ranks.
I have a single external js script (controllers.js) which contains code that affects several pages on the site. It is loaded on every page of the site. I put the js file just before the tag it works fine on the initial page load. However when I navigate thereafter (using the JQM Ajax methods) all functions in the script stop working. I would have thought the script would remain in cache - but heyho. Anyhow there's an FAQ which answers this question and I've implemented their suggestion which is: "...to reference the same set of stylesheets and scripts in the head of every page." I have done this but when I do even on the first page load the js doesn't fire. There aren't page specific scripts - so the remainder of that FAQ does not apply.
My cut down html looks like this:
<!doctype html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="stylesheet" href="/static/jquery-ui-1.10.3.custom.min.css">
<link rel="stylesheet" href="/static/jquery-mobile/css/themes/default/jquery.mobile-1.3.2.min.css">
<script src="/static/jquery-1.9.1.min.js"></script>
<script src="/static/jquery-ui/jquery-ui-1.10.3.custom/js/jquery-ui-1.10.3.custom.min.js"></script>
<script src="/static/jquery-mobile/js/jquery.mobile-1.3.2.min.js"></script>
<!-- CSS: implied media="all" -->
</head>
<body>
<div data-role="page" id = "main-page">
<div data-role="header" style="overflow:hidden;">
</div>
<div id = "home" data-role="content">
<input type ='button' id = "some_btn" value="press me">
</div>
</div>
<script type="text/javascript" src="/static/js/controllers.js"></script>
</body>
</html>
and within the javascript file
controllers.js
$('#some_btn').click(function()
{
alert('button pressed');
});
Any ideas on what I may be doing wrong here?
Since the content in the #page is loaded dynamically via ajax, click will not work, since it only works on elements that are on the page when the script is called.
You need to use the .on() method:
$('body').on('click','#some_btn',function()
{
alert('button pressed');
});
I have an issue with vclick (or click) events when fired.
This is my html code:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Document</title>
<link rel="stylesheet" href="css/jquery.mobile-1.3.1.css">
<link rel="stylesheet" href="css/estilo.css">
<script src="js/cordova-2.6.0.js"></script>
<script src="js/jquery-2.0.0.js"></script>
<script src="js/functions.js"></script>
<script src="js/jquery.mobile-1.3.1.js"></script>
</head>
<body>
<div data-role="page" id="page">
<div data-role="header" position="fixed">
<h1>Data</h1>
</div>
<div data-role="content">
<div id="btn_comentar">
</div>
</div>
</div>
</body>
</html>
And this is my functions.js
$(document ).bind("mobileinit", function(){
$(document).bind("pageinit",function(){
$("#btn_comentar").bind("vclick",function(e){
console.log(e.isDefaultPrevented());
console.log(e.result);
console.log(e.relatedTarget);
alert("buttooon");
list_comments();
});
});
}
When I click my #btn_comentar, the data that I want to retrieve from function list_comments (sending via ajax) is duplicated; I realized that it was sending twice, and finally that it was something about when I clicked on my button.
This is the output from the console (twice):
false
undefined
null
and also the alert message box (twice) "buttoon";
I have tried some solutions like:
jQuery Mobile : replace click event by vclick event
but without success, please need some help
This is my new code and how it is now working, but it seems that without jQuery Mobile's default configuration
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Document</title>
<link rel="stylesheet" href="css/jquery.mobile-1.3.1.css">
<link rel="stylesheet" href="css/estilo.css">
<script src="js/cordova-2.6.0.js"></script>
<script src="js/jquery-2.0.0.js"></script>
<script src="js/custom-mobile.js"></script>
<script src="js/jquery.mobile-1.3.1.js"></script>
<script src="js/functions.js"></script>
</head>
<body>
<div data-role="page" id="page">
<div data-role="header" position="fixed">
<h1>Data</h1>
</div>
<div data-role="content">
<div id="btn_comentar">
</div>
</div>
</div>
</body>
</html>
custom-mobile.js
$(document ).bind("mobileinit", function(){
//$.mobile.allowCrossDomainPages = true;
});
functions.js
$(document).on("ready",function(){
$("#btn_comentar").bind("vclick",function(){
list_comments();
});
});
According to docs,
These enhancements are applied based on jQuery Mobile's default configuration, which is designed to work with common scenarios, but may or may not match your particular needs. Fortunately, these settings are easy to configure using the mobileinit event.
So that's what you need to use mobileinit for. For setting defaults like this :
$(document).bind('mobileinit', function(){
$.mobile.defaultTransition = 'slideup';
});
If my understanding is right, mobileinit is included/fired before jQuery Mobile's js is included. Assuming you done that, your script order must look like this :
<script src="jquery.js"></script>
<script src="custom-scripting.js"></script> <!-- This script must have mobileinit -->
<script src="jquery-mobile.js"></script>
At this point of time (when custom-scripting.js is loaded), pageinit wouldnt be defined.
It would be wise to add your pageinit event AFTER jQM script.
<script src="jquery.js"></script>
<script src="custom-scripting.js"></script> <!-- This script must have mobileinit -->
<script src="jquery-mobile.js"></script>
<script>
$(document).bind("pageinit", function(){
$(document).bind("vclick", "#btn_comentar" ,function(e){
console.log(e.isDefaultPrevented());
console.log(e.result);
console.log(e.relatedTarget);
alert("buttooon");
list_comments();
});
});
</script>
use once on pageinit:
$(document).on('pageinit') {
$("#btn_comentar").on("vclick",function(e){
console.log(e.isDefaultPrevented());
console.log(e.result);
console.log(e.relatedTarget);
alert("buttooon");
list_comments();
});
}
this should work
Another cause of double-vclicks I've encountered is due to Chromium synthesizing both touch events in addition to mouse events. I confirmed this cause by running the app in desktop Chrome's developer "device mode" (where the mouse cursor changes into a circle), confirming the problem exists, then toggling off device mode, and confirming the problem is "fixed".
jblas discusses it, partial excerpt:
Note that vclick does NOT suppress the synthesized mouse/click events that are generated by the browser because it does not know what context it is being used in, AND form input elements require the mouse/click events to function normally.
If you use a joystick or mouse (desktop), the alert will fire on the normal mouse click event.
If you want to suppress the click event while using touch, you have to call event.preventDefault() in your vclick handler. This will queue the request to kill the click event that follows but due to the differences in the way device vendors implement their events, and some bugs within different android OS versions, this turns out to be very hard to do. We try a couple of methods to figure out whether or not to kill a click event ... one is based on the element the touch event was triggered, on, and another is the position of the touch event. This is necessary because The browser does not necessarily dispatch the mouse events to the same element that it used for the touch event.
I created a new test project in MVC4 to test if I could use Facebook Helper from the Nuget gallery. I followed the instructions which are very simple, point and click to install the Facebook helper from Nuget, edit your page, and insert like button.
When I run the project I can actually see the facebook likebutton being generated when I view it through firebug, but it does not appear on the page.
Here is my default index page
#{
ViewBag.Title = "Home Page";
}
#section featured {
<section class="featured">
<div class="content-wrapper">
<hgroup class="title">
<h1>#ViewBag.Title.</h1>
<h2>#ViewBag.Message</h2>
</hgroup>
<p>
To learn more about ASP.NET MVC visit
http://asp.net/mvc.
The page features <mark>videos, tutorials, and samples</mark> to help you get the most from ASP.NET MVC.
If you have any questions about ASP.NET MVC visit
our forums.
</p>
</div>
</section>
}
<h3>We suggest the following:</h3>
<div>
#Facebook.LikeButton()
</div>
I´ve gone through several pages regarding Facebook helper and they all describe the process in the same way. Does anyone know if I should config any files before using this library?
Edited:
I will paste my html code that is loaded into the markup after execution. I will abbreviate some scripts and stuff since it has many hundreds of line of code :)
<html id="facebook" class="" lang="en">
<head>
<meta charset="utf-8">
<script>
<script>
<noscript><meta http-equiv="refresh" content="0; URL=/plugins/like.php?href=http%3A%2F%2Flocalhost%3A13876%2F&layout=standard&show_faces=src&width=450&action=like&colorscheme=light&height=80&font&locale=en_US&ref&_fb_noscript=1" /></noscript>
<meta content="noodp, noydir" name="robots">
<meta id="meta_referrer" content="default" name="referrer">
<meta content="Facebook is a social utility that connects people with friends and others who work, study and live around them. People use Facebook to keep up with friends, upload an unlimited number of photos, post links and videos, and learn more about the people they meet." name="description">
<link href="https://www.facebook.com/plugins/like.php?href=http%3A%2F%2Flocalhost%3A13876%2F&layout=standard&show_faces=src&width=450&action=like&colorscheme=light&height=80&font&locale=en_US&ref" media="handheld" rel="alternate">
<title>Facebook</title>
<link href="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yc/r/-WUN6qLbp5n.css" rel="stylesheet">
<link href="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yf/r/gd49XKRAMNQ.css" rel="stylesheet">
<link href="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yc/r/XaOowWd9_Aq.css" rel="stylesheet">
<link href="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yH/r/Dpn1SKTH3-z.css" rel="stylesheet">
<script src="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yB/r/Vm1JnKckidu.js">
<script src="https://fbstatic-a.akamaihd.net/rsrc.php/v2/y-/r/ARN_8tuLdws.js">
<script src="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yq/r/XboiPeV3jQI.js">
<script>
<script>
<script src="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yP/r/eTzqwp1jxjF.js" async="">
<script src="https://fbstatic-a.akamaihd.net/rsrc.php/v2/yh/r/aTzvHAZI_Jd.js" async="">
</head>
<body class="plugin ff4 win Locale_en_US">
<div id="FB_HiddenContainer" style="position:absolute; top:-10000px; width:0px; height:0px;"></div>
<script>
<script>
<script>
</body>
</html>
See where in the Url it generates (link href="https://www.facebook...) it says "&show_faces=src"
That causes the problem; it should be "&show_faces=true"
Even though true is supposed to be the default, if you have this issue try changing it to: #Facebook.LikeButton(showFaces: false)
EDIT: this only helps if you set it to false. Same issue with LikeBox and all boolean properties.
If the HTML is being rendered I can only assume the problem it's a CSS problem, check the styling of the elements in FireBug and make sure they aren't being hidden e.g. check for display: none or visibility: hidden or make sure there isn't float applied which is taking it off the screen.
I am at my wits end with jQuery Mobile events. I do not understand them, despite following the docs to the T. I am using the following code to initialize my pages. The problem is that some seem to fire multiple times and occasionally when I go back to a page nothing will appear, as though .live pageinit simply doesn't fire at all. I am quite confused. Is pageinit the way to go? is .live best practice? Do I need to clean up after myself and use something like pagehide to remove stuff from the DOM? Please help me understand. Thanks!
// page number 1
<header>
includes and stuff
<header>
<body>
<div data-role="page" data-theme="a" id="dashboardPage">
$('#dashboardPage').live('pageinit',function() {
});
// somewhere in here a page transition to anotherPage.html (not necessarily the id of the new page in the <div data-role-"page data-theme...> declaration
$.mobile.changePage("anotherPage.html",{transition : "slide"});
</div>
</body>
// page number 2
<header>
includes and stuff
<header>
<body>
<div data-role="page" data-theme="a" id="specialsPage">
$('#specialsPage').live('pageinit',function() {
});
</div>
</body>
You could try something like this:
<html>
<header>
<!-- BASIC INCLUDES -->
<link rel="stylesheet" href="./css/jquery.structure-1.1.0.min.css" />
<link rel="stylesheet" href="./css/jquery.mobile-1.1.0.min.css" />
<link rel="stylesheet" href="./css/jquery.mobile.theme-1.1.0.min.css" />
<script type="text/javascript" src="./js/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="./js/jquery.mobile-1.1.0.min.js"></script>
<!-- END - BASIC INCLUDES -->
</header>
<body>
<!-- PAGE 1 -->
<div data-role="page" data-theme="a" id="dashboardPage">
<script>
// INITIALIGE PAGE 1 - dashboardPage
$("#dashboardPage").live('pageinit',function(event){
alert( '"initializing" page 1: dashboardPage');
});
</script>
HERE YOU ARE AT PAGE 1 (dashboardPage)!!!<br><br>
CLICK HERE TO GO TO PAGE 2 (specialsPage)
</div>
<!-- PAGE 2 -->
<div data-role="page" data-theme="a" id="specialsPage">
<script>
// INITIALIGE PAGE 2 - specialsPage
$("#specialsPage").live('pageinit',function(event){
alert( '"initializing" page 2: specialsPage');
});
</script>
HERE YOU ARE AT PAGE 2 (specialsPage)!!!<br><br>
CLICK HERE TO GO TO PAGE 1 (dashboardPage)
</div>
</body>
</html>
Hope it helps.
When using JQuery mobile you should consider you have a single page which content is updated dynamically. You do not navigate from one page to the other as you normally would.
Because of this, any code present in a page you navigate to with JQM simply be ignored (tyr changing the header of page number 2, and navigate to this page from page number 1 with changepage, you will not see any difference).
Because of that, all your js code should be directly available within the page in which your user will arrive.
If you add the following code in the scripts of page 1, you will proeperly detect the initialization of of page2 when it is loaded with changepage.
$("#specialsPage").live('pageinit',function(event){
alert( '"initializing" page 2: specialsPage');
});
An important thing to keep in mind is that you user might also directly access your page2, which means that all the code should also be available for page 2. Because of this I would strongly recommend including all your code in a js file referenced in the headers of all your page, and NOT directly in script tags within your pages.