Wyłącz przewijanie na `<input type = number>`


121

Czy można wyłączyć kółko przewijania, zmieniając liczbę w polu wejściowym? Bawiłem się CSS specyficznym dla webkit, aby usunąć spinner, ale chciałbym całkowicie pozbyć się tego zachowania. Lubię używać, type=numberponieważ wyświetla ładną klawiaturę na iOS.


7
użyj typu wejścia tel (type = "tel") zamiast używać type = number. Pojawi się klawiatura numer iOS.
Praveen Vijayan

Co się stanie, gdy dodasz program onscrollobsługujący tylko event.preventDefault()w nim?
robertc

14
Moim skromnym zdaniem nawet nie sądzę, żeby to była funkcja. Gdybym miał coś do powiedzenia na temat rozwoju przeglądarki, prawdopodobnie naciskałbym na usunięcie tej funkcji. To tylko irytujące i nie znam nikogo, kto by go używał.
Kirkland

3
Całkowicie się z tobą zgadzam, Kirkland. Przewijanie strony z formularzem to po prostu zły UX, a gdy element wprowadzania liczb znajdzie się pod myszą, zaczyna się zwiększać i strona przestaje się przewijać. Całkowicie dezorientujące.
kjs3

3
@PraveenVijayan: Chociaż jest to obejście, jest to sprzeczne z jakimkolwiek powodem używania nowych typów danych wejściowych html5. W przyszłości może się zdarzyć, że telefony dadzą ci możliwość wybierania numeru z kontaktów lub czegokolwiek, co będzie wyglądać bardzo dziwnie, jeśli chcesz, aby był to inny rodzaj numeru.
awe

Odpowiedzi:


92

Zapobiegaj domyślnemu zachowaniu zdarzenia mousewheel na elementach wejściowych, takich jak sugerowane przez innych (wywołanie "blur ()" normalnie nie byłoby preferowanym sposobem, aby to zrobić, ponieważ nie byłoby to tym, czego chce użytkownik).

ALE. Unikałbym nasłuchiwania zdarzenia mousewheel na wszystkich elementach input-number przez cały czas i robiłbym to tylko wtedy, gdy element jest w centrum uwagi (wtedy istnieje problem). W przeciwnym razie użytkownik nie może przewijać strony, gdy wskaźnik myszy znajduje się w dowolnym miejscu nad elementem wejściowym.

Rozwiązanie dla 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')
})

(Deleguj zdarzenia fokusu do otaczającego elementu formularza - aby uniknąć wielu detektorów zdarzeń, które są niekorzystne dla wydajności).


2
Świetna odpowiedź. Myślę, że gdybym robił to dzisiaj, użyłbym Modernizr.touch, aby warunkowo zastosować detektor zdarzeń na urządzeniach niedotykowych. Zasadniczo to, co powiedział poniżej użytkownik2863715.
kjs3

Świetna odpowiedź, ale przynajmniej w pakiecie webkit zapobiega to również przewijaniu okna, gdy kursor znajduje się nad elementem wejściowym. Myślałem, że wydarzenia związane z kółkiem myszy też powinny się rozwijać.
Ryan McGeary

W przypadku stosowania funkcji wyłączania przewijania tylko do elementu, który jest aktywny, nie powinno to zapobiegać przewijaniu, gdy wskaźnik myszy znajduje się nad elementem wejściowym. Ale zapobiega przewijaniu okna, gdy element jest aktywny. I tak, dlaczego kółko myszy nie bulgocze ...: /
Grantzau

6
To miła poprawka dla zachowania, które w ogóle nie powinno istnieć.
Jonny

To nie działa na większości platform typu unix gnome, osx, android (z myszą), ponieważ zachowanie przewijania nie wymaga skupienia danych wejściowych. Alternatywnie, po prostu używany typ tekstowy wejść i umieścić moje ograniczenie numer do niego (Lossing w górę / w dół klawisz skrótu, ale można dodać z js również)
zeachco


20

Jeden odbiornik wydarzeń, który rządzi wszystkimi

Jest to podobne do @Simon Perepelitsa „s odpowiedzi w czystym js, ale nieco prostsze, ponieważ stawia jeden detektor zdarzeń na elemencie dokumentu i sprawdza, czy element skupiony jest wejście numer:

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

Jeśli chcesz wyłączyć przewijanie wartości w niektórych polach, ale nie w innych, po prostu zrób to:

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

z tym:

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

Jeśli wejście ma klasę noscroll, nie zmieni się przy przewijaniu, w przeciwnym razie wszystko pozostanie takie samo.

Przetestuj tutaj z JSFiddle


Widzę, że to zostało odrzucone. Jeśli widzisz problem, zostaw komentarz wraz z głosem przeciw. Jeśli coś przegapiłem, chciałbym się dowiedzieć, co to jest. Dzięki.
Peter Rakmanyi

1
W przypadku nowoczesnej przeglądarki użyj zdarzenia wheelzamiast mousewheel. Źródła: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event PS. To nie ja głosuję przeciw.
vee

19
input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(event){ this.blur() })

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

Aby zapoznać się z przykładem jQuery i rozwiązaniem dla różnych przeglądarek, zobacz powiązane pytanie:

Odbiornik zdarzeń HTML5 do przewijania wprowadzania liczb - tylko Chrome


2
Dzięki Seymon, udało mi się to zrobić dla wszystkich wprowadzanych liczb w jQuery w ten sposób: $(':input[type=number]' ).live('mousewheel',function(e){ $(this).blur(); });użyłem rozmycia zamiast zapobiegania domyślnemu, aby nie blokowało przewijania strony!
Thibaut Colson,

Niezły dodatek dotyczący rozmycia, nie wiedziałem, że nadal możesz zezwolić na przewijanie strony. Zaktualizowałem moją odpowiedź.
Simon Perepelitsa

13
FYI, live()jest przestarzałe. Teraz powinieneś użyć on(): $(':input[type=number]').on('mousewheel',function(e){ $(this).blur(); });
Saeid Zebardast

3
@SaeidZebardast Twój komentarz zasługuje na odpowiedź
snumpy

W przypadku nowoczesnej przeglądarki użyj zdarzenia wheelzamiast mousewheel. Źródła: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
vee

16

Możesz po prostu użyć atrybutu HTML onwheel .

Ta opcja nie ma wpływu na przewijanie innych elementów strony.

I dodaj detektor dla wszystkich wejść, które nie działają w danych wejściowych utworzonych dynamicznie później.

Dodatkowo możesz usunąć strzałki wejściowe za pomocą 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()" />


1
Dzięki stary!! zadziałało idealnie w Angular - Material 6
Nasiruddin Saiyed


Możemy znaleźć ten CSS w wielu źródłach. W każdym razie CSS to tylko bonus. Prawdziwym rozwiązaniem tego pytania jest atrybut HTML onwheel. ;-)
Maikon Matheus

5
Podoba mi się to rozwiązanie! Dzięki. W moim przypadku onBlur()nie był zgodny z oczekiwaniami. return falseZamiast tego ustawiam proste, żeby naprawdę „nic nie robić na kole”. <input type="number" onwheel="return false;">
Daniel

14

@Semyon Perepelitsa

Jest na to lepsze rozwiązanie. Rozmycie usuwa fokus z wejścia i jest to efekt uboczny, którego nie chcesz. Zamiast tego należy użyć evt.preventDefault. Zapobiega to domyślnemu zachowaniu danych wejściowych podczas przewijania przez użytkownika. Oto kod:

input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(evt){ evt.preventDefault(); })

6
Działa, ale zdarzenie nie będzie bąbelkowe, więc strona nie będzie się przewijać. Czy istnieje sposób, aby zachować domyślne zachowanie (przewijanie strony) bez blasku?
GuiGS

W przypadku nowoczesnej przeglądarki użyj zdarzenia wheelzamiast mousewheel. Źródła: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
vee

7

Najpierw musisz zatrzymać zdarzenie kółka myszy przez:

  1. Wyłączanie go za pomocą mousewheel.disableScroll
  2. Przechwytywanie go z e.preventDefault();
  3. Usuwając fokus z elementu el.blur();

Pierwsze dwa podejścia zatrzymują przewijanie okna, a ostatnie usuwa fokus z elementu; z których oba są niepożądanymi skutkami.

Jednym z obejść jest użycie el.blur()i ponowne ustawienie elementu po opóźnieniu:

$('input[type=number]').on('mousewheel', function(){
  var el = $(this);
  el.blur();
  setTimeout(function(){
    el.focus();
  }, 10);
});

3
Dobrze wyglądające rozwiązanie, ale podczas testów odkryłem, że to rozwiązanie miało efekt uboczny polegający na kradzieży ostrości. Poprawiłem to, aby mieć: test, aby sprawdzić, czy element był skupiony: var hadFocus = el.is(':focus');przed rozmyciem, a następnie strażnik, aby ustawić limit czasu tylko wtedy, gdy hadFocs był prawdziwy.
Rob Dawson

2

Podane odpowiedzi nie działają w przeglądarce Firefox (Quantum). Odbiornik zdarzeń należy zmienić z kółka myszy na kółko:

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

Ten kod działa na Firefox Quantum i Chrome.


2

Dla każdego, kto pracuje z Reactem i szuka rozwiązania. Odkryłem, że najłatwiejszym sposobem jest użycie właściwości onWheelCapture w komponencie Input w następujący sposób:

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


2

Odmiana maszynopisu

Maszynopis musi wiedzieć, że pracujesz z elementem HTMLElement dla bezpieczeństwa typów, w przeciwnym razie zobaczysz wiele Property 'type' does not exist on type 'Element'rodzajów błędów.

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

1

Próbując rozwiązać ten problem samodzielnie, zauważyłem, że w rzeczywistości można zachować przewijanie strony i fokus danych wejściowych, wyłączając zmiany liczbowe, próbując ponownie uruchomić przechwycone zdarzenie na elemencie nadrzędnym, <input type="number"/>na którym zostało przechwycone , po prostu w ten sposób:

e.target.parentElement.dispatchEvent(e);

Jednak powoduje to błąd w konsoli przeglądarki i prawdopodobnie nie ma gwarancji, że będzie działać wszędzie (testowałem tylko w przeglądarce Firefox), ponieważ jest to celowo nieprawidłowy kod.

Innym rozwiązaniem, które działa ładnie przynajmniej na Firefoksie i Chromium, jest tymczasowe utworzenie <input>elementu w readOnlyten sposób:

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

Jednym z efektów ubocznych, który zauważyłem, jest to, że może powodować migotanie pola na ułamek sekundy, jeśli masz inny styl readOnlypól, ale przynajmniej w moim przypadku nie wydaje się to stanowić problemu.

Podobnie (jak wyjaśniono w odpowiedzi Jamesa) zamiast modyfikować readOnlywłaściwość, można blur()pole, a następnie focus()przywrócić, ale znowu, w zależności od używanych stylów, może wystąpić migotanie.

Alternatywnie, jak wspomniano w innych komentarzach, możesz po prostu zadzwonić preventDefault()na wydarzenie. Zakładając, że obsługujesz wheelzdarzenia tylko na danych wejściowych liczbowych, które są w centrum uwagi i pod kursorem myszy (to właśnie oznaczają trzy powyższe warunki), negatywny wpływ na wrażenia użytkownika byłby bliski zeru.


1

Jeśli potrzebujesz rozwiązania, które nie wymaga JavaScript, połączenie niektórych funkcji HTML z pseudoelementem CSS załatwia sprawę:

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>

To działa, ponieważ kliknięcie zawartości <label>powiązanej z polem formularza spowoduje zaznaczenie pola. Jednak „okienko” pseudoelementu nad polem będzie blokować dotarcie do niego zdarzeń kółka myszy.

Wadą jest to, że przyciski pokrętła góra / dół już nie działają, ale powiedziałeś, że i tak je usunąłeś.


Teoretycznie można przywrócić menu kontekstowe bez konieczności uprzedniego skupienia się na danych wejściowych: :hoverstyle nie powinny uruchamiać się, gdy użytkownik przewija, ponieważ przeglądarki unikają ich ponownego obliczania podczas przewijania ze względu na wydajność, ale nie dokładnie między przeglądarkami / urządzeniami przetestowałem to.


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;
}

0

Najłatwiejszym rozwiązaniem jest dodanie onWheel={ event => event.currentTarget.blur() }}samego wejścia.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.