Desative a rolagem em `<input type = number>`

121

É possível desativar a roda de rolagem alterando o número em um campo de entrada de número? Eu mexi com CSS específico do webkit para remover o botão giratório, mas gostaria de me livrar totalmente desse comportamento. Eu gosto de usar type=numberporque traz um bom teclado no iOS.

kjs3
fonte
7
use o tipo de entrada tel (tipo = "tel") em vez de usar o tipo = número. Irá aparecer um teclado numérico iOS.
Praveen Vijayan
O que acontece quando você adiciona um onscrollmanipulador apenas event.preventDefault()com ele?
robertc
14
Na minha humilde opinião, nem acho que isso deveria ser um recurso. Se eu pudesse opinar sobre o desenvolvimento do navegador, provavelmente pressionaria para remover esse recurso. Não é nada além de irritante, e não conheço ninguém que realmente o usaria.
Kirkland
3
Eu concordo totalmente com você Kirkland. É apenas uma UX ruim rolar uma página com um formulário e, quando um elemento de entrada de número passa por baixo do mouse, ele começa a aumentar e a página para de rolar. Totalmente desorientador.
kjs3
3
@PraveenVijayan: Embora esta seja uma solução alternativa, ela vai contra qualquer razão para usar os novos tipos de entrada html5. No futuro, pode ser que os telefones lhe dêem a possibilidade de escolher o número de contatos ou qualquer coisa, o que parecerá muito estranho se você quisesse que fosse um tipo diferente de número.
awe

Respostas:

92

Impedir o comportamento padrão do evento mousewheel em elementos de número de entrada como sugerido por outros (chamar "blur ()" normalmente não seria a maneira preferida de fazer isso, porque não seria o que o usuário deseja).

MAS. Eu evitaria ouvir o evento mousewheel em todos os elementos de número de entrada o tempo todo e só faria isso quando o elemento estiver em foco (é quando o problema existe). Caso contrário, o usuário não pode rolar a página quando o ponteiro do mouse estiver em qualquer lugar sobre um elemento de número de entrada.

Solução para 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')
})

(Delegue eventos de foco para o elemento de formulário circundante - para evitar muitos ouvintes de eventos, que são ruins para o desempenho).

Grantzau
fonte
2
Ótima resposta. Acho que se eu estivesse fazendo isso hoje, usaria Modernizr.touch para aplicar condicionalmente o ouvinte de evento em dispositivos não sensíveis ao toque. Basicamente, o que o usuário 2863715 disse abaixo.
kjs3 de
Ótima resposta, mas pelo menos no webkit, isso também evita que a janela role enquanto o cursor está sobre o elemento de entrada. Achei que os eventos da roda do mouse deveriam borbulhar também.
Ryan McGeary
Ao aplicar a função de desativação de rolagem a um elemento apenas quando ele está em foco, isso não deve impedir a rolagem, quando o mouse está sobre o elemento de entrada. Mas impede a rolagem da janela, quando o elemento está em foco. E sim, por que o evento da
roda do mouse
6
Esta é uma boa correção para um comportamento que não deveria existir em primeiro lugar.
Jonny
Isso não funciona na maioria das plataformas do tipo Unix gnome, osx, android (com mouse), pois o comportamento de rolagem não requer que a entrada seja focada. Como alternativa, eu apenas usei o tipo de entrada de texto e coloquei minha restrição de número nele (perdendo o atalho de tecla para cima / para baixo, mas também pode ser adicionado com js)
zeachco
40
$(document).on("wheel", "input[type=number]", function (e) {
    $(this).blur();
});
Stovroz
fonte
2
este é aquele que é perfeito.
Patrik Laszlo
Funciona bem. Obrigado.
Vijay S
20

Um ouvinte de evento para governar todos eles

Isto é semelhante ao @Simon Perepelitsa 's resposta em js puro, mas um pouco mais simples, já que coloca um ouvinte de evento no elemento documento e verifica se o elemento focalizado é uma entrada de número:

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

Se você deseja desativar o comportamento de rolagem de valor em alguns campos, mas não em outros, basta fazer isso:

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

com isso:

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

Se uma entrada tiver a classe noscroll, ela não mudará na rolagem, caso contrário, tudo permanecerá igual.

Teste aqui com JSFiddle

Peter Rakmanyi
fonte
Vejo que a votação foi negativa. Se você encontrar um problema, deixe um comentário também com o voto negativo. Se perdi algo, gostaria de saber o que é. Obrigado.
Peter Rakmanyi
1
Para um navegador moderno, use o evento em wheelvez de mousewheel. Referência: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event PS. Não sou eu que votei contra.
vee
19
input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(event){ this.blur() })

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

Para obter um exemplo de jQuery e uma solução para vários navegadores, consulte a pergunta relacionada:

Listener de evento HTML5 para rolagem de entrada de número - apenas Chrome

Simon Perepelitsa
fonte
2
Obrigado Seymon, eu consegui fazer isso para todos os números de entrada com jQuery desta maneira: $(':input[type=number]' ).live('mousewheel',function(e){ $(this).blur(); });usei desfoque em vez de prevenir padrão para não bloquear a página de rolar!
Thibaut Colson
Boa adição sobre desfoque, eu não sabia que você ainda pode permitir a rolagem de página. Eu atualizei minha resposta.
Simon Perepelitsa
13
Para sua informação, live()está obsoleto. Agora, você deve usar on(): $(':input[type=number]').on('mousewheel',function(e){ $(this).blur(); });
Saeid Zebardast 11/10/2013
3
@SaeidZebardast Seu comentário merece ser uma resposta
ranzinza
Para um navegador moderno, use o evento em wheelvez de mousewheel. Referência: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
vee
16

Você pode simplesmente usar o atributo HTML onwheel .

Essa opção não tem efeito na rolagem de outros elementos da página.

E adicionar um ouvinte para todas as entradas não funciona em entradas criadas dinamicamente posteriormente.

Além disso, você pode remover as setas de entrada com 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
fonte
1
Obrigado mano !! funcionou perfeitamente em Angular - Material 6
Nasiruddin Saiyed
Podemos encontrar esse CSS em muitas fontes. Enfim, o CSS é apenas um bônus. A verdadeira solução para a questão é o atributo HTML onwheel. ;-)
Maikon Matheus
5
Gosto desta solução! Obrigado. No meu caso, onBlur()não foi o esperado. Eu configurei um simples em return falsevez de realmente "não fazer nada na roda". <input type="number" onwheel="return false;">
Daniel
14

@Semyon Perepelitsa

Existe uma solução melhor para isso. Desfocar remove o foco da entrada e isso é um efeito colateral que você não deseja. Você deve usar evt.preventDefault em vez disso. Isso evita o comportamento padrão da entrada quando o usuário faz a rolagem. Aqui está o código:

input = document.getElementById("the_number_input")
input.addEventListener("mousewheel", function(evt){ evt.preventDefault(); })
Dibran
fonte
6
Funciona, mas o evento não borbulha, portanto a página não rola. Existe uma maneira de manter o comportamento padrão (rolagem de página) sem borrar?
GuiGS
Para um navegador moderno, use o evento em wheelvez de mousewheel. Referência: developer.mozilla.org/en-US/docs/Web/API/Element/wheel_event
vee
7

Primeiro, você deve interromper o evento da roda do mouse:

  1. Desativando com mousewheel.disableScroll
  2. Interceptando com e.preventDefault();
  3. Ao remover o foco do elemento el.blur();

As duas primeiras abordagens param a janela de rolar e a última remove o foco do elemento; ambos são resultados indesejáveis.

Uma solução alternativa é usar el.blur()e refocar o elemento após um atraso:

$('input[type=number]').on('mousewheel', function(){
  var el = $(this);
  el.blur();
  setTimeout(function(){
    el.focus();
  }, 10);
});
James EJ
fonte
3
Uma solução com boa aparência, mas em testes descobri que essa solução tinha o efeito colateral de roubar o foco. Eu ajustei isso para ter: um teste para ver se o elemento tinha foco: var hadFocus = el.is(':focus');antes do desfoque e, em seguida, um guarda para definir o tempo limite apenas se hadFocs fosse verdadeiro.
Rob Dawson,
2

As respostas fornecidas não funcionam no Firefox (Quantum). O ouvinte de evento precisa ser alterado da roda do mouse para a roda:

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

Este código funciona no Firefox Quantum e Chrome.

Banco Mathias
fonte
2

Para quem trabalha com React e procura uma solução. Descobri que a maneira mais fácil é usar o prop onWheelCapture no componente de entrada como este:

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

Aleš Chromec
fonte
2

Variação datilografada

O typescript precisa saber que você está trabalhando com um HTMLElement para segurança de tipos, caso contrário, você verá muitos Property 'type' does not exist on type 'Element'tipos de erros.

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

Ao tentar resolver isso por mim mesmo, percebi que é realmente possível manter a rolagem da página e o foco da entrada enquanto desativa as alterações de número tentando disparar novamente o evento capturado no elemento pai do <input type="number"/>no qual foi capturado , simplesmente assim:

e.target.parentElement.dispatchEvent(e);

No entanto, isso causa um erro no console do navegador e provavelmente não é garantido que funcione em todos os lugares (eu só testei no Firefox), pois é um código intencionalmente inválido.

Outra solução que funciona bem pelo menos no Firefox e no Chromium é fazer temporariamente o <input>elemento readOnly, assim:

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

Um efeito colateral que observei é que pode fazer com que o campo pisque por uma fração de segundo se você tiver estilos diferentes para os readOnlycampos, mas, pelo menos no meu caso, isso não parece ser um problema.

Da mesma forma, (conforme explicado na resposta de James), em vez de modificar a readOnlypropriedade, você pode blur()inserir o campo e depois focus()voltar, mas, novamente, dependendo dos estilos em uso, pode ocorrer alguma oscilação.

Como alternativa, conforme mencionado em outros comentários aqui, você pode simplesmente ligar preventDefault()para o evento. Supondo que você apenas manipule wheeleventos em entradas de número que estão em foco e sob o cursor do mouse (é isso que as três condições acima significam), o impacto negativo na experiência do usuário seria quase nulo.

Rimas Kudelis
fonte
1

Se você deseja uma solução que não precisa de JavaScript, combinar algumas funcionalidades HTML com um pseudoelemento CSS resolverá o problema:

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>

Isso funciona porque clicar no conteúdo de um <label>que está associado a um campo de formulário focalizará o campo. No entanto, a “vidraça” do pseudoelemento sobre o campo impedirá que os eventos da roda do mouse o alcancem.

A desvantagem é que os botões giratórios para cima / para baixo não funcionam mais, mas você disse que os removeu mesmo assim.


Em teoria, pode-se restaurar o menu de contexto sem exigir que a entrada seja focada primeiro: os :hoverestilos não devem ser disparados quando o usuário rola, uma vez que os navegadores evitam recalculá-los durante a rolagem por motivos de desempenho, mas não cruzei totalmente o navegador / dispositivo testei.

Tigt
fonte
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
fonte
0

A solução mais fácil é adicionar onWheel={ event => event.currentTarget.blur() }}a própria entrada.

TSlegaíte
fonte