RESUMO:
Fornecemos aqui a capacidade de desktop e celular no navegador sem jQuery para responder consistentemente a interações de intervalo / controle deslizante, algo que não é possível nos navegadores atuais. É essencialmente obriga todos os navegadores para emular do IE11 on("change"...
evento, quer para os seus on("change"...
ou on("input"...
eventos. A nova função é ...
function onRangeChange(r,f) {
var n,c,m;
r.addEventListener("input",function(e){n=1;c=e.target.value;if(c!=m)f(e);m=c;});
r.addEventListener("change",function(e){if(!n)f(e);});
}
... onde r
está seu elemento de entrada de intervalo e f
é seu ouvinte. O ouvinte será chamado após qualquer interação que altere o valor do intervalo / controle deslizante, mas não após as interações que não alteram esse valor, incluindo as interações iniciais do mouse ou toque na posição atual do controle deslizante ou ao sair de qualquer extremidade do controle deslizante.
Problema:
No início de junho de 2016, diferentes navegadores diferem em termos de como eles respondem ao uso do intervalo / controle deslizante. Cinco cenários são relevantes:
- inicial do mouse para baixo (ou toque inicial) na posição atual do controle deslizante
- inicial do mouse para baixo (ou toque inicial) em uma nova posição do controle deslizante
- qualquer movimento subsequente do mouse (ou toque) após 1 ou 2 ao longo do controle deslizante
- qualquer movimento subsequente do mouse (ou toque) após 1 ou 2 após a extremidade do controle deslizante
- mouse final (ou touch-end) final
A tabela a seguir mostra como pelo menos três navegadores de desktop diferentes diferem em seu comportamento em relação a quais dos cenários acima eles respondem:
Solução:
A onRangeChange
função fornece uma resposta consistente e previsível entre navegadores às interações de intervalo / controle deslizante. Força todos os navegadores a se comportarem de acordo com a tabela a seguir:
No IE11, o código essencialmente permite que tudo opere conforme o status quo, ou seja, permite que o "change"
evento funcione da maneira padrão e o "input"
evento é irrelevante, pois nunca é acionado. Em outros navegadores, o "change"
evento é efetivamente silenciado (para impedir que eventos extras e às vezes não-aparentes sejam disparados). Além disso, o"input"
evento aciona seu ouvinte apenas quando o valor do intervalo / controle deslizante é alterado. Para alguns navegadores (por exemplo, Firefox), isso ocorre porque o ouvinte é efetivamente silenciado nos cenários 1, 4 e 5 da lista acima.
(Se você realmente deseja que um ouvinte seja ativado nos cenários 1, 4 e / ou 5, tente incorporar "mousedown"
/ "touchstart"
, "mousemove"
/ "touchmove"
e / ou "mouseup"
/ "touchend"
eventos. Essa solução está além do escopo desta resposta.)
Funcionalidade em navegadores móveis:
Testei esse código em navegadores de desktop, mas não em navegadores móveis. No entanto, em outra resposta desta página, o MBourne mostrou que minha solução aqui "... parece funcionar em todos os navegadores que eu encontrei (Win desktop: IE, Chrome, Opera, FF; Android Chrome, Opera e FF, iOS Safari) " . (Obrigado MBourne.)
Uso:
Para usar esta solução, inclua a onRangeChange
função do resumo acima (simplificado / minificado) ou o trecho de código de demonstração abaixo (funcionalmente idêntico, mas mais auto-explicativo) em seu próprio código. Chame da seguinte maneira:
onRangeChange(myRangeInputElmt, myListener);
onde myRangeInputElmt
está o <input type="range">
elemento DOM desejado e myListener
é a função de ouvinte / manipulador que você deseja chamar em "change"
eventos semelhantes.
Seu ouvinte pode ter menos parâmetros, se desejado, ou pode usar o event
parâmetro, ou seja, qualquer um dos seguintes procedimentos funcionará, dependendo de suas necessidades:
var myListener = function() {...
ou
var myListener = function(evt) {...
(Remover o ouvinte de evento do input
elemento (por exemplo, usar removeEventListener
) não é abordado nesta resposta.)
Descrição da demonstração:
No trecho de código abaixo, a função onRangeChange
fornece a solução universal. O restante do código é simplesmente um exemplo para demonstrar seu uso. Qualquer variável que comece com my...
é irrelevante para a solução universal e está presente apenas para fins de demonstração.
Os shows de demonstração do valor / intervalo de controle deslizante, bem como o número de vezes que o padrão "change"
, "input"
e personalizados "onRangeChange"
eventos têm disparados (linhas A, B e C, respectivamente). Ao executar esse snippet em diferentes navegadores, observe o seguinte ao interagir com o intervalo / controle deslizante:
- No IE11, os valores nas linhas A e C mudam nos cenários 2 e 3 acima, enquanto a linha B nunca muda.
- No Chrome e Safari, os valores nas linhas B e C mudam nos cenários 2 e 3, enquanto a linha A muda apenas para o cenário 5.
- No Firefox, o valor na linha A é alterado apenas para o cenário 5, a linha B é alterada para todos os cinco cenários e a linha C é alterada apenas para os cenários 2 e 3.
- Em todos os navegadores acima, as alterações na linha C (a solução proposta) são idênticas, ou seja, apenas para os cenários 2 e 3.
Código de demonstração:
// main function for emulating IE11's "change" event:
function onRangeChange(rangeInputElmt, listener) {
var inputEvtHasNeverFired = true;
var rangeValue = {current: undefined, mostRecent: undefined};
rangeInputElmt.addEventListener("input", function(evt) {
inputEvtHasNeverFired = false;
rangeValue.current = evt.target.value;
if (rangeValue.current !== rangeValue.mostRecent) {
listener(evt);
}
rangeValue.mostRecent = rangeValue.current;
});
rangeInputElmt.addEventListener("change", function(evt) {
if (inputEvtHasNeverFired) {
listener(evt);
}
});
}
// example usage:
var myRangeInputElmt = document.querySelector("input" );
var myRangeValPar = document.querySelector("#rangeValPar" );
var myNumChgEvtsCell = document.querySelector("#numChgEvtsCell");
var myNumInpEvtsCell = document.querySelector("#numInpEvtsCell");
var myNumCusEvtsCell = document.querySelector("#numCusEvtsCell");
var myNumEvts = {input: 0, change: 0, custom: 0};
var myUpdate = function() {
myNumChgEvtsCell.innerHTML = myNumEvts["change"];
myNumInpEvtsCell.innerHTML = myNumEvts["input" ];
myNumCusEvtsCell.innerHTML = myNumEvts["custom"];
};
["input", "change"].forEach(function(myEvtType) {
myRangeInputElmt.addEventListener(myEvtType, function() {
myNumEvts[myEvtType] += 1;
myUpdate();
});
});
var myListener = function(myEvt) {
myNumEvts["custom"] += 1;
myRangeValPar.innerHTML = "range value: " + myEvt.target.value;
myUpdate();
};
onRangeChange(myRangeInputElmt, myListener);
table {
border-collapse: collapse;
}
th, td {
text-align: left;
border: solid black 1px;
padding: 5px 15px;
}
<input type="range"/>
<p id="rangeValPar">range value: 50</p>
<table>
<tr><th>row</th><th>event type </th><th>number of events </th><tr>
<tr><td>A</td><td>standard "change" events </td><td id="numChgEvtsCell">0</td></tr>
<tr><td>B</td><td>standard "input" events </td><td id="numInpEvtsCell">0</td></tr>
<tr><td>C</td><td>new custom "onRangeChange" events</td><td id="numCusEvtsCell">0</td></tr>
</table>
Crédito:
Embora a implementação aqui seja em grande parte minha, foi inspirada na resposta de MBourne . Essa outra resposta sugeriu que os eventos "input" e "change" pudessem ser mesclados e que o código resultante funcionaria nos navegadores de desktop e móvel. No entanto, o código nessa resposta resulta no acionamento de eventos "extras" ocultos, o que por si só é problemático e os eventos acionados diferem entre os navegadores, um problema adicional. Minha implementação aqui resolve esses problemas.
Palavras-chave:
Eventos de controle deslizante de intervalo do tipo de entrada JavaScript alteram a compatibilidade do navegador de entrada desktop no navegador móvel no-jQuery
onchange
não dispara. Foi na solução desse problema que encontrei esta pergunta.