Désactiver le défilement sur `<input type = number>`

121

Est-il possible de désactiver la molette de défilement en modifiant le numéro dans un champ de saisie numérique? J'ai manipulé le CSS spécifique au webkit pour supprimer le spinner, mais j'aimerais complètement me débarrasser de ce comportement. J'aime utiliser type=numbercar cela fait apparaître un joli clavier sur iOS.

kjs3
la source
7
utilisez le type d'entrée tel (type = "tel") au lieu d'utiliser type = nombre. Il fera apparaître un clavier numérique iOS.
Praveen Vijayan
Que se passe-t-il lorsque vous ajoutez un onscrollgestionnaire avec juste event.preventDefault()dedans?
robertc
14
À mon humble avis, je ne pense même pas que cela devrait être une fonctionnalité. Si j'avais un mot à dire dans le développement du navigateur, je pousserais probablement pour supprimer cette fonctionnalité. Ce n'est rien d'autre que gênant, et je ne connais personne qui l'utiliserait réellement.
Kirkland
3
Je suis totalement d'accord avec toi Kirkland. C'est juste une mauvaise UX de faire défiler une page avec un formulaire et, lorsqu'un élément d'entrée numérique passe sous votre souris, il commence à s'incrémenter et la page cesse de défiler. Totalement désorientant.
kjs3
3
@PraveenVijayan: Bien que ce soit une solution de contournement, cela va à l'encontre de toute raison d'utiliser les nouveaux types d'entrée html5. À l'avenir, il se peut que les téléphones vous donnent la possibilité de choisir un numéro parmi les contacts ou quoi que ce soit, ce qui semblera très étrange si vous vouliez que ce soit un type de numéro différent.
awe

Réponses:

92

Empêchez le comportement par défaut de l'événement mousewheel sur les éléments de nombre d'entrée comme suggéré par d'autres (appeler "blur ()" ne serait normalement pas la meilleure façon de le faire, car ce ne serait pas ce que l'utilisateur veut).

MAIS. J'éviterais d'écouter l'événement mousewheel sur tous les éléments de nombre d'entrée tout le temps et de ne le faire que lorsque l'élément est au point (c'est là que le problème existe). Sinon, l'utilisateur ne peut pas faire défiler la page lorsque le pointeur de la souris se trouve n'importe où sur un élément de nombre d'entrée.

Solution pour jQuery:

// disable mousewheel on a input number field when in focus
// (to prevent Cromium browsers change the value when scrolling)
$('form').on('focus', 'input[type=number]', function (e) {
  $(this).on('wheel.disableScroll', function (e) {
    e.preventDefault()
  })
})
$('form').on('blur', 'input[type=number]', function (e) {
  $(this).off('wheel.disableScroll')
})

(Déléguez les événements de focus à l'élément de formulaire environnant - pour éviter de nombreux écouteurs d'événements, ce qui nuit aux performances.)

Grantzau
la source
2
Très bonne réponse. Je pense que si je faisais cela aujourd'hui, j'utiliserais Modernizr.touch pour appliquer conditionnellement l'écouteur d'événements sur des appareils non tactiles. Fondamentalement, ce que user2863715 a dit ci-dessous.
kjs3
Excellente réponse, mais au moins dans le kit Web, cela empêche également la fenêtre de défiler lorsque le curseur se trouve sur l'élément d'entrée. Je pensais que les événements de la molette de la souris étaient également censés bouillonner.
Ryan McGeary
Lorsque vous appliquez uniquement la fonction désactiver le défilement à un élément lorsqu'il est sélectionné, cela ne doit pas empêcher le défilement lorsque la souris est sur l'élément d'entrée. Mais cela empêche de faire défiler la fenêtre, lorsque l'élément est au point. Et oui, pourquoi l'événement de la molette de la souris ne bouillonne-t-il pas ...: /
Grantzau
6
C'est une bonne solution à un comportement qui ne devrait pas exister en premier lieu.
Jonny
Cela ne fonctionne pas sur la plupart des plates-formes de type unix gnome, osx, android (avec souris) car le comportement de défilement ne nécessite pas que l'entrée soit focalisée. Alternativement, je viens d'utiliser le type de texte des entrées et d'y mettre ma restriction de nombre (perte de raccourci clavier haut / bas mais peut également être ajouté avec js)
zeachco
40
$(document).on("wheel", "input[type=number]", function (e) {
    $(this).blur();
});
stovroz
la source
2
c'est celui qui est parfait.
Patrik Laszlo
Ça marche bien. Merci.
Vijay S
21

Un écouteur d'événements pour les gouverner tous

Ceci est similaire à @Simon Perepelitsa de réponse dans js pure, mais un peu plus simple, car il met un écouteur d'événement sur l'élément de document et vérifie si l'élément ciblé est une entrée de numéro:

document.addEventListener("wheel", function(event){
    if(document.activeElement.type === "number"){
        document.activeElement.blur();
    }
});

Si vous souhaitez désactiver le comportement de défilement des valeurs sur certains champs, mais pas sur d'autres, procédez comme suit:

document.addEventListener("wheel", function(event){
    if(document.activeElement.type === "number" &&
       document.activeElement.classList.contains("noscroll"))
    {
        document.activeElement.blur();
    }
});

avec ça:

<input type="number" class="noscroll"/>

Si une entrée a la classe noscroll, elle ne changera pas lors du défilement, sinon tout reste le même.

Testez ici avec JSFiddle

Peter Rakmanyi
la source
Je vois que cela a été critiqué. Si vous voyez un problème, veuillez également laisser un commentaire avec le vote défavorable. Si j'ai raté quelque chose, j'aimerais savoir ce que c'est. Merci.
Peter Rakmanyi
1
Pour un navigateur moderne, veuillez utiliser event wheelau lieu de mousewheel. Référence: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event PS. Je ne suis pas celui qui vote.
vee le
19
input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(event){ this.blur() })

http://jsfiddle.net/bQbDm/2/

Pour un exemple jQuery et une solution multi-navigateurs, voir la question associée:

Écouteur d'événements HTML5 pour le défilement de la saisie numérique - Chrome uniquement

Simon Perepelitsa
la source
2
Merci Seymon, j'ai réussi à le faire pour toutes les entrées de nombres avec jQuery de cette façon: $(':input[type=number]' ).live('mousewheel',function(e){ $(this).blur(); });j'ai utilisé le flou au lieu d'empêcher la valeur par défaut pour ne pas empêcher la page de défiler!
Thibaut Colson
Bel ajout sur le flou, je ne savais pas que vous pouvez toujours autoriser le défilement des pages. J'ai mis à jour ma réponse.
Simon Perepelitsa
13
FYI, live()est obsolète. Maintenant, vous devriez utiliser on(): $(':input[type=number]').on('mousewheel',function(e){ $(this).blur(); });
Saeid Zebardast
3
@SaeidZebardast Votre commentaire mérite d'être une réponse
snumpy
Pour un navigateur moderne, veuillez utiliser event wheelau lieu de mousewheel. Référence: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
vee le
16

Vous pouvez simplement utiliser l' attribut HTML onwheel .

Cette option n'a aucun effet sur le défilement des autres éléments de la page.

Et ajouter un écouteur pour toutes les entrées ne fonctionne pas dans les entrées créées dynamiquement en arrière.

De plus, vous pouvez supprimer les flèches d'entrée avec CSS.

input[type="number"]::-webkit-outer-spin-button, 
input[type="number"]::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
input[type="number"] {
    -moz-appearance: textfield;
}
<input type="number" onwheel="this.blur()" />

Maikon Matheus
la source
1
Merci mec!! il a fonctionné parfaitement dans Angular - Material 6
Nasiruddin Saiyed
Nous pouvons trouver ce CSS dans de nombreuses sources. Quoi qu'il en soit, le CSS n'est qu'un bonus. La vraie solution à la question est l'attribut HTML onwheel. ;-)
Maikon Matheus
5
J'aime cette solution! Merci. Dans mon cas, ce onBlur()n'était pas comme prévu. J'ai mis en place un simple à la return falseplace pour vraiment "ne rien faire sur roue". <input type="number" onwheel="return false;">
Daniel
14

@Semyon Perepelitsa

Il existe une meilleure solution pour cela. Le flou supprime le focus de l'entrée et c'est un effet secondaire que vous ne voulez pas. Vous devez utiliser evt.preventDefault à la place. Cela empêche le comportement par défaut de l'entrée lorsque l'utilisateur fait défiler. Voici le code:

input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(evt){ evt.preventDefault(); })
Dibran
la source
6
Cela fonctionne, mais l'événement ne bouillonnera pas, donc la page ne défilera pas. Existe-t-il un moyen de conserver le comportement par défaut (défilement de la page) sans flou?
GuiGS
Pour un navigateur moderne, veuillez utiliser event wheelau lieu de mousewheel. Référence: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
vee le
7

Vous devez d'abord arrêter l'événement de la molette de la souris en:

  1. Désactiver avec mousewheel.disableScroll
  2. L'intercepter avec e.preventDefault();
  3. En supprimant le focus de l'élément el.blur();

Les deux premières approches arrêtent à la fois le défilement de la fenêtre et la dernière supprime le focus de l'élément; qui sont tous deux des résultats indésirables.

Une solution de contournement consiste à utiliser el.blur()et à recentrer l'élément après un délai:

$('input[type=number]').on('mousewheel', function(){
  var el = $(this);
  el.blur();
  setTimeout(function(){
    el.focus();
  }, 10);
});
James EJ
la source
3
Une bonne solution, mais lors des tests, j'ai trouvé que cette solution avait pour effet secondaire de voler la concentration. J'ai modifié ceci pour avoir: un test pour voir si l'élément avait le focus: var hadFocus = el.is(':focus');avant le flou, puis un garde pour ne définir le délai d'expiration que si hadFocs était vrai.
Rob Dawson
3

Pour tous ceux qui travaillent avec React et recherchent une solution. J'ai découvert que le moyen le plus simple est d'utiliser le prop onWheelCapture dans un composant d'entrée comme celui-ci:

onWheelCapture={e => { e.target.blur() }}

Aleš Chromec
la source
2

Les réponses fournies ne fonctionnent pas dans Firefox (Quantum). L'écouteur d'événement doit être changé de la molette de la souris à la roue:

$(':input[type=number]').on('wheel',function(e){ $(this).blur(); });

Ce code fonctionne sur Firefox Quantum et Chrome.

Banque Mathias
la source
2

Variation typographique

Typescript doit savoir que vous travaillez avec un HTMLElement pour la sécurité des types, sinon vous verrez beaucoup de Property 'type' does not exist on type 'Element'types d'erreurs.

document.addEventListener("mousewheel", function(event){
  let numberInput = (<HTMLInputElement>document.activeElement);
  if (numberInput.type === "number") {
    numberInput.blur();
  }
});
vinyle
la source
1

En essayant de résoudre cela par moi-même, j'ai remarqué qu'il était en fait possible de conserver le défilement de la page et le focus de l'entrée tout en désactivant les changements de nombre en tentant de relancer l'événement capturé sur l'élément parent de l'élément <input type="number"/>sur lequel il a été capturé , simplement comme ceci:

e.target.parentElement.dispatchEvent(e);

Cependant, cela provoque une erreur dans la console du navigateur, et il n'est probablement pas garanti de fonctionner partout (je n'ai testé que sur Firefox), car il s'agit d'un code intentionnellement invalide.

Une autre solution qui fonctionne bien au moins sur Firefox et Chromium est de créer temporairement l' <input>élément readOnly, comme ceci:

function handleScroll(e) {
  if (e.target.tagName.toLowerCase() === 'input'
    && (e.target.type === 'number')
    && (e.target === document.activeElement)
    && !e.target.readOnly
  ) {
      e.target.readOnly = true;
      setTimeout(function(el){ el.readOnly = false; }, 0, e.target);
  }
}
document.addEventListener('wheel', function(e){ handleScroll(e); });

Un effet secondaire que j'ai remarqué est que cela peut faire scintiller le champ pendant une fraction de seconde si vous avez un style différent pour les readOnlychamps, mais pour mon cas au moins, cela ne semble pas être un problème.

De même, (comme expliqué dans la réponse de James) au lieu de modifier la readOnlypropriété, vous pouvez blur()le champ, puis focus()le retourner, mais encore une fois, selon les styles utilisés, un scintillement peut se produire.

Alternativement, comme mentionné dans d'autres commentaires ici, vous pouvez simplement appeler preventDefault()l'événement à la place. En supposant que vous gérez uniquement les wheelévénements sur les entrées numériques qui sont au point et sous le curseur de la souris (c'est ce que signifient les trois conditions ci-dessus), l'impact négatif sur l'expérience utilisateur serait proche de zéro.

Rimas Kudelis
la source
1

Si vous voulez une solution qui n'a pas besoin de JavaScript, combiner des fonctionnalités HTML avec un pseudo-élément CSS fait l'affaire:

span {
  position: relative;
  display: inline-block; /* Fit around contents */
}

span::after {
  content: "";
  position: absolute;
  top: 0; right: 0; bottom: 0; left: 0; /* Stretch over containing block */
  cursor: text; /* restore I-beam cursor */
}

/* Restore context menu while editing */
input:focus {
  position: relative;
  z-index: 1;
}
<label>How many javascripts can u fit in u mouth?
  <span><input type="number" min="0" max="99" value="1"></span>
</label>

Cela fonctionne parce que cliquer sur le contenu d'un <label>qui est associé à un champ de formulaire focalisera le champ. Cependant, le «volet de la fenêtre» du pseudo-élément au-dessus du champ empêchera les événements de la molette de la souris de l'atteindre.

L'inconvénient est que les boutons fléchés haut / bas ne fonctionnent plus, mais vous avez dit que vous les aviez quand même supprimés.


En théorie, on pourrait restaurer le menu contextuel sans exiger que l'entrée soit focalisée en premier: les :hoverstyles ne devraient pas se déclencher lorsque l'utilisateur fait défiler, car les navigateurs évitent de les recalculer pendant le défilement pour des raisons de performances, mais je n'ai pas complètement cross-browser / device testé.

Tigt
la source
0
function fixNumericScrolling() {
$$( "input[type=number]" ).addEvent( "mousewheel", function(e) {
    stopAll(e);     
} );
}

function stopAll(e) {
if( typeof( e.preventDefault               ) != "undefined" ) e.preventDefault();
if( typeof( e.stopImmediatePropagation     ) != "undefined" ) e.stopImmediatePropagation();
if( typeof( event ) != "undefined" ) {
    if( typeof( event.preventDefault           ) != "undefined" ) event.preventDefault();
    if( typeof( event.stopImmediatePropagation ) != "undefined" ) event.stopImmediatePropagation();
}

return false;
}
syb
la source
0

La solution la plus simple consiste à ajouter l' onWheel={ event => event.currentTarget.blur() }}entrée elle-même.

TSlegaitis
la source