2012-05-18 32 views
9

Tengo un sitio web asp.net que estoy construyendo para ser compatible con ipad. Cuando me concentro en un elemento de entrada y aparece el teclado, la posición fija del encabezado div (que normalmente se desplaza junto con la página) mostrará una distancia equivalente a la cantidad que ocupa el teclado y se congelará allí durante el tiempo que dure. proceso de entrada. Una vez que el teclado se vuelve a colocar, el div vuelve a su lugar y se comporta normalmente de nuevo. Estoy probando en iOS5, por lo que la posición: fija debería ser compatible.posición fija div se congela en la página (iPad)

¿Es esto un problema conocido? ¿Alguien ha encontrado esto y lo ha tratado antes? Parece que no puedo encontrar nada sobre esto.

Respuesta

15

El posicionamiento fijo se interrumpe en iOS5/iOS6/iOS7.

Editar 3: Ver el enlace a una solución de trabajo cerca del final de esta respuesta para iOS8.

Posición: fijo se rompe cuando:

a) la página se reduce

o

b) el teclado muestra en el iPad/iPhone (debido a un foco de entrada conseguir).

Puede ver los errores por su cuenta en jsbin.com/icibaz/3 abriendo el enlace y haciendo zoom, o dando el foco de entrada. Puede editar el edit the html usted mismo.

Notas acerca de los errores (a) y (b):

  1. un div fijo con top: 0px; left: 0px; mostrará en la posición incorrecta (por encima o por debajo de la parte superior de la pantalla) cuando una entrada se pone el foco y la teclado muestra.

  2. El problema parece tener algo que ver con el autocentrado de la entrada en la pantalla (cambiando window.pageYOffset).

  3. parece ser un fallo de cálculo, y no es un fallo redibujado: si fuerza el top: cambiar (por ejemplo, el cambio entre 0px y 1px) en el caso OnScroll, se puede ver el div movimiento fijado por un pixel, pero permanece en el lugar equivocado.

  4. Una solución que utilicé anteriormente es hide the fixed div when an input gets focus - vea la otra Respuesta que escribí.

  5. El div fijo parece bloquearse en la misma posición absoluta en la página en que estaba cuando se abrió el teclado.

  6. ¿Entonces quizás cambie el div al posicionamiento absoluto cuando una entrada tiene enfoque? Editar 3: vea el comentario al final usando esta solución. O tal vez guarde los valores pageOffset/pageYOffset antes de abrir el teclado, y en un evento onScroll calcule la diferencia entre esos valores y los valores actuales de la página XOffset/pageYOffset (actual una vez que se abre el teclado), y desplace el div fijo por esa diferencia.

  7. Parece que hay un problema diferente con el posicionamiento fijo si la página se acerca - pruébalo here (También buena información here sobre soporte de Android para comentarios fijos).

Edición 1: Para reproducir el uso jsbin (no jsFiddle) y utilizar la vista de pantalla completa de jsbin (no la página de edición). Evite jsfiddle (y edite la vista de jsbin) porque insertan el código dentro de un iframe que causa interferencia con el posicionamiento fijo y pageYOffset.

Editar 2: iOS 6 y iOS 7 Mobile Safari position:fixed; todavía tiene los mismos problemas, ¡presumiblemente son por diseño !.

Edición 3: Una solución de trabajo para (b) es cuando la entrada obtiene el foco, cambie el encabezado al posicionamiento absoluto y luego configure el encabezado superior en el evento de desplazamiento de página for example. Esta solución:

  • Usa un posicionamiento fijo cuando la entrada no está enfocada (usando window.onscroll tiene una trepidación terrible).
  • No permita el pellizco-zoom (evite el error (a) anterior).
  • Usa el posicionamiento absoluto y window.pageYOffset una vez que una entrada obtiene el foco (para que el encabezado esté correctamente posicionado).
  • Si se desplaza mientras la entrada tiene foco, establezca style.top en igual páginaYOffset (el encabezado fluctuará algo debido a la demora del evento onscroll incluso en iOS8).
  • Si usa UIWebView dentro de una aplicación en iOS8 o usa < = iOS7, si se desplaza cuando la entrada tiene foco, el encabezado estará súper nervioso porque onscroll no se dispara hasta que finaliza el desplazamiento.
  • Volver al encabezado de posición fija una vez que la entrada pierde el foco (Ejemplo utiliza input.onblur, pero probablemente tider para usar document.body.onfocus).
  • Cuidado con la usabilidad fallar que si el encabezado es demasiado grande, la entrada puede ocluirse/cubrirse.
  • No pude trabajar para un pie de página debido a errores en la altura de la página/vista de iOS cuando se muestra el teclado.
  • ejemplo Editar usando http://jsbin.com/xujofoze/4/edit y la vista utilizando http://output.jsbin.com/xujofoze/4/quiet
+0

resulta que esto es un error de IOS, que se irá este problema por ahora, pero sus sugerencias son válidas, así que marcaré esta respuesta correcta – jas

+0

jas, ¿es posible dar el enlace para la url de problema de error de iOS? – robocat

+0

No he visto lo que se ha solucionado en iOS6, pero se ha agregado al menos un error adicional: http://igstudio.blogspot.com/2012/09/positionfixed-in-ios-6.html (también sospecha que al darle al div fijo una propiedad de CSS que lo fuerza a tener una composición acelerada por hardware también podría arreglarlo). – robocat

0

Para mis necesidades, me pareció más fácil de usar una cabecera posicionado absoluto, lo oculta antes de desplazamiento y demostrar que cuando rollo acabado (necesito el mismo código para apoyar iOS4 y Android).

Para mis propósitos, que ocultan la cabecera de un evento touchstart, y muestran de nuevo en touchend o scroll evento (además de algunos temporizadores para mejorar la capacidad de respuesta/reducir el parpadeo). Destella, pero es el mejor compromiso que pude encontrar. Uno puede detectar el inicio del desplazamiento utilizando el touchmove evento (jQuery hace esto), pero encontré touchmove no funcionó así para mí porque:

  1. regularmente el iPad deja de hacer un repintado antes de desplazarse (es decir, el encabezado absoluto permanece atascado, aunque se modificó el top antes de que se inicie el desplazamiento).

  2. cuando un elemento de entrada se enfoca, el iPad centra automáticamente el elemento, pero el evento scrollstart no se dispara (porque no hay movimiento si solo click es una entrada).

La implementación de una cabecera fija en iOS5 podría mejorarse mediante el uso de un enfoque híbrido de posicionamiento fijo y absoluto:

  • posicionamiento fijo utilizado para iOS5 hasta una entrada obtiene foco.

  • cuando una entrada obtiene el foco (mostrando el teclado), cambie al código de posicionamiento absoluto iOS4.

  • cuando el teclado está cerrado, cambie de nuevo a la posición fija.

Código para detectar cuando el teclado está cerrado (por ejemplo, utilizando guardallaves teclado) es registrar el evento DOMFocusOut en el elemento document y hacer algo como el siguiente código. El tiempo de espera es necesario porque el evento DOMFocusOut puede activarse cuando un elemento obtiene el foco y otro lo pierde.

function document_DOMFocusOut() { 
    clearTimeout(touchBlurTimer); 
    touchBlurTimer = setTimeout(function() { 
     if (document.activeElement == document.body) { 
      handleKeyboardHide(); 
     } 
    }.bind(this), 400); 
} 

Mi código de cabecera fijo es algo así como:

{ 
    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 móvil es compatible con scrollstart y scrollstop eventos:

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); 
     }); 
    } 
}; 
Cuestiones relacionadas