24 votes

Empêcher le défilement de la page lors du changement de focus

Je suis novice en matière de développement et de web mobile. Mon application utilise jquery-mobile, phonegap et Compass (scss).

J'ai un problème sur ma page de connexion :

Le logo et les champs sont contenus dans des conteneurs "div" standard (data-role="content" data-type="vertical"). L'arrière-plan est coloré.

lorsque l'on passe du champ "login" au champ "mot de passe", la page se déplace vers le haut, ce que je ne souhaite pas. Je voudrais que mon logo et mes champs restent en place, comme sur la page de connexion de l'application Skype iOS.

voici ce qui se passe :

bug description

J'ai essayé plusieurs astuces, en essayant de bloquer les événements de défilement, ou en forçant la page à défiler à 0,0, sans succès.

Je pense maintenant à une nouvelle stratégie, peut-être en utilisant le positionnement relatif supérieur pour le logo et les champs, et en récupérant les événements de focus pour faire défiler la page moi-même, lors du glissement du clavier vers le haut (en animant les coordonnées de positionnement relatif supérieur).

Bien que cela semble faisable, je me demande si c'est le type de contournement utilisé par l'équipe de l'application Skype iOS...

Tout conseil sur les techniques utilisées dans ce cas particulier est le bienvenu !

Santé,

Fred

0 votes

Je suis presque sûr que l'application Skype iOS utilise une vue native pour les champs de connexion au lieu de Phonegap qui utilise une WebView. Peut-être en essayant de changer la hauteur du corps à 100% ou en mettant le login/mot de passe dans la moitié supérieure quand la page s'ouvre.

0 votes

@GeekNum88 Bien sûr. Soleshoe cherche une solution à ce problème en utilisant WebView.

0 votes

@Zulakis - stupide touche entrée - je n'avais pas fini de taper...sigh

34voto

Gabriel Mahan Points 369

Je pense que vous voulez appeler le focus sur l'élément directement et utiliser l'option 'preventScroll'.

el.focus({preventScroll: true});

$('#el').focus((e) => {
  e.preventDefault();
  e.target.focus({preventScroll: true});
})

https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus

0 votes

@mohsinulhaq dans Firefox 66 (2019) preventScroll fonctionne aussi très bien

1 votes

MISE À JOUR : j'avais tort - il semble que dans Firefox preventScroll ne fonctionne que pour le défilement horizontal, le défilement vertical est toujours appliqué :/ dans Chrome preventScroll s'applique dans les deux directions.

0 votes

J'ai cherché pendant des heures une solution à un problème très similaire, et celle-ci a fonctionné à merveille. Ceci devrait être marqué comme la réponse, stat.

5voto

Jonathan Tonge Points 860

Je ne l'ai pas encore testé, mais e.preventDefault() arrête-t-il le problème ? En général, vous utilisez e.preventDefault() pour arrêter le comportement par défaut du défilement/du glissement.

$(document).bind('focus', function(e){
  e.preventDefault();
});

ou mieux

$(element).bind('focus', function(e){
  e.preventDefault();
});

ou

$(document).bind('touchstart', function(e){
  e.preventDefault();
});

Un champ de saisie serait-il plus efficace ?

$(":input").live({
  focus: function(e) {
    e.preventDefault();
  }
});

0 votes

La première et la deuxième solution n'apportent rien à mon problème. La troisième solution empêche l'utilisateur de changer de focus... La quatrième solution empêche le clavier de glisser vers le haut lorsque .focus() est appelé sur le champ login (mais le clavier glisse vers le haut lorsqu'il touche un champ, et le problème reste le même)...

2voto

soleshoe Points 373

Je n'ai finalement pas réussi à trouver de solution de codage pour gérer mon problème, mais j'ai remarqué qu'un positionnement chirurgical me permettait de friser l'écran lors du passage d'un champ à l'autre.

L'espace disponible pour l'ensemble de l'écran de connexion ne doit pas dépasser l'espace disponible une fois le clavier visible, c'est-à-dire 200 pixels. De plus, pour éviter le défilement lors du changement de champ, la position centrale du dernier champ de saisie ne doit pas dépasser 100 pixels maximum. En utilisant le CSS, il est possible de jouer sur le padding et la marge pour obtenir le résultat souhaité.

Il devrait être possible d'obtenir un espace vertical supplémentaire en supprimant la "barre de navigation des champs" du clavier, mais je ne sais pas non plus comment faire :/

enter image description here

0 votes

Jetez un coup d'œil à cette solution : stackoverflow.com/a/39584526/1043231

2voto

AlexMorley-Finch Points 1498

J'ai eu un problème similaire (avec le défilement de la page lors du clic/de la mise au point) et il m'a fallu du temps pour trouver la solution, c'est pourquoi je la documente ici.

J'avais un champ de formulaire personnalisé qui, lorsqu'on cliquait dessus, faisait défiler la page vers le haut. Il s'est avéré que l'entrée était masquée à l'aide de position: absolute; top: -99999999px; (parce que si vous le cachez correctement, les événements de focus ne fonctionnent pas). Cela a placé l'entrée cachée en haut de la page. Ensuite, lorsque nous cliquons sur l'étiquette et que l'entrée est focalisée, la page défile vers le haut.

J'ai cherché des choses comme "Scroll to top on focus/click" et toutes sortes de choses mais je n'ai rien trouvé de décent. La solution était d'utiliser right: -99999999px à la place. J'ai évité d'utiliser l'habituel left: -9999999px car apparemment cela peut affecter le site lors de l'utilisation direction: rtl;

1voto

Tobias Buschor Points 291

Il existe un preventScroll option pour focus() :
el.focus({preventScroll: true});

Et si vous devez prendre en charge les navigateurs ( IE11, Safari<14 ?) qui ne prennent pas en charge les options de mise au point, utilisez la méthode suivante polyfill :

!function(){

    let supported = false;
    document.createElement('i').focus({
        get preventScroll() {
            supported = true;
        },
    });
    if (!supported) {
        let original = HTMLElement.prototype.focus;
        Element.prototype.focus = HTMLElement.prototype.focus = function(options){
            if (options.preventScroll) {
                const map = new Map();
                let p = this;
                while (p = p.parentNode) map.set(p, [p.scrollLeft, p.scrollTop]);
                original.apply(this, arguments);
                map.forEach(function(pos, el){
                    // todo: test: only if changed? does it trigger scroll?
                    // IE flickers
                    el.scrollLeft = pos[0]
                    el.scrollTop  = pos[1]
                });
            } else {
                original.apply(this, arguments);
            }
        }
    }

}();

0 votes

Qu'en est-il si les gens utilisent une bibliothèque qui fait le focus et non pas nous manuellement en utilisant .focus() ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X