Stop jQuery Mobile swipe event double bubbling - ios

I have jQuery Mobile on iPad Safari and for some reason touch swipe events are firing twice.
People have reported the same problem over the past year as recently as this week but I cannot find an explanation for how to fix the double event without modifying jQuery Mobile and I do not want to do that. Thread on jQuery forums
The follwoing element bindings for the swipe handler all have the same incorrect double-event result where the alert is called twice for every one swipe.
How should jQuery Mobile touch events be bound in order to avoid double bubbling?
// Test 1: Binding directly to document with delegate()
$(document).delegate(document, 'swipeleft swiperight', function (event) {
alert('You just ' + event.type + 'ed!');
});
// Test 2: Binding to document with on() handler recommended as of 1.7 with and without preventDefault
$(document).on('swipeleft',function(event, data){
event.preventDefault();
alert('You just ' + event.type + 'ed!');
});
// Test 3: Binding to body with on() with and without event.stopPropagation
$('body').on('swipeleft',function(event, data){
event.stopPropagation();
alert('You just ' + event.type + 'ed!');
});
// Test 4: Binding to div by class
$('.container').on('swipeleft',function(event, data){
event.stopPropagation();
alert('You just ' + event.type + 'ed!');
});

event.stopImmediatePropagation() was the trick, which is different from stopPropagation(). Ensuring the jQuery on() method is called in document.ready seems to help. I was able to use any element selector to bind the events including using the swipeup and swipe down from here
$(document).ready(function(){
$(document).on('swipeleft swiperight swipedown swipeup',function(event, data){
event.stopImmediatePropagation();
console.log('(document).Stop prop: You just ' + event.type + 'ed!');
});
});

Well, had the same problem with swipe event been called twice.
The workaround is to bind the event this way:
$(document).on('swipeleft', '#div_id', function(event){
//console.log("swipleft"+event);
// code
});

it really helped in my case too. I was trying to swipe pages with mobile jquery and swipe events( left and right ) were triggering several times. event.stopImmediatePropagation() it worked like a charm. Thank you !!
here is my code.
<script type="text/javascript">
$(document).bind( 'pageinit', function(event) {
$("div:jqmData(role='page')").live('swipeleft swiperight',function(event){
if (event.type == 'swipeleft') {
var prev = $(this).prev("div:jqmData(role='page')");
if(typeof(prev.data('url')) !='undefined') {
$.mobile.changePage(prev.data('url'), { transition: 'slide', reverse: false});
event.stopImmediatePropagation();
}
}
if (event.type == 'swiperight') {
var next = $(this).next("div:jqmData(role='page')");
if(typeof(next.data('url')) != 'undefined') {
$.mobile.changePage(next.data('url'), { transition: 'slide', reverse: false});
event.stopImmediatePropagation();
}
}
});
});
</script>
HTML -
<div data-role="page" id="page1" data-url="#page1">
<div data-role="content">
<div>
<h1> Page 1 </h1>
<p>I'm first in the source order so I'm shown as the page.</p>
<p>View internal page called</p>
<ul data-role="listview" data-inset="true" data-theme="c">
<li>Swipe Right to view Page 2</li>
</ul>
</div>
</div>
</div>
<div data-role="page" id="page2" data-url="#page2">
<div data-role="content">
<div>
<h1> Page 2 </h1>
<p>I'm first in the source order so I'm shown as the page.</p>
<p>View internal page called</p>
<ul data-role="listview" data-inset="true" data-theme="c">
<li>Swipe Right to view Page 3</li>
</ul>
</div>
</div>
</div>

Related

jQuery Mobile - Make only certain part of collapsible widget active

does anyone know of a way to make only a certain part of a collapsible widget actually toggle the collapsible part? I have been trying to figure this out for hours and can't seem to get it.
Basically, I don't want a whole <li> to trigger the expand/collapse, but only a small area on the right.
Thanks.
This is my solution:
block default events
create a clickable element on the right side
manage expand/collapse status
HTML
<div data-role="page" id="page">
<div data-role="content">
<div data-role="collapsible" data-inset="false" data-collapsed="true">
<h1>Title <span class="clickme">Click me</span></h1>
<h5>Content</h5>
</div>
<div data-role="collapsible" data-inset="false" data-collapsed="true">
<h1>Title <span class="clickme">Click me</span></h1>
<h5>Content</h5>
</div>
<div data-role="collapsible" data-inset="false" data-collapsed="true">
<h1>Title <span class="clickme">Click me</span></h1>
<h5>Content</h5>
</div>
</div>
</div>
JavaScript
$(document).on("pagecreate", "#page", function()
{
$("[data-role=collapsible] a").on("click", function()
{
event.preventDefault();
event.stopPropagation();
});
$(".clickme").on("click", function()
{
var element = $(this).closest("[data-role=collapsible]");
if (element.attr("data-collapsed") == "true")
{
element.attr("data-collapsed", "false");
element.collapsible("expand");
}
else
{
element.attr("data-collapsed", "true");
element.collapsible("collapse");
}
});
});
JSFiddle
So there were a few things I was doing incorrectly, helpfully pointed out by Sga.
You can't use vclick. For whatever reason, this jQM plugin isn't using an established jQM event to handle it's stuff. I was previously listening to the vclick event:
$parent.on('vclick', '[data-role=collapsible] a', function() {});
You can't bind the thing you want to prevent default on on a parent. For example:
$parent.on('click', '[data-role=collapsible] a', function() {});
That doesn't work, you have to bind it after the elements are rendered a la:
$('.collapse-target', $list).on('click', function() {});
After doing all that, which totally isn't optimal, I was able to achieve the effect I wanted. It's too bad this feature wasn't included out of the box for the collapsible widget. Hopefully this helps someone.

Navbar content loading all options

I have a navbar in Jquery mobile which have 5 contents, each content have a text right now. The problem is that the first time that I load the page, in the first tab all the content is loaded, and as soon as you start pressing the buttons, the content is loaded normally, as shown in the image below:
Then when I start clicking the buttons the content shows correctly:
Below is the code that I'm using:
Javascript:
<script>
(function($) {
// Before handling a page change...
$(document).bind("pagebeforechange", function(e, data)
{
// If the new page is not being loaded by URL, bail
if (typeof data.toPage !== "string")
{
return;
}
// If the new page has a corresponding navbar link, activate its content div
var url = $.mobile.path.parseUrl(data.toPage);
var $a = $("div[data-role='navbar'] a[href='" + url.hash + "']");
if ($a.length)
{
// Suppress normal page change handling since we're handling it here for this case
e.preventDefault();
}
// If the new page has a navbar, activate the content div for its active item
else
{
$a = $(url.hash + " div[data-role='navbar']").find("a.ui-btn-active");
// Allow normal page change handling to continue in this case so the new page finishes rendering
}
// Show the content div to be activated and hide other content divs for this page
var $content = $($a.attr("href"));
$content.siblings().hide();
$content.show();
});
})(jQuery);
Here is the HTML:
<body>
<div data-role="page">
<div data-role="header">
<div data-role="navbar">
<ul>
<li>One</li>
<li>Two</li>
<li>Three</li>
<li>Four</li>
<li>Five</li>
</ul>
</div>
</div>
<center>
<div data-role="content">
<div id="oneContent">First</div>
<div id="twoContent">Second</div>
<div id="threeContent">Third</div>
<div id="fourContent">Fourth</div>
<div id="fiveContent">Fifth</div>
</div>
</center>
</div>
</body>
How can avoid the problem of loading all content at first load?
UPDATED TO ANSWER NEW REQUEST BELOW
If you want the first tab to show up by default, set the CSS of them all individually then toggle them into view.
First put this in your <head> or in a linked CSS file:
<style>[data-role=content] > div { display: none; }</style>
Then insert these two lines into your Javascript at the beginning like so:
(function ($) {
$(document).ready(function() {
var path = (window.location.hash) ? window.location.hash : '#oneContent';
$(path).show();
});
$(document).bind("pagebeforechange", function (e, data) {
....
Here's an updated fiddle diddle skittle

jQueryMobile Change listview's item attributes dynamically

I want to change dynamically (click event) a data-theme of a list item in a jQueryMobile listview. Firebug shows me that the change is performed but the list is not refreshed. (If I add elements it works, but with attribute changes the list won't be refreshed.) For example I want to change the data-theme from c to f when the item is clicked.
I tried everything I read in this forum, from trigger('mouseout') over trigger('create') on parents to refreshing the whole page, but no effect at all, so I guess I am blind to see something obvious. Maybe somebody can give me a hint... :)
<script>
function fillList() {
var li = '<li data-theme="c">List item</li>';
$("#alist").empty().append(li).promise().done(function () {
$(this).off("click").on("click", ".info", function (e) {
e.stopImmediatePropagation();
e.preventDefault();
var theLiElem = $(this).closest('li');
theLiElem.attr('data-theme','f').trigger('mouseout');
theLiElem.trigger("create");
});
$(this).listview("refresh");
});
}
$(document).on("pageinit", "#info-page", function () {
fillList();
});
</script>
<div data-role="page" id="info-page">
<div data-role="content">
<ul data-role="listview" id="alist" data-inset="true">
</ul>
</div>
</div>
Kind regards,
Steve

Show feedback to a user when loading content though ajax

I'm using the jquery tab plugin with the same results that are on jquery ui site (link text).
The problem is that I want to change the tab label to something like "Loading" when the Ajax request is waiting for a response. How shall I achieve that?
Edit:
What I have now:
<div id="tabs">
<ul>
<li>User Profile</li>
<li>Edit</li>
<li>Delete AccountT</li>
</ul>
<div id="tabs-1">
<% Html.RenderPartial("UserProfile", Model); %>
</div>
</div>
and on javascript I just have the following code:
<script type="text/javascript">
$(function () {
$("#tabs").tabs({
});
});
</script>
What i want is to show a loading message inside the div of the active tab.
According to the widget doc, ajax should work outside the box if your href is an actual link :
To customize loading message, you can use the spinner option :
Fetch external content via Ajax for
the tabs by setting an href value in
the tab links. While the Ajax request
is waiting for a response, the tab
label changes to say "Loading...",
then returns to the normal label once
loaded.
<script>
$(function() {
$( "#tabs" ).tabs({
spinner: "<em>My Loading Msg</em>",
ajaxOptions: {
error: function( xhr, status, index, anchor ) {
$( anchor.hash ).html(
"Couldn't load this tab. We'll try to fix this as soon as possible. " +
"If this wouldn't be a demo." );
}
}
});
});
</script>
Why don't you just set the tab label to "loading" onclick and change it to whatever you want once you get a response back from your AJAX?
$(function () {
$("#tabs").tabs();
$("#tab1").click(function() {
$(this).val("Loading...");
$.ajax({
blablabla...
success: function() {
$("#tab1").val("Tab 1");
}
});
});
This should give you an idea, obviously the function could be written better to act on each tab.
I think what you are after already exists in the jquery ui for Tabs.
The HTML content of this string is
shown in a tab title while remote
content is loading.
http://jqueryui.com/demos/tabs/#option-spinner
Hope this helps!
From your example am I correct in assuming that you want to navigate to another page when the user clicks on either of the second two tabs, but you want a loading message to be displayed while you are waiting for the next tab to load?
If so, you could do it by keeping a loading message in the other tabs like so:
<input type="hidden" id="modelId" value="<%=Model.Id%>" />
<div id="tabs">
<ul>
<li>
User Profile
</li>
<li>
Edit
</li>
<li>
Delete AccountT
</li>
</ul>
<div id="tabs-1">
<% Html.RenderPartial("UserProfile", Model); %>
</div>
<div id="tabs-2">
<p>Loading, please wait...</p>
</div>
<div id="tabs-3">
<p>Loading, please wait...</p>
</div>
</div>
And doing the redirection to your other pages in your javascript, doing it something like this:
$(document).ready(document_ready);
function document_ready()
{
$("tabs").bind("tabsselect", onTabSelected);
}
function onTabSelected(event, ui)
{
var modelId = $("#modelId").val();
switch (ui.panel.id)
{
case "tabs-2":
window.location.href = "/User/EditUser/" + modelId;
break;
case "tabs-3":
window.location.href = "/User/Delete/" + modelId;
break;
}
}

some issue with nested tab while trying to implement jquery ui tab with back/forward/refresh button support

I'm having problems with the browser history (using the "Back" and "Forward" buttons) with jQuery's tab plugin. I combined jQuery UI tab and jQuery BBQ: Back Button & Query Library plugin together for this.
I think the problem may deal with nested tabs.
You can find full code at http://jsfiddle.net/nQgC8/
If you test this, you'll see that tabs at top works fine: you can navigate to any of the tabs and successfully go back and forward at any point. This is not the case for nested tab. Nested tab can be found inside tab 3.
What do I need to do to make this work for nested tabs?
The following are snippets to show my point:
<div id="tabs">
<ul>
<li>Tab 1</li>
<li>Tab 2</li>
<li>Tab 3</li>
</ul>
<div id="tabs-1">...</div>
<div id="tabs-2">...</div>
<div id="tabs-3">
<div id="tabs2">
<ul>
<li>Tab 3-1</li>
<li>Tab 3-2</li>
<li>Tab 3-3</li>
</ul>
<div id="tabs-31">...</div>
<div id="tabs-32">...</div>
<div id="tabs-33">...</div>
</div>
</div>
</div>
<script>
$(function() {
$( '#tabs' ).tabs();
// Adding hash to url for tab tracking
$('#tabs').bind('tabsselect', function (event, ui) {
window.location.href = window.location.protocol + '//' + window.location.hostname + window.location.pathname + ui.tab.hash;
});
$( '#tabs2' ).tabs();
// Adding hash to url for tab tracking
$('#tabs2').bind('tabsselect', function (event, ui) {
window.location.href = window.location.protocol + '//' + window.location.hostname + window.location.pathname + ui.tab.hash;
});
// Handle back/forward/refresh buttons
$(window).hashchange(function () {
var hash = location.hash;
switch (hash) {
case '#tabs-1':
case '#tabs-2':
case '#tabs-3':
$('#tabs').tabs('select', hash);
break;
case '#tabs-31':
case '#tabs-32':
case '#tabs-33':
$('#tabs').tabs('select', '#tabs-3');
$('#tabs2').tabs('select', hash);
break;
default:
$('#tabs').tabs('select', '#tabs-1');
location.hash = '#tabs-1';
}
});
$(window).hashchange();
});
</script>
This works for me in chrome. What browser are you trying it with?

Resources