mirror of
https://github.com/Hopiu/jquery-mobile.git
synced 2026-04-21 06:40:59 +00:00
Merge pull request #1959 from rwldrn/jquery.mobile.fixHeaderFooter.js
jQuery core style guide conformance: jquery.mobile.fixHeaderFooter.js
This commit is contained in:
commit
a68cdd9f33
1 changed files with 227 additions and 148 deletions
|
|
@ -4,29 +4,45 @@
|
|||
* 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(){
|
||||
var $this = $(this);
|
||||
|
||||
if( $this.jqmData('fullscreen') ){ $this.addClass('ui-page-fullscreen'); }
|
||||
$this.find( ".ui-header:jqmData(position='fixed')" ).addClass('ui-header-fixed ui-fixed-inline fade'); //should be slidedown
|
||||
$this.find( ".ui-footer:jqmData(position='fixed')" ).addClass('ui-footer-fixed ui-fixed-inline fade'); //should be slideup
|
||||
|
||||
(function( $, undefined ) {
|
||||
|
||||
$.fn.fixHeaderFooter = function( options ) {
|
||||
|
||||
if ( !$.support.scrollTop ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
return this.each(function() {
|
||||
var $this = $( this );
|
||||
|
||||
if ( $this.jqmData( "fullscreen" ) ) {
|
||||
$this.addClass( "ui-page-fullscreen" );
|
||||
}
|
||||
|
||||
// Should be slidedown
|
||||
$this.find( ".ui-header:jqmData(position='fixed')" ).addClass( "ui-header-fixed ui-fixed-inline fade" );
|
||||
|
||||
// Should be slideup
|
||||
$this.find( ".ui-footer:jqmData(position='fixed')" ).addClass( "ui-footer-fixed ui-fixed-inline fade" );
|
||||
});
|
||||
};
|
||||
|
||||
//single controller for all showing,hiding,toggling
|
||||
$.fixedToolbars = (function(){
|
||||
if( !$.support.scrollTop ){ return; }
|
||||
var currentstate = 'inline',
|
||||
//single controller for all showing,hiding,toggling
|
||||
$.fixedToolbars = (function() {
|
||||
|
||||
if ( !$.support.scrollTop ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentstate = "inline",
|
||||
autoHideMode = false,
|
||||
showDelay = 100,
|
||||
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
|
||||
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",
|
||||
//for storing quick references to duplicate footers
|
||||
stickyFooter,
|
||||
supportTouch = $.support.touch,
|
||||
touchStartEvent = supportTouch ? "touchstart" : "mousedown",
|
||||
touchStopEvent = supportTouch ? "touchend" : "mouseup",
|
||||
|
|
@ -34,7 +50,7 @@ $.fixedToolbars = (function(){
|
|||
scrollTriggered = false,
|
||||
touchToggleEnabled = true;
|
||||
|
||||
function showEventCallback(event) {
|
||||
function showEventCallback( event ) {
|
||||
// An event that affects the dimensions of the visual viewport has
|
||||
// been triggered. If the header and/or footer for the current page are in overlay
|
||||
// mode, we want to hide them, and then fire off a timer to show them at a later
|
||||
|
|
@ -44,103 +60,125 @@ $.fixedToolbars = (function(){
|
|||
//
|
||||
// If we are in autoHideMode, we don't do anything because we know the scroll
|
||||
// callbacks for the plugin will fire off a show when the scrolling has stopped.
|
||||
if (!autoHideMode && currentstate == 'overlay') {
|
||||
if (!delayTimer)
|
||||
$.fixedToolbars.hide(true);
|
||||
if ( !autoHideMode && currentstate === "overlay" ) {
|
||||
if ( !delayTimer ) {
|
||||
$.fixedToolbars.hide( true );
|
||||
}
|
||||
|
||||
$.fixedToolbars.startShowTimer();
|
||||
}
|
||||
}
|
||||
|
||||
$(function() {
|
||||
$(document)
|
||||
.bind( "vmousedown",function(event){
|
||||
if( touchToggleEnabled ) {
|
||||
var $document = $( document ),
|
||||
$window = $(window);
|
||||
|
||||
$document
|
||||
.bind( "vmousedown", function( event ) {
|
||||
if ( touchToggleEnabled ) {
|
||||
stateBefore = currentstate;
|
||||
}
|
||||
})
|
||||
.bind( "vclick",function(event){
|
||||
if( touchToggleEnabled ) {
|
||||
if( $(event.target).closest(ignoreTargets).length ){ return; }
|
||||
if( !scrollTriggered ){
|
||||
$.fixedToolbars.toggle(stateBefore);
|
||||
.bind( "vclick", function( event) {
|
||||
if ( touchToggleEnabled ) {
|
||||
|
||||
if ( $(event.target).closest( ignoreTargets ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !scrollTriggered ) {
|
||||
$.fixedToolbars.toggle( stateBefore );
|
||||
stateBefore = null;
|
||||
}
|
||||
}
|
||||
})
|
||||
.bind('silentscroll', showEventCallback);
|
||||
.bind( "silentscroll", showEventCallback );
|
||||
|
||||
/*
|
||||
The below checks first for a $(document).scrollTop() value, and if zero, binds scroll events to $(window) instead. If the scrollTop value is actually zero, both will return zero anyway.
|
||||
|
||||
Works with $(document), not $(window) : Opera Mobile (WinMO phone; kinda broken anyway)
|
||||
Works with $(window), not $(document) : IE 7/8
|
||||
Works with either $(window) or $(document) : Chrome, FF 3.6/4, Android 1.6/2.1, iOS
|
||||
Needs work either way : BB5, Opera Mobile (iOS)
|
||||
// The below checks first for a $(document).scrollTop() value, and if zero, binds scroll events to $(window) instead.
|
||||
// If the scrollTop value is actually zero, both will return zero anyway.
|
||||
//
|
||||
// Works with $(document), not $(window) : Opera Mobile (WinMO phone; kinda broken anyway)
|
||||
// Works with $(window), not $(document) : IE 7/8
|
||||
// Works with either $(window) or $(document) : Chrome, FF 3.6/4, Android 1.6/2.1, iOS
|
||||
// Needs work either way : BB5, Opera Mobile (iOS)
|
||||
|
||||
*/
|
||||
( ( $document.scrollTop() === 0 ) ? $window : $document )
|
||||
.bind( "scrollstart", function( event ) {
|
||||
|
||||
(( $(document).scrollTop() === 0 ) ? $(window) : $(document))
|
||||
.bind('scrollstart',function(event){
|
||||
scrollTriggered = true;
|
||||
if(stateBefore === null){ stateBefore = currentstate; }
|
||||
|
||||
if ( stateBefore === null ) {
|
||||
stateBefore = currentstate;
|
||||
}
|
||||
|
||||
// We only enter autoHideMode if the headers/footers are in
|
||||
// an overlay state or the show timer was started. If the
|
||||
// show timer is set, clear it so the headers/footers don't
|
||||
// show up until after we're done scrolling.
|
||||
var isOverlayState = stateBefore == 'overlay';
|
||||
var isOverlayState = stateBefore == "overlay";
|
||||
|
||||
autoHideMode = isOverlayState || !!delayTimer;
|
||||
if (autoHideMode){
|
||||
|
||||
if ( autoHideMode ) {
|
||||
$.fixedToolbars.clearShowTimer();
|
||||
if (isOverlayState) {
|
||||
$.fixedToolbars.hide(true);
|
||||
|
||||
if ( isOverlayState ) {
|
||||
$.fixedToolbars.hide( true );
|
||||
}
|
||||
}
|
||||
})
|
||||
.bind('scrollstop',function(event){
|
||||
if( $(event.target).closest(ignoreTargets).length ){ return; }
|
||||
.bind( "scrollstop", function( event ) {
|
||||
|
||||
if ( $( event.target ).closest( ignoreTargets ).length ) {
|
||||
return;
|
||||
}
|
||||
|
||||
scrollTriggered = false;
|
||||
if (autoHideMode) {
|
||||
autoHideMode = false;
|
||||
|
||||
if ( autoHideMode ) {
|
||||
$.fixedToolbars.startShowTimer();
|
||||
autoHideMode = false;
|
||||
}
|
||||
stateBefore = null;
|
||||
});
|
||||
|
||||
$(window).bind('resize', showEventCallback);
|
||||
});
|
||||
|
||||
//before page is shown, check for duplicate footer
|
||||
$('.ui-page').live('pagebeforeshow', function(event, ui){
|
||||
var page = $(event.target),
|
||||
footer = page.find( ":jqmData(role='footer')" ),
|
||||
id = footer.data('id'),
|
||||
prevPage = ui.prevPage,
|
||||
prevFooter = prevPage && prevPage.find( ":jqmData(role='footer')" ),
|
||||
prevFooterMatches = prevFooter.length && prevFooter.jqmData( "id" ) === id;
|
||||
|
||||
if( id && prevFooterMatches ){
|
||||
stickyFooter = footer;
|
||||
setTop( stickyFooter.removeClass( "fade in out" ).appendTo( $.mobile.pageContainer ) );
|
||||
}
|
||||
$window.bind( "resize", showEventCallback );
|
||||
});
|
||||
|
||||
//after page is shown, append footer to new page
|
||||
$('.ui-page').live('pageshow', function(event, ui){
|
||||
var $this = $(this);
|
||||
|
||||
if( stickyFooter && stickyFooter.length ){
|
||||
|
||||
setTimeout(function(){
|
||||
setTop( stickyFooter.appendTo( $this ).addClass("fade") );
|
||||
stickyFooter = null;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
$.fixedToolbars.show(true, this);
|
||||
});
|
||||
|
||||
//When a collapsiable is hidden or shown we need to trigger the fixed toolbar to reposition itself (#1635)
|
||||
// 1. Before page is shown, check for duplicate footer
|
||||
// 2. After page is shown, append footer to new page
|
||||
$( ".ui-page" )
|
||||
.live( "pagebeforeshow", function( event, ui ) {
|
||||
|
||||
var page = $( event.target ),
|
||||
footer = page.find( ":jqmData(role='footer')" ),
|
||||
id = footer.data( "id" ),
|
||||
prevPage = ui.prevPage,
|
||||
prevFooter = prevPage && prevPage.find( ":jqmData(role='footer')" ),
|
||||
prevFooterMatches = prevFooter.length && prevFooter.jqmData( "id" ) === id;
|
||||
|
||||
if ( id && prevFooterMatches ) {
|
||||
stickyFooter = footer;
|
||||
setTop( stickyFooter.removeClass( "fade in out" ).appendTo( $.mobile.pageContainer ) );
|
||||
}
|
||||
})
|
||||
.live( "pageshow", function( event, ui ) {
|
||||
|
||||
var $this = $( this );
|
||||
|
||||
if ( stickyFooter && stickyFooter.length ) {
|
||||
|
||||
setTimeout(function() {
|
||||
setTop( stickyFooter.appendTo( $this ).addClass( "fade" ) );
|
||||
stickyFooter = null;
|
||||
}, 500);
|
||||
}
|
||||
|
||||
$.fixedToolbars.show( true, this );
|
||||
});
|
||||
|
||||
//When a collapsiable is hidden or shown we need to trigger the fixed toolbar to reposition itself (#1635)
|
||||
$( ".ui-collapsible-contain" ).live( "collapse expand", showEventCallback );
|
||||
|
||||
// element.getBoundingClientRect() is broken in iOS 3.2.1 on the iPad. The
|
||||
|
|
@ -151,118 +189,159 @@ $.fixedToolbars = (function(){
|
|||
//
|
||||
// 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;
|
||||
function getOffsetTop( ele ) {
|
||||
var top = 0,
|
||||
op;
|
||||
|
||||
if ( ele ) {
|
||||
op = ele.offsetParent, body = document.body;
|
||||
top = ele.offsetTop;
|
||||
while (ele && ele != body) {
|
||||
|
||||
while ( ele && ele != body ) {
|
||||
top += ele.scrollTop || 0;
|
||||
if (ele == op){
|
||||
|
||||
if ( ele == op ) {
|
||||
top += op.offsetTop;
|
||||
op = ele.offsetParent;
|
||||
}
|
||||
|
||||
ele = ele.parentNode;
|
||||
}
|
||||
}
|
||||
return top;
|
||||
}
|
||||
|
||||
function setTop(el){
|
||||
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')),
|
||||
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,
|
||||
useRelative = el.parents( ".ui-page:not(.ui-page-fullscreen)" ).length,
|
||||
relval;
|
||||
if( el.is('.ui-header-fixed') ){
|
||||
|
||||
if ( el.is( ".ui-header-fixed" ) ) {
|
||||
|
||||
relval = fromTop - thisTop + thisCSStop;
|
||||
if( relval < thisTop){ relval = 0; }
|
||||
return el.css('top', ( useRelative ) ? relval : fromTop);
|
||||
|
||||
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 );
|
||||
// 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
|
||||
// Exposed methods
|
||||
return {
|
||||
show: function(immediately, page){
|
||||
|
||||
show: function( immediately, page ) {
|
||||
|
||||
$.fixedToolbars.clearShowTimer();
|
||||
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.
|
||||
|
||||
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');
|
||||
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);
|
||||
hide: function( immediately ) {
|
||||
|
||||
currentstate = "inline";
|
||||
|
||||
var $ap = $.mobile.activePage ? $.mobile.activePage :
|
||||
$( ".ui-page-active" );
|
||||
|
||||
return $ap.children( toolbarSelector ).each(function() {
|
||||
|
||||
var el = $(this),
|
||||
thisCSStop = el.css( "top" ),
|
||||
classes;
|
||||
|
||||
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 ) {
|
||||
|
||||
classes = "out reverse";
|
||||
|
||||
el.animationComplete(function() {
|
||||
el.removeClass( classes ).css( "top", 0 );
|
||||
}).addClass( classes );
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
startShowTimer: function(){
|
||||
|
||||
startShowTimer: function() {
|
||||
|
||||
$.fixedToolbars.clearShowTimer();
|
||||
|
||||
var args = $.makeArray(arguments);
|
||||
delayTimer = setTimeout(function(){
|
||||
|
||||
delayTimer = setTimeout(function() {
|
||||
delayTimer = undefined;
|
||||
$.fixedToolbars.show.apply(null, args);
|
||||
$.fixedToolbars.show.apply( null, args );
|
||||
}, showDelay);
|
||||
},
|
||||
|
||||
clearShowTimer: function() {
|
||||
if (delayTimer) {
|
||||
clearTimeout(delayTimer);
|
||||
if ( delayTimer ) {
|
||||
clearTimeout( delayTimer );
|
||||
}
|
||||
delayTimer = undefined;
|
||||
},
|
||||
toggle: function(from){
|
||||
if(from){ currentstate = from; }
|
||||
return (currentstate == 'overlay') ? $.fixedToolbars.hide() : $.fixedToolbars.show();
|
||||
|
||||
toggle: function( from ) {
|
||||
if ( from ) {
|
||||
currentstate = from;
|
||||
}
|
||||
return ( currentstate === "overlay" ) ? $.fixedToolbars.hide() :
|
||||
$.fixedToolbars.show();
|
||||
},
|
||||
setTouchToggleEnabled: function(enabled) {
|
||||
touchToggleEnabled = enabled;
|
||||
}
|
||||
|
||||
setTouchToggleEnabled: function(enabled) {
|
||||
touchToggleEnabled = enabled;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue