jquery-archive / jquery-mobile

jQuery Mobile Framework
https://jquerymobile.com
Other
9.68k stars 2.4k forks source link

Content on page transition with wrong layout when using fixed header with image #4652

Closed MasterJames closed 12 years ago

MasterJames commented 12 years ago

Right I guess the title says it all? I have PhoneGap jquery mobile with a fixed header. (which when the header image loads I have to trigger a update layout or the image hides content of page one). When I slide to a page transition (had to override in newest version from fade), the content is hidden behind the fixed header dynamically resized by the image (who's width and size is dynamic because of width 100% and a max-width etc.) If I go back the reverse transition and the content looks right as it slides away. When I go to that page again it's correct. If I turn off fixed headers it's also okay. Note: We'd like a title header. It looks silly sliding in the same image header for each page transition and like the footer with ad banner should stay fixed during page changes. It was trouble to get my default expectation done right within the confines and solutions for this (data-id="mainheader" data-position="fixed" which leaves duplicate code on multiple pages?!) but it's buggy and might have to do away with all jQuery Mobile page transitions, or no more fixed headers ;-(. I tried something like this (as it helped for the headers image load event for first page, which doesn't help other pages either)... $('div.pages').bind('pagecreate', function() { $(this).trigger('updatelayout'); }); but it made no difference neither with pageshow etc. Still something happens when it slides back?! I'd like to call that in pagebeforeshow or whatever would fix the problem?! clearly a bug though right?

jaspermdegroot commented 12 years ago

@MasterJames

Please provide a simple test page that illustrates the issue. See https://github.com/jquery/jquery-mobile#issues. Did you set height and width for the image in the header?

jaspermdegroot commented 12 years ago

@MasterJames

We really need a test page to be able to look into your issue. Otherwise we have to close.

Thanks!

MasterJames commented 12 years ago

Hmmm... to the first comment; You'd need an image on the server for the header which via PhoneGap works for all orientations and devices (in theory) so there's no size?! it does have a max size in the CSS, but it's 100% basically otherwise I think.

It's just really hard to squeeze all my non disclosed plugins, css etc. into your "test page". I've taken a quick try and nothing is working with 1.7.2 on that test page. [All I see is a spinning icon and the footer up top.] I've been using a variety of things... fluid CSS like this... @media only screen and (min-width: 481px) { etc. <link href="boilerplate.css" rel="stylesheet" type="text/css"> <link href="css/MY_fluid-CSS.css" rel="stylesheet" type="text/css"> <link href="jquery.mobile-1.1.0/jquery.mobile-1.1.0.css" rel="stylesheet" type="text/css"/> <link href="css/themes/PrivateClientsStuff.min.css" rel="stylesheet" type="text/css"> <!--[if lt IE 9]> <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <script src="jquery.mobile-1.1.0/jquery-1.7.2.js" type="text/javascript"></script> <script src="jquery.mobile-1.1.0/overrides.js" type="text/javascript"></script> <script src="jquery.mobile-1.1.0/jquery.mobile-1.1.0.js" type="text/javascript"></script>

<script src="GreenSock/TweenMax.min.js" type="text/javascript"></script> <script src="js/Utilities/Utilities.js" type="text/javascript"></script> <script src="js/MyAdNetwork-0.1.0.js" type="text/javascript"></script> <script src="js/PrivateClientPlugin-0.1.0.js" type="text/javascript"></script>

Well I'll burn an hour or two trying further but I doubt I can help you reproduce this (obvious to me and seems like a major) bug?! I'd assumed someone in the forum would be well familiar with this problem, and a couple of weeks would result in something more... Again my first impression is Headers and Footers should only exists once and be static and the content changes. It's seems like an after though, wasteful as it's duplicated and redundantly hacked for headers & footers to work. It appears as if they actually disappear after not animating to reveal a duplicate header underneath. So it seems static headers and footers is totally being done wrong. If it's static there should be only one entry needed. I think that's at the root of it's dysfunction. Cause it clearly doesn't update the content section's top after the image in the duplicate banner loads/resizes (stomping over the content area).

MasterJames commented 12 years ago

Well maybe if this would work at all it might show the basic problem?!... http://jsbin.com/awoluv/165/edit I've kept the wrapper and footer externally for now as it's part of Fluid Grid logic etc. I had to remove the first "fixed" tag to get it to work on this end temporarily. So the header currently on this end slides away revealing a static header underneath and then the content is fine. I wonder if it's loading duplicate headers for every page on startup. That would explain some of the startup delay to.

jaspermdegroot commented 12 years ago

@MasterJames

I had a look at your test page. You set width: 100%; on the header image on the first page, but not on page 2, 3 and 4. You don't set a height for the image at all. Although a fixed header with same data-id appears to stay in view it is actually replaced by the header of the page you navigate to. I don't understand what you mean by "which leaves duplicate code on multiple pages". Duplicate data-id is not the same as duplicate id. Only the latter has to be avoided.

I added a class to the header image on all four pages and set width: 100%; height: auto; for it and then the header image is rendered the same on every page and doesn't obscure the content: http://jsbin.com/awoluv/166/edit#html There is no need to call updateLayout and the build in updatePagePadding function makes sure the padding-top on the page is the same as the height of the header.

I also noticed you use div[data-role="content"] twice on the first page. Not sure if it could cause problems, but it is not really intended to be used that way. It also mean you have two divs with role="main" on the same page, where there is supposed to be only one.

I am closing this as solved. If I missed something, or if setting width and height for the header image on all pages doesn't solve it in your actual PhoneGap project, please comment and we will reopen.

MasterJames commented 12 years ago

Oh ya no that slipped in there I never actually did that everything css anyway. It was cause of the other comments. It never ran for me anyway, not on the tester page or mine here. (Ohps sorry.) The div data-content duplicates are part of the fluid css where they hide and display the different content based on screen size (orientation etc.) I added to the css the height: auto; for the header and I still have the same problem here. I'll look into it further and try and reproduce again. thanks for your help so far, but let's not close this just yet.

jaspermdegroot commented 12 years ago

Reopen because there still seems to be an issue.

@MasterJames - Can you describe in short what is the actual issue, because it is not really clear to me. Thanks!

MasterJames commented 12 years ago

Well I'm adding the css for fluid grid into the test page... http://jsbin.com/awoluv/169/edit but that hasn't revealed the problem yet. basically the content on page two is hidden under the logo unless you go back and then the second time it's top is aligned with the bottom of the header, otherwise the first load it's top is aligned with what I suppose would have been the bottom if there was no image in the header... it's hard to say because the content is hidden... underneath... the header. The header is too big if I use Auto anyway and it wasn't the cause so I'm not doing that now either. As explained originally when you click to go back you then see the content appear aligned and then zip off.

MasterJames commented 12 years ago

I noticed this on the site trying to find 1.1.0? jQuery Mobile 1.1.0 Final Requires jQuery core 1.6.4 or 1.7.1 but I'm using 1.7.2 could that be related? Here it is... http://code.jquery.com/mobile/1.1.0/jquery.mobile-1.1.0.min.js and http://jsbin.com/awoluv/174/edit ohp sorry now added the oficiall 1.1.0 CSS link to be sure it's the same things. http://jsbin.com/awoluv/176/edit It doesn't run in test page but it does for me, so again not sure how to debug it thus far. Sorry this version I made the image bigger via google so that the small size of the other one doesn't affect result. http://jsbin.com/awoluv/178/edit Seems there was also a 1.1.0 css that's a custom minified theme but it's doesn't work without both of them right here on this end... http://jsbin.com/awoluv/179/edit Either way it doesn't work anymore on the test page.

jaspermdegroot commented 12 years ago

@MasterJames

Ok, so the problem can be described as "content is placed underneath fixed header".

If you leave all the other (fluid grid) CSS and JS out, does the problem occur as well?

The updatePagePadding that I described in my previous comment should prevent content being obscured by the fixed header. If you inspect the code, is there padding-top on the div with class ui-page?

[Update:] Just to clarify; here at the issue tracker we look at bare test cases, to see if something is a bug in the framework. When it is about getting things to work when you combine JQM with all kinds of other things (boilerplate, fluid grid, etc.) the forum is the right place to ask for help.

MasterJames commented 12 years ago

Yes if I remove Fluid CSS and theme weird stuff happens but that problem still exists.

MasterJames commented 12 years ago

I have overrides... (but that's not important either... $(document).bind("mobileinit", function(){ $m = $.mobile; $.support.cors = true; $m.allowCrossDomainPages = true; $m.defaultPageTransition = "slide"; }); Otherwise TweenMax was added later so also not the problem nor is my Utilities which is simple jQuery Plugin patches for things like string prototype s key=value stuff etc. so I can remove all those and my custom plugins too... <script src="GreenSock/TweenMax.min.js" type="text/javascript"></script> <script src="js/Utilities/Utilities.js" type="text/javascript"></script> <script src="js/MyAdNetwork-0.1.0.js" type="text/javascript"></script> <script src="js/PrivateClientPlugin-0.1.0.js" type="text/javascript"></script>

So I strip down to just... jquery.mobile-1.1.0.css jquery-1.7.2.js jquery.mobile-1.1.0.js but I might have tweaked something there at one point? Okay I tried with the "min" versions so those would be clean also same problem.

MasterJames commented 12 years ago

You suggested this... "The updatePagePadding that I described in my previous comment should prevent content being obscured by the fixed header. If you inspect the code, is there padding-top on the div with class ui-page?" I missed that please explain?! I have entries in the jquery.mobile-1.1.0.css for ui-page but I never see or heard anything about that until now?

jaspermdegroot commented 12 years ago

Ok. This is the bare test page: http://jsbin.com/awoluv/183/ All custom CSS is removed.

updatePagePadding is an internal function that sets padding-top on div ui-page equal to the height of the fixed header. This ensures the content is not masked by the header. So this function checks the height of the header and therefor it is important to set the height and width of the image. If you don't do that the browser doesn't know the height of the header yet when updatePagePadding checks it and the padding is wrong.

At first I did see that issue happening. Adding display: block; for the image solved it.

MasterJames commented 12 years ago

Okay I have something to report. First off all none of your solutions worked in the end. Your mention of padding was workable. For now my patch is this... You need to have some custom events for page hide and pageshow associated with the pages in a jQuery plugin init call. Then you have to hold the fixed header in pagehide since in pageshow the header doesn't exist yet (first clue of possible breakage in jquery mobile). Then in pagehide I have to calculate like I actually do for my footer already the true width and or height within the restrictions of the img inside the div the screen size and the max-sizes... for now this un-purified code demonstrates that process... I'm just thinking now since it fixes itself you could put 'one' instead of 'bind'. Maybe this will give insight and some cleanup would be appreciated. Basically it needs to go into the jquery mobile code somewhere anyway. I noticed as explained that as soon as you go back it's corrected. With firebug I inspected and found the padding-top style was updated properly, where or how to trigger that on the first load I wonder, my guess is that because the header isn't appended after the content yet it doesn't incorporate it. ;

var setupUpdating = function() {
    $('div.pages').bind('pagehide', function() {
        s.lastHeader = $(this).find('[data-role="header"]');
    });
    $('div.pages').bind('pageshow', function() {
        var image = $(s.lastHeader).find('img');
        var cont = $(this).find('[data-role="content"]');
        var hh = findContentsHeight(image);
        $(this).css('padding-top', hh);
        //$(this).trigger('updatelayout');
    });
}

var findContentsHeight = function (image) {
    var height;
    if(image.length != 0) {
        height = image[0].naturalHeight;
        var width = image[0].naturalWidth;
        var mw = getTrueMaxInPixels(image, 'max-width', $(window).width());
        var mh = getTrueMaxInPixels(image, 'max-height', $(window).height());
        var sw = $(window).width();
        var sh = $(window).height();
        if(mw < width) {
            height = (height / (width / mw));
        }
        if(mh < height) {
            height = mh;
        }
    }
    //console.log("findContentsHeight: " + height);

    if(height == undefined) {
        return 0;
    }

    return height;
}

var getTrueMaxInPixels = function (image, maxName, screenVal) {
    var maxVal = image.css(maxName);
    var mv;
    if(maxVal.indexOf('%') != -1) {
        mv = (screenVal * (parseInt(maxVal) / 100));
    }
    else {
        if(maxVal != "none") {
            mv = parseInt(maxVal);
        }
        else {
            mv = screenVal;
        }
    }
    if(mv > screenVal) {
        mv = screenVal;
    }
    return mv;
}
MasterJames commented 12 years ago

So I'll now add you can't use 'one' instead of 'bind' because of multi-device, resize orientations, etc. and you want pagebeforehide and pagebeforeshow

jaspermdegroot commented 12 years ago

@MasterJames

I understand that you try to make things work for your project, but we should focus on establishing if there is a bug in the framework yes or no.

My questions:

  1. What do you mean by "none of your solutions worked in the end"?
  2. What do you mean by "Your mention of padding was workable"?

I like to repeat here that you should leave out all other CSS and JS that you want to implement and just test with JQM and PhoneGap

Can you please answer my two questions in short? Thanks!

MasterJames commented 12 years ago

Well I've modified various code on this end now (and still need to do more) but the example code given is the only way I was able to get it to work. I've had to customize code to explicitly redefine the "padding-top" value in the initial page's pagebeforeshow event. You're final suggestion of display: block; or something like that was probably thought to work because it always has put a padding-top after the first load returns 'back', which was the hint I used to inspect that that is the nature of this problem. It's particularly difficult to determine that value with banner images, not loaded/ready, min-max widths, screen-widths and 100% width and image widths, or their naturalWidth's etc. It's also come to my attention that image.ready doesn't always mean the image is loaded (not cached) and showing it has size yet, and I have to test for that and bind once to the load events manually as well in another case (footer banners).

Anyway this problem is closed for me but I will leave it open for you to hopefully do something about it so it isn't living hell using this stuff for others as it has turned out for me. I suppose it needs to be since it's super dynamic, but I'll just say AS3 was a better way for me to deploy on multiple platforms although I like (and still feel) this route has promise.

Thanks for your help. I'll just add I usually don't use forums unless there's something really wrong going on. I don't think this problem is related to my specific rare situation, and probably pretty easily reproducible. I think you said you did notice, and therefore were able to reproduce the problem more recently, so that should be enough to help fix jquery-mobile in this regard. I'll remain hopeful you will be able to post more on the issue, and show me a better solution. With that in mind as I think the original post inquires a question I'll leave you with (slightly revised), "What method would I call in pagebeforeshow that triggers the correct behavior of setting padding-top as it does when I leave the page (via any back action)?". This ultimate must relate to the header not truly being ready. Okay one more thought (as it may be more the real problem here)... If I load html into a div that contains an IMG SRC (not yet cached and remote [running slowly in a mobile via phonegap]{as an ad banner system needs to}), and the image is not really loaded but the ready() event will fire since only the html is ready (I suspect), that's probably a wrong behavior as it can't initially calculate the sizes yet. It would need to bind once to the load event of the image(s) in that dynamically loaded html of the div and then resize everything again then. I suspect if you fixed that (which might be a bug in jq 1.7.2 or something not part of jq-mobile) then this might disappear.

MasterJames commented 12 years ago

Well this is related but really a separate topic. I figured for clarity on my previous comment I better ammend with new information. It seems "load" won't fire?!@.... I'm now calling setTimeout with 888 to recall the method/function when it finds image size to have zero. My NaN error was caused by this in a separate Utility. I'm not sure why when an image is "ready" it's called it's "load" and it's still has a naturalSize of zero, but maybe that is causing the size calculation error. I'd definitely look into that problem first, as it's my suspicion still this problem will go away when ready really means ready. Maybe this is actually an initmobile vs doc.ready problem on my end? I'm still looking into it. It also seems to find the images with size zero even though they should be cached, and again I'm looking at naturalWidth and naturalHeight. his happens randomly... imageSize.width = image[0].naturalWidth; imageSize.height = image[0].naturalHeight; a break point here and when inspecting the values shows (while hovering in firebug) the correct value on image[0].naturalHeight but hovering/inspecting one sees it's set to zero on imageSize.height?!? Extremely strange, pretty obviously it's updated shortly after, despite the breakpoint. I can add that building a simple inline pause command like this... var milliDelay = function (delay) { var goal = (new Date().getTime() + parseInt(delay)); var cur = new Date().getTime(); while (goal > cur) { cur = new Date().getTime(); } };

will not help for any amount of time. setTimeout however does. upon return the size is no longer zero for already loaded images.

arschmitz commented 12 years ago

@MasterJames when you say the content is placed under the header do you mean the content div comes after header div in you look at them in firebug or chrome dev tools? if so this is a duplicate of issue #4019 https://github.com/jquery/jquery-mobile/issues/4019 this is fixed.

jaspermdegroot commented 12 years ago

@arschmitz - That was the case. There was also an issue with the height of the header not adjusting to the height of the image in it, but that's not a framework bug. Closing.