7

I've created an app using jQuery Mobile (1.3.1) and PhoneGap (3.4), and have skinned it to have a fairly "flat" looking interface:

home screen

I'm having some trouble where, only on iOS, and only occasionally, the "back" button on only one certain screen becomes unresponsive. The same HTML & CSS for the back button on other screens seems fine, it's just this one screen. Here it is in chrome with the button selected in devtools to highlight its hit area:

back button

And just for good measure, here's the header as well:

header

The code for this screen's header is the same for just about every screen in the app:

<div data-role="header" data-theme="e" data-position="fixed" data-tap-toggle="false">
    <h1>Event Detail</h1>
    <a data-rel="back">Back</a>
</div>

We've found that, should the user get stuck on this screen and force-close the app, the button seems to work as expected in subsequent use.

So my current theory is that the header is somehow getting in the way of the back button (sometimes). The fact that it's not every time makes me not really believe this theory completely, however.

The button has its z-index set to 10 and the header's z-index is left at the default (which is 1, correct?). So even if it were the header getting in the way, my understanding is that the z-index of 10 should put the button "on top" and give it the first opportunity to receive the click/tap event.

The app uses jQueryMobile 1.3.1 because of the timing of when and how it was created, and upgrading is not a reasonable option at this moment. (There have been a significant number of breaking changes in 1.4.x)

I have debugged the app on both iOS and Android and no JavaScript errors are thrown. I'm at a total loss for what to do. This app is in the process of rolling out to thousands of users and there's a high likelihood that many, possibly most, will run into this bug. I would rather not have to explain (with my tail between my legs) that force-closing is the only way to fix this... but that's what I've been doing so far.

Does anyone have any advice or ideas on how to fix this?


Update 1:

I've noticed while remote-debugging the app in Safari over USB that I can watch the classes on the back button change, when tapped, from ui-btn-up-e to ui-btn-hover-e briefly to ui-btn-down-e and back to ui-btn-up-e -- and yet the app is not going back! :(

And as requested, here is the CSS applied to the header, the H1, and the back button: (just the "computed" final values, not all of the intermediary overridden values)

header div:

-webkit-background-clip: border-box;
-webkit-background-origin: padding-box;
-webkit-background-size: auto;
background-attachment: scroll;
background-clip: border-box;
background-color: rgb(179, 27, 27);
background-image: none;
background-origin: padding-box;
background-size: auto;
border-bottom-color: rgb(179, 27, 27);
border-bottom-style: solid;
border-bottom-width: 1px;
border-image-outset: 0px;
border-image-repeat: stretch;
border-image-slice: 100%;
border-image-source: none;
border-image-width: 1;
border-left-color: rgb(179, 27, 27);
border-left-style: solid;
border-left-width: 0px;
border-right-color: rgb(179, 27, 27);
border-right-style: solid;
border-right-width: 0px;
border-top-color: rgb(179, 27, 27);
border-top-style: solid;
border-top-width: 1px;
color: rgb(255, 255, 255);
display: block;
font-family: MyriadPro, Helvetica, sans-serif;
font-weight: bold;
height: 45px;
left: 0px;
padding-top: 1px;
position: fixed;
right: 0px;
text-shadow: rgb(85, 85, 85) 0px 1px 0px;
top: -1px;
width: 320px;
z-index: 1000;
zoom: 1;

H1:

color: rgb(255, 255, 255);
display: block;
font-family: MyriadPro, Helvetica, sans-serif;
font-size: 18px;
font-weight: bold;
height: 23px;
margin-bottom: 10px;
margin-left: 48px;
margin-right: 48px;
margin-top: 12px;
min-height: 19.799999237060547px;
outline-color: rgb(255, 255, 255);
outline-style: none;
outline-width: 0px;
overflow-x: hidden;
overflow-y: hidden;
padding-bottom: 0px;
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
text-align: center;
text-overflow: ellipsis;
text-shadow: rgb(85, 85, 85) 0px 1px 0px;
white-space: nowrap;
width: 224px;
zoom: 1;

Back button:

-webkit-background-clip: border-box;
-webkit-background-origin: padding-box;
-webkit-background-size: auto;
-webkit-box-shadow: none;
background-attachment: scroll;
background-clip: border-box;
background-color: rgb(179, 27, 27);
background-image: none;
background-origin: padding-box;
background-size: auto;
border-bottom-color: rgb(179, 27, 27);
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom-style: solid;
border-bottom-width: 0px;
border-image-outset: 0px;
border-image-repeat: stretch;
border-image-slice: 100%;
border-image-source: none;
border-image-width: 1;
border-left-color: rgb(179, 27, 27);
border-left-style: solid;
border-left-width: 0px;
border-right-color: rgb(179, 27, 27);
border-right-style: solid;
border-right-width: 0px;
border-top-color: rgb(179, 27, 27);
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-top-style: solid;
border-top-width: 0px;
box-shadow: none;
color: rgb(255, 255, 255);
cursor: pointer;
display: block;
font-family: MyriadPro, Helvetica, sans-serif;
font-weight: bold;
height: 31px;
left: 5px;
margin-bottom: 0px;
margin-left: 0px;
margin-right: 0px;
margin-top: 0px;
padding-bottom: 0px;
padding-left: 0px;
padding-right: 0px;
padding-top: 0px;
position: absolute;
text-align: center;
text-decoration: none solid rgb(255, 255, 255);
text-shadow: rgb(0, 0, 0) 0px 1px 0px;
top: 15px;
vertical-align: middle;
width: 60px;
z-index: 10;
zoom: 1;
Adam Tuttle
  • 19,505
  • 17
  • 80
  • 113
  • Can you show us the CSS applied to the header and the h1? – graphicdivine May 30 '14 at 14:22
  • Also note that I can see the CSS responding to the tap, but the navigation is not taking place. I described this a bit in update1, too. – Adam Tuttle May 30 '14 at 14:41
  • What if you remove this: data-tap-toggle="false". Same behaviour? – graphicdivine May 30 '14 at 14:46
  • Yeah, that doesn't fix it and adds additional undesired behavior of making the header disappear (if you tap in the content of the screen). – Adam Tuttle May 30 '14 at 15:09
  • CSS looks OK. Hard to debug from here, though. Can you get a working example online somehow? – graphicdivine May 30 '14 at 15:34
  • I don't think so. It only happens on-device. – Adam Tuttle May 30 '14 at 15:50
  • Per http://stackoverflow.com/questions/16033416/while-scrolling-on-an-ios-device-the-z-index-of-elements-isnt-working and http://css-tricks.com/forums/topic/safari-for-ios-z-index-ordering-bug-while-scrolling-a-page-with-a-fixed-element/ I tried setting z-index on the header to -10, and adding `-webkit-transform: translate3d(0,0,0);` to the button (individually and at the same time) and none of those 3 attempts fixed it either. – Adam Tuttle May 30 '14 at 15:52
  • btw, fixed toolbars have `z-index` set to 1000. – Omar Jun 02 '14 at 09:56
  • Thanks Omar, I just tried setting header buttons to `z-index: 1050` and that didn't fix it either. :o( – Adam Tuttle Jun 03 '14 at 01:12
  • 1
    Tell me one thing, do you (I mean your application) at any point programatically remove content from your app, for example pages. This error will occur if previous page is somehow removed from the DOM and navigation history is not changed accordingly. It this case, if you try to go back (and you are doing this with data-rel="back") application will get stuck. – Gajotres Jun 05 '14 at 13:03
  • No, we do not remove pages from the app. We do change page contents with, for example, `$("#foo").empty();` but all pages exist for the entire lifetime of the application and we only inject/remove _content_ as necessary. – Adam Tuttle Jun 05 '14 at 16:55

1 Answers1

4

First let me tell you that your problem probably don't have nothing to do with button/header CSS. If this error is showing sporadically and only during back function your problem is happening probably due to data-rel="back" functionality.

Unlike classic href page movement, data-rel="back" uses javascript navigation history which may or may not work depending on your app content. Because jQuery Mobile uses AJAX for page handling (and from your button I can see you are using this method) it also has its own way of handling navigation history.

Now, lets say your application heavily uses dynamic content generation (including content removal), and for example previous page is removed from the DOM, in this case data-rel="back" will not work. If previous page don't exist you will not be able to move back. There are several other factors that can also affect this functionality.

What you can do to solve this problem:

  1. Give this button a class name or even an id. After that bind a click event to it (vclick even better because it is as fast as tap, without 300ms delay, and works on desktop and mobile browsers a like) and add a console output each time you click on this button. So you would do it like this:

    $(document).on('vclick', '#button-ID-goes-here', function(){ 
        console.log('Problematic button');
    });
    

    This way you can test if button has been clicked.

  2. Next thing is little bit complicated. You never posted your page HTML but looking at your page image I think you are using correct jQuery Mobile paradigm, basically you are using data-role="page" div containers as page containers. You would be amazed how many jQuery Mobile developers don't know/understand they need/must have it if they want a proper jQuery Mobile app.

    Take a look at a page that holds this button in its header. Bind a pagebeforeshow event to that page and monitor will it trigger, and if it triggers you can use it to show previous and next page.

    Working example: http://jsfiddle.net/Gajotres/vds2U/72/

    $(document).on('pagebeforeshow', '.ui-page',function (e, data) {
        alert("Previous page : " + data.prevPage.attr('id') + " - Next page: " + $.mobile.activePage.attr('id'));
    }); 
    

    Use this solution to check if next page is correct and if it even exist. If this page event is not triggering when you click back button then something bad is happening.

  3. Use this next code as a workaround. Basically use page events and remember your previous page. Because you are currently using back button this solution will be viable here. We will use pagebeforeshow event to find out our previous page, we will store it into some JavaScript object (it will persist between page transitions).

    Working example: http://jsfiddle.net/Gajotres/vds2U/73/

    $(document).on('pagebeforeshow', '#second',function (e, data) {
        pageHandler.prevPage = data.prevPage.attr('id');
    
        $(document).on('click', '#custom-back-button',function () {
            $.mobile.changePage( "#"+pageHandler.prevPage);
        });    
    }); 
    
    var pageHandler = {
        prevPage : null
    }
    

    This is complete custom solution to data-rel="back" page handling.

Gajotres
  • 57,309
  • 16
  • 102
  • 130
  • Thanks for the suggestions. I am definitely using `
    `. I'll try the other things and get back to you...
    – Adam Tuttle Jun 05 '14 at 15:51
  • Preliminary tests look good. It seems to be working on my personal devices. Need to get some wider testing before I can accept this, but it's looking good. I can't believe this works, considering the circumstances, but I will just be happy that it does. (I'll update the question with more details soon...) – Adam Tuttle Jun 05 '14 at 16:57