私のニーズでは、絶対位置ヘッダーを使用し、スクロールする前に非表示にし、スクロールが終了したときに表示する方が簡単であることがわかりました (iOS4 と Android をサポートするには同じコードが必要です)。
私の目的のために、イベントでヘッダーを非表示にし、またはイベントtouchstart
で再び表示します (さらに、応答性を向上させたり、ちらつきを減らしたりするためのいくつかのタイマー)。点滅しますが、私が見つけることができる最良の妥協点です. イベントを使用してスクロールの開始を検出できます(jQuery がこれを行います) が、次の理由でうまく機能しないことがわかりました。touchend
scroll
touchmove
touchmove
定期的に、iPad はスクロール前の再描画に失敗します (つまり、スクロールが開始される前にヘッダーが変更されたにもかかわらず、絶対ヘッダーがスタックしたままになりますtop
)。
入力要素がフォーカスされると、iPad はその要素を自動的に中央に配置しますが、scrollstart イベントは発生しません (click
入力だけでは touchmove が発生しないため)。
iOS5 での固定ヘッダーの実装は、固定位置と絶対位置のハイブリッド アプローチを使用することで改善できます。
キーボードが閉じられたことを検出するコード (例: キーボードの非表示キーを使用) はDOMFocusOut
、要素にイベントを登録しdocument
、次のコードのようなことを行います。DOMFocusOut
ある要素がフォーカスを取得してから別の要素がフォーカスを失うまでの間にイベントが発生する可能性があるため、タイムアウトが必要です。
function document_DOMFocusOut() {
clearTimeout(touchBlurTimer);
touchBlurTimer = setTimeout(function() {
if (document.activeElement == document.body) {
handleKeyboardHide();
}
}.bind(this), 400);
}
私の固定ヘッダーコードは次のようなものです:
{
setup: function() {
observe(window, 'scroll', this, 'onWinScroll');
observe(document, 'touchstart', this, 'onTouchStart');
observe(document, 'touchend', this, 'onTouchEnd');
if (isMobile) {
observe(document, 'DOMFocusOut', this, 'docBlurTouch');
} else if (isIE) {
// see http://ajaxian.com/archives/fixing-loss-of-focus-on-ie for code to go into this.docBlurIe()
observe(document, 'focusout', this, 'docBlurIe');
} else {
observe(isFirefox ? document : window, 'blur', this, 'docBlur');
}
},
onWinScroll: function() {
clearTimeout(this.scrollTimer);
this.scrolling = false;
this.rehomeAll();
},
rehomeAll: function() {
if ((isIOS5 && this.scrolling) || isIOS4 || isAndroid) {
this.useAbsolutePositioning();
} else {
this.useFixedPositioning();
}
},
// Important side effect that this event registered on document on iOs. Without it event.touches.length is incorrect for any elements in the document using the touchstart event!!!
onTouchStart: function(event) {
clearTimeout(this.scrollTimer);
if (!this.scrolling && event.touches.length == 1) {
this.scrolling = true;
this.touchStartTime = inputOrOtherKeyboardShowingElement(event.target) ? 0 : (new Date).getTime();
// Needs to be in touchStart so happens before iPad automatic scrolling to input, also not reliable using touchMove (although jQuery touch uses touchMove to unreliably detect scrolling).
this.rehomeAll();
}
},
onTouchEnd: function(event) {
clearTimeout(this.scrollTimer);
if (this.scrolling && !event.touches.length) {
var touchedDuration = (new Date).getTime() - this.touchStartTime;
// Need delay so iPad can scroll to the input before we reshow the header.
var showQuick = this.touchStartTime && touchedDuration < 400;
this.scrollTimer = setTimeout(function() {
if (this.scrolling) {
this.scrolling = false;
this.rehomeAll();
}
}.bind(this), showQuick ? 0 : 400);
}
},
// ... more code
}
jQuery モバイルでは、scrollstart および scrollstop イベントがサポートされています。
var supportTouch = $.support.touch,
scrollEvent = "touchmove scroll",
touchStartEvent = supportTouch ? "touchstart" : "mousedown",
touchStopEvent = supportTouch ? "touchend" : "mouseup",
touchMoveEvent = supportTouch ? "touchmove" : "mousemove";
function triggerCustomEvent( obj, eventType, event ) {
var originalType = event.type;
event.type = eventType;
$.event.handle.call( obj, event );
event.type = originalType;
}
// also handles scrollstop
$.event.special.scrollstart = {
enabled: true,
setup: function() {
var thisObject = this,
$this = $( thisObject ),
scrolling,
timer;
function trigger( event, state ) {
scrolling = state;
triggerCustomEvent( thisObject, scrolling ? "scrollstart" : "scrollstop", event );
}
// iPhone triggers scroll after a small delay; use touchmove instead
$this.bind( scrollEvent, function( event ) {
if ( !$.event.special.scrollstart.enabled ) {
return;
}
if ( !scrolling ) {
trigger( event, true );
}
clearTimeout( timer );
timer = setTimeout(function() {
trigger( event, false );
}, 50 );
});
}
};