jquery-mobile/js/jquery.mobile.fixHeaderFooter.js
Kin Blas c910f4b01f Fix for issues:
470 - Multiple Footers when using data-position=fixed and data-id=[someid]

773 - The fixHeaderFooter pagebeforeshow live function is not triggered for the initial page shown

- Removed the findStickyFooter() function.

- Reworked the logic in the pagebeforeshow live function. We now add a ui-sticky-footer class to the sticky footer element. This will guarantee that we find the sticky footer if it already exists. We may at some point want to consider the use of ids on the sticky footers so that we don't have to crawl the entire document.

- Moved the live pagebeforeshow and pageshow calls outside of the DOMReady function so that they get registered as soon as the plugin is loaded. This guarantees that they will be triggered when the core plugin's DOMReady function fires.

- This fix makes issue/pull-request 765 obsolete.
2011-01-07 01:07:22 -08:00

215 lines
No EOL
7.2 KiB
JavaScript

/*
* jQuery Mobile Framework : "fixHeaderFooter" plugin - on-demand positioning for headers,footers
* Copyright (c) jQuery Project
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*/
(function($, undefined ) {
$.fn.fixHeaderFooter = function(options){
if( !$.support.scrollTop ){ return $(this); }
return $(this).each(function(){
if( $(this).data('fullscreen') ){ $(this).addClass('ui-page-fullscreen'); }
$(this).find('.ui-header[data-position="fixed"]').addClass('ui-header-fixed ui-fixed-inline fade'); //should be slidedown
$(this).find('.ui-footer[data-position="fixed"]').addClass('ui-footer-fixed ui-fixed-inline fade'); //should be slideup
});
};
//single controller for all showing,hiding,toggling
$.fixedToolbars = (function(){
if( !$.support.scrollTop ){ return; }
var currentstate = 'inline',
delayTimer,
ignoreTargets = 'a,input,textarea,select,button,label,.ui-header-fixed,.ui-footer-fixed',
toolbarSelector = '.ui-header-fixed:first, .ui-footer-fixed:not(.ui-footer-duplicate):last',
stickyFooter, //for storing quick references to duplicate footers
supportTouch = $.support.touch,
touchStartEvent = supportTouch ? "touchstart" : "mousedown",
touchStopEvent = supportTouch ? "touchend" : "mouseup",
stateBefore = null,
scrollTriggered = false,
touchToggleEnabled = true;
$(function() {
$(document)
.bind(touchStartEvent,function(event){
if( touchToggleEnabled ) {
if( $(event.target).closest(ignoreTargets).length ){ return; }
stateBefore = currentstate;
}
})
.bind('scrollstart',function(event){
if( $(event.target).closest(ignoreTargets).length ){ return; } //because it could be a touchmove...
scrollTriggered = true;
if(stateBefore == null){ stateBefore = currentstate; }
if (stateBefore == 'overlay') {
$.fixedToolbars.hide(true);
}
})
.bind(touchStopEvent,function(event){
if( touchToggleEnabled ) {
if( $(event.target).closest(ignoreTargets).length ){ return; }
if( !scrollTriggered ){
$.fixedToolbars.toggle(stateBefore);
stateBefore = null;
}
}
})
.bind('scrollstop',function(event){
if( $(event.target).closest(ignoreTargets).length ){ return; }
scrollTriggered = false;
if (stateBefore == 'overlay') {
$.fixedToolbars.show();
}
stateBefore = null;
});
});
//before page is shown, check for duplicate footer
$('.ui-page').live('pagebeforeshow', function(event, ui){
var page = $(event.target);
var footer = page.find('[data-role="footer"]:not(.ui-sticky-footer)');
var id = footer.data('id');
if (id)
{
stickyFooter = $('.ui-footer[data-id="' + id + '"].ui-sticky-footer');
if (stickyFooter.length == 0) {
// No sticky footer exists for this data-id. We'll use this
// footer as the sticky footer for the group and then create
// a placeholder footer for the page.
stickyFooter = footer;
footer = stickyFooter.clone(); // footer placeholder
stickyFooter.addClass('ui-sticky-footer').before(footer);
}
footer.addClass('ui-footer-duplicate');
stickyFooter.appendTo($.pageContainer).css('top',0);
setTop(stickyFooter);
}
});
//after page is shown, append footer to new page
$('.ui-page').live('pageshow', function(event, ui){
if( stickyFooter && stickyFooter.length ){
stickyFooter.appendTo(event.target).css('top',0);
}
$.fixedToolbars.show(true, this);
});
// element.getBoundingClientRect() is broken in iOS 3.2.1 on the iPad. The
// coordinates inside of the rect it returns don't have the page scroll position
// factored out of it like the other platforms do. To get around this,
// we'll just calculate the top offset the old fashioned way until core has
// a chance to figure out how to handle this situation.
//
// TODO: We'll need to get rid of getOffsetTop() once a fix gets folded into core.
function getOffsetTop(ele)
{
var top = 0;
if (ele)
{
var op = ele.offsetParent, body = document.body;
top = ele.offsetTop;
while (ele && ele != body)
{
top += ele.scrollTop || 0;
if (ele == op)
{
top += op.offsetTop;
op = ele.offsetParent;
}
ele = ele.parentNode;
}
}
return top;
}
function setTop(el){
var fromTop = $(window).scrollTop(),
thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
thisCSStop = el.css('top') == 'auto' ? 0 : parseFloat(el.css('top')),
screenHeight = window.innerHeight,
thisHeight = el.outerHeight(),
useRelative = el.parents('.ui-page:not(.ui-page-fullscreen)').length,
relval;
if( el.is('.ui-header-fixed') ){
relval = fromTop - thisTop + thisCSStop;
if( relval < thisTop){ relval = 0; }
return el.css('top', ( useRelative ) ? relval : fromTop);
}
else{
//relval = -1 * (thisTop - (fromTop + screenHeight) + thisCSStop + thisHeight);
//if( relval > thisTop ){ relval = 0; }
relval = fromTop + screenHeight - thisHeight - (thisTop - thisCSStop);
return el.css('top', ( useRelative ) ? relval : fromTop + screenHeight - thisHeight );
}
}
//exposed methods
return {
show: function(immediately, page){
currentstate = 'overlay';
var $ap = page ? $(page) : ($.mobile.activePage ? $.mobile.activePage : $(".ui-page-active"));
return $ap.children( toolbarSelector ).each(function(){
var el = $(this),
fromTop = $(window).scrollTop(),
thisTop = getOffsetTop(el[0]), // el.offset().top returns the wrong value on iPad iOS 3.2.1, call our workaround instead.
screenHeight = window.innerHeight,
thisHeight = el.outerHeight(),
alreadyVisible = (el.is('.ui-header-fixed') && fromTop <= thisTop + thisHeight) || (el.is('.ui-footer-fixed') && thisTop <= fromTop + screenHeight);
//add state class
el.addClass('ui-fixed-overlay').removeClass('ui-fixed-inline');
if( !alreadyVisible && !immediately ){
el.animationComplete(function(){
el.removeClass('in');
}).addClass('in');
}
setTop(el);
});
},
hide: function(immediately){
currentstate = 'inline';
var $ap = $.mobile.activePage ? $.mobile.activePage : $(".ui-page-active");
return $ap.children( toolbarSelector ).each(function(){
var el = $(this);
var thisCSStop = el.css('top'); thisCSStop = thisCSStop == 'auto' ? 0 : parseFloat(thisCSStop);
//add state class
el.addClass('ui-fixed-inline').removeClass('ui-fixed-overlay');
if (thisCSStop < 0 || (el.is('.ui-header-fixed') && thisCSStop != 0))
{
if(immediately){
el.css('top',0);
}
else{
if( el.css('top') !== 'auto' && parseFloat(el.css('top')) !== 0 ){
var classes = 'out reverse';
el.animationComplete(function(){
el.removeClass(classes);
el.css('top',0);
}).addClass(classes);
}
}
}
});
},
hideAfterDelay: function(){
delayTimer = setTimeout(function(){
$.fixedToolbars.hide();
}, 3000);
},
toggle: function(from){
if(from){ currentstate = from; }
return (currentstate == 'overlay') ? $.fixedToolbars.hide() : $.fixedToolbars.show();
},
setTouchToggleEnabled: function(enabled) {
touchToggleEnabled = enabled;
}
};
})();
})(jQuery);