mirror of
https://github.com/Hopiu/jquery-mobile.git
synced 2026-05-05 05:04:46 +00:00
added new script and associated styles for handling "fixed" toolbars using native support for CSS position: fixed, where possible. Non-supporting platforms will fall back to inline positioning, either by gracefully degrading on their own or by opting them out through a blacklist (fixed positioning detection's got issues...).
unit tests and api documentation coming next, but for the most part, the API is the same as before.
This commit is contained in:
parent
69324e31f0
commit
103f409c47
6 changed files with 309 additions and 51 deletions
3
Makefile
3
Makefile
|
|
@ -31,8 +31,7 @@ JSFILES = js/jquery.ui.widget.js \
|
|||
js/jquery.mobile.buttonMarkup.js \
|
||||
js/jquery.mobile.controlGroup.js \
|
||||
js/jquery.mobile.links.js \
|
||||
js/jquery.mobile.fixHeaderFooter.js \
|
||||
js/jquery.mobile.fixHeaderFooter.native.js \
|
||||
js/jquery.mobile.fixedtoolbar.js \
|
||||
js/jquery.mobile.init.js
|
||||
|
||||
# The files to include when compiling the CSS files
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ div.ui-mobile-viewport { overflow-x: hidden; }
|
|||
.ui-bar { font-size: 16px; margin: 0; }
|
||||
.ui-bar h1, .ui-bar h2, .ui-bar h3, .ui-bar h4, .ui-bar h5, .ui-bar h6 { margin: 0; padding: 0; font-size: 16px; display: inline-block; }
|
||||
|
||||
.ui-header, .ui-footer { display: block; }
|
||||
.ui-page .ui-header, .ui-page .ui-footer { position: relative; }
|
||||
.ui-header, .ui-footer { position: relative; border-left-width: 0; border-right-width: 0; }
|
||||
.ui-header .ui-btn-left { position: absolute; left: 10px; top: .4em; }
|
||||
.ui-header .ui-btn-right { position: absolute; right: 10px; top: .4em; }
|
||||
.ui-header .ui-title, .ui-footer .ui-title { min-height: 1.1em; text-align: center; font-size: 16px; display: block; margin: .6em 90px .8em; padding: 0; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; outline: 0 !important; }
|
||||
|
|
@ -80,44 +79,6 @@ div.ui-mobile-viewport { overflow-x: hidden; }
|
|||
|
||||
/*content area*/
|
||||
.ui-content { border-width: 0; overflow: visible; overflow-x: hidden; padding: 15px; }
|
||||
.ui-page-fullscreen .ui-content { padding:0; }
|
||||
|
||||
/* native fixed headers and footers */
|
||||
.ui-mobile-touch-overflow.ui-page.ui-native-fixed,
|
||||
.ui-mobile-touch-overflow.ui-page.ui-native-fullscreen {
|
||||
overflow: visible;
|
||||
}
|
||||
.ui-mobile-touch-overflow.ui-native-fixed .ui-header,
|
||||
.ui-mobile-touch-overflow.ui-native-fixed .ui-footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 200;
|
||||
}
|
||||
.ui-mobile-touch-overflow.ui-page.ui-native-fixed .ui-footer {
|
||||
top: auto;
|
||||
bottom: 0;
|
||||
}
|
||||
.ui-mobile-touch-overflow.ui-native-fixed .ui-content {
|
||||
padding-top: 2.5em;
|
||||
padding-bottom: 3em;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
height: auto;
|
||||
position: absolute;
|
||||
}
|
||||
.ui-mobile-touch-overflow.ui-native-fullscreen .ui-content {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
.ui-mobile-touch-overflow.ui-native-fullscreen .ui-header,
|
||||
.ui-mobile-touch-overflow.ui-native-fullscreen .ui-footer {
|
||||
opacity: .9;
|
||||
}
|
||||
.ui-native-bars-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* icons sizing */
|
||||
.ui-icon { width: 18px; height: 18px; }
|
||||
|
|
|
|||
|
|
@ -1,8 +1,37 @@
|
|||
/* fixed page header & footer configuration */
|
||||
.ui-header, .ui-footer, .ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { position: absolute; overflow: hidden; width: 100%; border-left-width: 0; border-right-width: 0; }
|
||||
.ui-header-fixed, .ui-footer-fixed {
|
||||
.ui-header,
|
||||
.ui-footer {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
.ui-header-fixed,
|
||||
.ui-footer-fixed {
|
||||
left: 0;
|
||||
right: 0;
|
||||
position: fixed;
|
||||
z-index: 1000;
|
||||
-webkit-transform: translateZ(0); /* Force header/footer rendering to go through the same rendering pipeline as native page scrolling. */
|
||||
}
|
||||
.ui-footer-duplicate, .ui-page-fullscreen .ui-fixed-inline { display: none; }
|
||||
.ui-page-fullscreen .ui-header, .ui-page-fullscreen .ui-footer { opacity: .9; }
|
||||
.ui-header-fixed {
|
||||
top: 0;
|
||||
}
|
||||
.ui-footer-fixed {
|
||||
bottom: 0;
|
||||
}
|
||||
.ui-header-fullscreen,
|
||||
.ui-footer-fullscreen {
|
||||
opacity: .9;
|
||||
}
|
||||
.ui-page-header-fixed {
|
||||
padding-top: 2.5em;
|
||||
}
|
||||
.ui-page-footer-fixed {
|
||||
padding-bottom: 3em;
|
||||
}
|
||||
.ui-page-fullscreen .ui-content {
|
||||
padding: 0;
|
||||
}
|
||||
.ui-fixed-hidden,
|
||||
.ui-footer-duplicate {
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -13,9 +13,9 @@
|
|||
</head>
|
||||
<body>
|
||||
|
||||
<div data-role="page" data-fullscreen="true" class="type-interior">
|
||||
<div data-role="page" class="type-interior">
|
||||
|
||||
<div data-role="header" data-position="fixed" data-theme="f">
|
||||
<div data-role="header" data-position="fixed" data-theme="f" data-fullscreen="true">
|
||||
<h1>Fullscreen fixed header</h1>
|
||||
<a href="../../" data-icon="home" data-iconpos="notext" data-direction="reverse" class="ui-btn-right jqm-home">Home</a>
|
||||
</div>
|
||||
|
|
@ -57,7 +57,7 @@
|
|||
|
||||
</div><!-- /content -->
|
||||
|
||||
<div data-role="footer" class="footer-docs" data-theme="a" data-position="fixed">
|
||||
<div data-role="footer" class="footer-docs" data-theme="a" data-position="fixed" data-fullscreen="true">
|
||||
<h1>Fullscreen Fixed Footer</h1>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ $files = array(
|
|||
'jquery.mobile.buttonMarkup.js',
|
||||
'jquery.mobile.controlGroup.js',
|
||||
'jquery.mobile.links.js',
|
||||
'jquery.mobile.fixHeaderFooter.js',
|
||||
'jquery.mobile.fixHeaderFooter.native.js',
|
||||
'jquery.mobile.fixedToolbar.js',
|
||||
'jquery.mobile.init.js'
|
||||
);
|
||||
|
||||
|
|
|
|||
270
js/jquery.mobile.fixedToolbar.js
Normal file
270
js/jquery.mobile.fixedToolbar.js
Normal file
|
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
* "fixedtoolbar" plugin - behavior for "fixed" headers and footers
|
||||
*/
|
||||
|
||||
(function( $, undefined ) {
|
||||
|
||||
$.widget( "mobile.fixedtoolbar", $.mobile.widget, {
|
||||
options: {
|
||||
visibleOnPageShow: true,
|
||||
togglePageZoom: true,
|
||||
transition: "fade", //can be false, fade, slide (slide maps to vertical slides)
|
||||
fullscreen: false,
|
||||
tapToggle: true,
|
||||
scrollToggle: false,
|
||||
|
||||
// Browser detection! Weeee, here we go...
|
||||
// Unfortunately, position:fixed is costly, not to mention probably impossible, to feature-detect accurately.
|
||||
// Some tests exist, but they currently return false results in critical devices and browsers,
|
||||
// which could lead to a broken experience.
|
||||
// Testing is also pretty obtrusive to page load here, requiring injected elements and scrolling the window
|
||||
// For that reason, the following function serves to rule out some browsers with known issues
|
||||
// This is a plugin option like any other, so feel free to improve or overwrite it
|
||||
supportBlacklist: function(){
|
||||
var blacklisted = false,
|
||||
ua = navigator.userAgent,
|
||||
platform = navigator.platform,
|
||||
w = window;
|
||||
|
||||
// iOS 4.3 and older
|
||||
if(
|
||||
// Platform is iPhone/Pad/Touch
|
||||
( platform.indexOf( "iPhone" ) > -1 || platform.indexOf( "iPad" ) > -1 || platform.indexOf( "iPod" ) > -1 ) &&
|
||||
// Rendering engine is Webkit
|
||||
ua.match( /(AppleWebKit)\/([0-9\.]+) / ) &&
|
||||
// Webkit version is less than 534 (ios5)
|
||||
RegExp.$1 && ( RegExp.$2 && RegExp.$2.split( "." )[0] < 534 )
|
||||
){
|
||||
blacklisted = true;
|
||||
}
|
||||
|
||||
// Opera Mini
|
||||
if( operamini = w.operamini && ({}).toString.call( w.operamini ) === "[object OperaMini]" ){
|
||||
blacklisted = true;
|
||||
}
|
||||
|
||||
//Android lte 2.1
|
||||
/*
|
||||
if( ... ){
|
||||
blacklisted = true;
|
||||
}
|
||||
*/
|
||||
|
||||
return blacklisted;
|
||||
},
|
||||
initSelector: ":jqmData(position='fixed')"
|
||||
},
|
||||
|
||||
_create: function() {
|
||||
|
||||
var self = this,
|
||||
o = self.options,
|
||||
$el = self.element,
|
||||
tbtype = $el.is( ".ui-header" ) ? "header" : "footer",
|
||||
$page = $el.closest(".ui-page");
|
||||
|
||||
// Feature detecting support for
|
||||
if( o.supportBlacklist() ){
|
||||
self.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
$el.addClass( "ui-"+ tbtype +"-fixed" );
|
||||
|
||||
// "fullscreen" overlay positioning
|
||||
// NOTE - this used to be only "data-fullscreen" on page element. Support both or deprecate page?
|
||||
if( $el.jqmData( "fullscreen" ) || $page.jqmData( "fullscreen" ) ){
|
||||
$el.addClass( "ui-" + tbtype + "-fullscreen" );
|
||||
}
|
||||
// If not fullscreen, add class to page to set top or bottom padding
|
||||
else{
|
||||
$page.addClass( "ui-page-" + tbtype + "-fixed" );
|
||||
}
|
||||
|
||||
self._addTransitionClass();
|
||||
self._bindPageEvents();
|
||||
self._bindToggleHandlers();
|
||||
},
|
||||
|
||||
_addTransitionClass: function(){
|
||||
var tclass = this.options.transition;
|
||||
|
||||
if( tclass ){
|
||||
// use appropriate slide for header or footer
|
||||
if( tclass === "slide" ){
|
||||
tclass = this.element.is( ".ui-header" ) ? "slidedown" : "slideup";
|
||||
}
|
||||
|
||||
this.element.addClass( tclass );
|
||||
}
|
||||
},
|
||||
|
||||
/* Note: this is all that's needed to make iOS 4.3 and Android 2.1 fix their positioning mistakes after scrolling
|
||||
it won't fully patch a "fixed effect", but rather just repositions after scrollstop
|
||||
|
||||
_fixFixedSupport: function(){
|
||||
var $el = this.element,
|
||||
tbtype = $el.is( ".ui-header" ) ? "header" : "footer";
|
||||
|
||||
$( window )
|
||||
.bind( "scrollstop", function(){
|
||||
// TODO: check if toolbars are not positioned correctly on screen, then proceed
|
||||
if( tbtype === "header" ){
|
||||
$el.css( "top", $( window ).scrollTop() );
|
||||
}
|
||||
else {
|
||||
$el.css( "bottom", -$( window ).scrollTop() );
|
||||
}
|
||||
})
|
||||
},
|
||||
*/
|
||||
|
||||
_bindPageEvents: function(){
|
||||
var self = this,
|
||||
o = self.options,
|
||||
$el = self.element;
|
||||
|
||||
//page event bindings
|
||||
$el.closest( ".ui-page" )
|
||||
.bind( "pagebeforeshow", function(){
|
||||
if( o.togglePageZoom ){
|
||||
self.disablePageZoom();
|
||||
}
|
||||
if( o.visibleOnPageShow ){
|
||||
self.show();
|
||||
}
|
||||
} )
|
||||
.bind( "pagehide", function(){
|
||||
if( o.togglePageZoom ){
|
||||
self.restorePageZoom();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
_visible: false,
|
||||
|
||||
show: function(){
|
||||
var hideClass = "ui-fixed-hidden";
|
||||
if( this.options.transition ){
|
||||
this.element
|
||||
.removeClass( "out " + hideClass )
|
||||
.addClass( "in" );
|
||||
}
|
||||
else {
|
||||
this.element.removeClass( hideClass );
|
||||
}
|
||||
this._visible = true;
|
||||
},
|
||||
|
||||
hide: function(){
|
||||
var hideClass = "ui-fixed-hidden";
|
||||
if( this.options.transition ){
|
||||
this.element
|
||||
.removeClass( "in" )
|
||||
.addClass( "out" )
|
||||
.animationComplete(function(){
|
||||
$(this).addClass( hideClass );
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.element.addClass( hideClass );
|
||||
}
|
||||
this._visible = false;
|
||||
},
|
||||
|
||||
toggle: function(){
|
||||
this[ this._visible ? "hide" : "show" ]();
|
||||
},
|
||||
|
||||
_visibleBeforeScroll: null,
|
||||
|
||||
_bindToggleHandlers: function(){
|
||||
var self = this,
|
||||
o = self.options,
|
||||
$el = self.element;
|
||||
|
||||
// tap toggle
|
||||
$el.closest( ".ui-page" )
|
||||
.bind( "vclick", function(){
|
||||
if( o.tapToggle ){
|
||||
self.toggle();
|
||||
}
|
||||
});
|
||||
|
||||
// scroll toggle
|
||||
$( window )
|
||||
.bind( "scrollstart", function(){
|
||||
if( o.scrollToggle && self.visible ){
|
||||
self.hide();
|
||||
}
|
||||
})
|
||||
.bind( "scrollstop", function(){
|
||||
if( o.scrollToggle && self._visibleBeforeScroll ){
|
||||
self.hide();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
destroy: function(){
|
||||
this.element.removeClass( "ui-header-fixed ui-footer-fixed ui-header-fullscreen ui-footer-fullscreen in out fade slidedown slideup ui-fixed-hidden" )
|
||||
this.element.closest( ".ui-page" ).removeClass( "ui-page-header-fixed ui-page-footer-fixed" );
|
||||
},
|
||||
|
||||
// for caching reference to meta viewport elem
|
||||
_metaViewport: null,
|
||||
|
||||
// on pageshow, does a meta viewport element exist in the head?
|
||||
_metaViewportPreexists: false,
|
||||
|
||||
// used for storing value of meta viewport content at page show, for restoration on hide
|
||||
_metaViewportContent: "",
|
||||
|
||||
// Fixed toolbars require page zoom to be disabled, otherwise usability issues crop up
|
||||
// This method is meant to disable zoom while a fixed-positioned toolbar page is visible
|
||||
disablePageZoom: function(){
|
||||
if( !this.options.togglePageZoom ){
|
||||
return;
|
||||
}
|
||||
var cont = "user-scalable=no";
|
||||
this._metaViewport = $( "meta[name='viewport']" );
|
||||
this._metaViewportPreexists = this._metaViewport.length;
|
||||
|
||||
var currentContent = this._metaViewport.attr( "content" );
|
||||
|
||||
// If scaling's already disabled, or another plugin is handling it on this page already
|
||||
if( currentContent.indexOf( cont ) > -1 ){
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this._metaViewportContent = currentContent;
|
||||
}
|
||||
|
||||
if( !this._metaViewportPreexists ){
|
||||
this._metaViewport = $( "<meta>", { "name": "viewport", "content": cont } ).prependTo( "head" );
|
||||
}
|
||||
else{
|
||||
this._metaViewport.attr( "content", this._metaViewportContent + ", " + cont );
|
||||
}
|
||||
},
|
||||
|
||||
// restore the meta viewport tag to its original state, or remove it
|
||||
restorePageZoom: function(){
|
||||
if( !this.options.togglePageZoom ){
|
||||
return;
|
||||
}
|
||||
if( this._metaViewportPreexists ){
|
||||
this._metaViewport.attr( "content", this._metaViewportContent );
|
||||
}
|
||||
else {
|
||||
this._metaViewport.remove();
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
//auto self-init widgets
|
||||
$( document ).bind( "pagecreate create", function( e ){
|
||||
$( $.mobile.fixedtoolbar.prototype.options.initSelector, e.target ).fixedtoolbar();
|
||||
});
|
||||
|
||||
})( jQuery );
|
||||
Loading…
Reference in a new issue