web-dev-qa-db-fra.com

Comment puis-je empêcher ma navigation fixe de se déplacer comme ceci lorsque le clavier virtuel s'ouvre dans Mobile Safari?

Je comprends que le safari mobile a beaucoup de bugs autour des éléments fixes, mais pour la plupart, j'ai réussi à faire fonctionner ma mise en page correctement jusqu'à ce que j'ajoute une entrée de texte bien nécessaire à la navigation fixe en bas. Maintenant, lorsque l'utilisateur se concentre sur l'élément de saisie de texte et que le clavier virtuel apparaît, ma navigation, qui est par ailleurs toujours fixée en bas de la page, saute à un endroit vraiment étrange au milieu de la page.

enter image description here

J'ajouterais une partie de mon code à cet article, mais je ne sais pas par où commencer. Cette navigation est fixée en bas et positionnée à gauche et en bas 0, et 100% de largeur. De là, je ne sais pas ce qui se passe, je peux seulement supposer que c'est un bug de safari mobile.

Il semble également perdre sa position fixe et devenir relatif, uniquement lorsque l'élément de saisie de texte est concentré et que le clavier virtuel est ouvert.

47
Eric

http://dansajin.com/2012/12/07/fix-position-fixed/ c'est l'une des solutions proposées. Semble valoir un coup.

En bref: définissez les éléments fixed sur position:absolute lorsqu'une entrée est focused et les réinitialiser lorsque cet élément est blurred

.header { 
    position: fixed; 
} 
.footer { 
    position: fixed; 
} 
.fixfixed .header, 
.fixfixed .footer { 
    position: absolute; 
} 

et

if ('ontouchstart' in window) {
    /* cache dom references */ 
    var $body = $('body'); 

    /* bind events */
    $(document)
    .on('focus', 'input', function() {
        $body.addClass('fixfixed');
    })
    .on('blur', 'input', function() {
        $body.removeClass('fixfixed');
    });
}
19
Sujesh Arukil

Les solutions sur le dessus sont quelques façons d'aller résoudre le problème, mais je pense que l'ajout de classe CSS supplémentaire ou l'utilisation de moderniz complique les choses.

Si vous voulez une solution plus simple, voici une non-modernizrnon-extra-css mais pure solution jquery et travailler sur tous les appareils et navigateurs J'utilise ce correctif sur tous mes projets

if ('ontouchstart' in window) {
    $(document).on('focus', 'textarea,input,select', function() {
        $('.navbar.navbar-fixed-top').css('position', 'absolute');
    }).on('blur', 'textarea,input,select', function() {
        $('.navbar.navbar-fixed-top').css('position', '');
    });
}
15
revobtz

J'ai eu un problème similaire, mais j'ai trouvé une solution de contournement en ajoutant la classe css suivante à l'élément body sur le focus d'entrée, puis en le supprimant à nouveau sur flou:

.u-oh {
    overflow: hidden;
    height: 100%;
    width: 100%;
    position: fixed;
}
5
sylowgreen

À partir de ce qu'a fait sylowgreen, la clé est de corriger le body en entrant le input. Ainsi:

$("#myInput").on("focus", function () {
    $("body").css("position", "fixed");
});

$("#myInput").on("blur", function () {
    $("body").css("position", "static");
});
3
Scott Semyan

Ajoutez du javascript comme ceci:

$(function() {
  var $body;
  if ('ontouchstart' in window) {
    $body = $("body");
    document.addEventListener('focusin', function() {
      return $body.addClass("fixfixed");
    });
    return document.addEventListener('focusout', function() {
      $body.removeClass("fixfixed");
      return setTimeout(function() {
        return $(window).scrollLeft(0);
      }, 20);
    });
  }
});

et ajoutez une classe comme celle-ci:

.fixfixed header{ 
    position: absolute; 
} 

vous pouvez référencer cet article: http://dansajin.com/2012/12/07/fix-position-fixed/

2
wxy112233

J'utilise ce script jQuery:

var focus = 0;
var yourInput = $(".yourInputClass");
yourInput.focusin(function(){
    if(!focus) {
        yourInput.blur();
        $("html, body").scrollTop($(document).height());
        focus = 1;
    }
    if(focus) {
        yourInput.focus();
        focus = 0;
    }
});

Fonctionne parfaitement pour moi.

1
Edie Johnny

Les événements focusin et focusout semblent mieux adaptés à ce problème que les événements focus et blur depuis l'ancienne bulle jusqu'à l'élément racine. Voir cette réponse sur SO.

Personnellement, j'utilise AngularJS, donc je l'ai implémenté comme ceci:

$window.document.body.addEventListener('focusin', function(event) {
    var element = event.target;
    var tagName = element.tagName.toLowerCase();
    if(!$rootScope.inputOverlay && (tagName === 'input' || tagName === 'textarea' || tagName === 'select')) {
        $rootScope.$apply(function() {
            $rootScope.inputOverlay = true;
        });
    }
});
$window.document.body.addEventListener('focusout', function() {
    if($rootScope.inputOverlay) {
        $rootScope.$apply(function() {
            $rootScope.inputOverlay = false;
        });
    }
});

Remarque: J'exécute conditionnellement ce script s'il s'agit de Safari mobile.

Je mets un ng-class attribut sur ma barre de navigation:

<div class="navbar navbar-default navbar-fixed-top" ng-class="{'navbar-absolute': inputOverlay}">

en utilisant le CSS suivant:

.navbar-absolute {
    position: absolute !important;
}

Vous pouvez en savoir plus sur focusinici et focusoutici .

1
brain_bacon

J'aime vraiment la solution ci-dessus. Je l'ai emballé dans un petit plugin jQuery pour que je puisse:

  • Définissez quel parent obtient la classe
  • Définissez les éléments auxquels cela s'applique (n'oubliez pas "zone de texte" et "sélectionner").
  • Définissez le nom de la classe parent
  • Permettez-lui d'être enchaîné
  • Autorisez-le à être utilisé plusieurs fois

Exemple de code:

$.fn.mobileFix = function (options) {
    var $parent = $(this),
    $fixedElements = $(options.fixedElements);

    $(document)
    .on('focus', options.inputElements, function(e) {
        $parent.addClass(options.addClass);
    })
    .on('blur', options.inputElements, function(e) {
        $parent.removeClass(options.addClass);

        // Fix for some scenarios where you need to start scrolling
        setTimeout(function() {
            $(document).scrollTop($(document).scrollTop())
        }, 1);
    });

    return this; // Allowing chaining
};

// Only on touch devices
if (Modernizr.touch) {
    $("body").mobileFix({ // Pass parent to apply to
        inputElements: "input,textarea,select", // Pass activation child elements
        addClass: "fixfixed" // Pass class name
    });
}
1
martinedwards

Aucune de ces solutions n'a fonctionné pour moi car mon DOM est compliqué et j'ai des pages de défilement infinies dynamiques, j'ai donc dû créer les vôtres.

Background: J'utilise un en-tête fixe et un élément plus bas qui reste en dessous une fois que l'utilisateur fait défiler aussi bas. Cet élément possède un champ de saisie de recherche. De plus, j'ai des pages dynamiques ajoutées pendant le défilement avant et arrière.

Problème: Dans iOS, à chaque fois que l'utilisateur clique sur l'entrée dans l'élément fixe, le navigateur défile jusqu'en haut de la page. Cela a non seulement causé un comportement indésirable, mais a également déclenché mon ajout de page dynamique en haut de la page.

Solution attendue: Pas de défilement dans iOS (pas du tout) lorsque l'utilisateur clique sur l'entrée dans l'élément collant.

Solution:

     /*Returns a function, that, as long as it continues to be invoked, will not
    be triggered. The function will be called after it stops being called for
    N milliseconds. If `immediate` is passed, trigger the function on the
    leading Edge, instead of the trailing.*/
    function debounce(func, wait, immediate) {
        var timeout;
        return function () {
            var context = this, args = arguments;
            var later = function () {
                timeout = null;
                if (!immediate) func.apply(context, args);
            };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) func.apply(context, args);
        };
    };

     function is_iOS() {
        var iDevices = [
          'iPad Simulator',
          'iPhone Simulator',
          'iPod Simulator',
          'iPad',
          'iPhone',
          'iPod'
        ];
        while (iDevices.length) {
            if (navigator.platform === iDevices.pop()) { return true; }
        }
        return false;
    }

    $(document).on("scrollstop", debounce(function () {
        //console.log("Stopped scrolling!");
        if (is_iOS()) {
            var yScrollPos = $(document).scrollTop();
            if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px)
                $('#searchBarDiv').css('position', 'absolute');
                $('#searchBarDiv').css('top', yScrollPos + 50 + 'px'); //50 for fixed header
            }
            else {
                $('#searchBarDiv').css('position', 'inherit');
            }
        }
    },250,true));

    $(document).on("scrollstart", debounce(function () {
        //console.log("Started scrolling!");
        if (is_iOS()) {
            var yScrollPos = $(document).scrollTop();
            if (yScrollPos > 200) { //200 here to offset my fixed header (50px) and top banner (150px)
                $('#searchBarDiv').css('position', 'fixed');
                $('#searchBarDiv').css('width', '100%');
                $('#searchBarDiv').css('top', '50px'); //50 for fixed header
            }
        }
    },250,true));

Exigences: JQuery mobile est requis pour que les fonctions de démarrage et d'arrêt fonctionnent.

La fonction anti-rebond est incluse pour lisser tout décalage créé par l'élément collant.

Testé sur iOS10.

0
Dima

Testez celui-ci. Ça marche. Je viens de le tester.

$(document).on('focus','input', function() {
    setTimeout(function() {
        $('#footer1').css('position', 'absolute');
        $('#header1').css('position', 'absolute');
    }, 0);
});
$(document).on('blur','input', function() {
    setTimeout(function() {
        $('#footer1').css('position', 'fixed');
        $('#header1').css('position', 'fixed');
    }, 800);
});
0
Hesam