SOMMARIO:
Fornisco qui una capacità desktop-e-mobile no-jQuery cross-browser per rispondere in modo coerente alle interazioni intervallo / dispositivo di scorrimento, cosa impossibile nei browser attuali. In pratica costringe tutti i browser a emulare l' on("change"...
evento IE11 per i loro eventi on("change"...
o on("input"...
. La nuova funzione è ...
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);});
}
... dov'è r
l'elemento di input del range ed f
è il tuo ascoltatore. L'ascoltatore verrà chiamato dopo qualsiasi interazione che modifica il valore dell'intervallo / dispositivo di scorrimento, ma non dopo interazioni che non modificano quel valore, comprese le interazioni iniziali del mouse o del tocco nella posizione corrente del dispositivo di scorrimento o quando si sposta da una delle estremità del dispositivo di scorrimento.
Problema:
All'inizio di giugno 2016, diversi browser si differenziano per il modo in cui rispondono all'utilizzo del range / cursore. Sono rilevanti cinque scenari:
- mouse iniziale in basso (o touch-start) nella posizione corrente del cursore
- mouse-down iniziale (o touch-start) in una nuova posizione del cursore
- qualsiasi successivo movimento del mouse (o tocco) dopo 1 o 2 lungo il cursore
- qualsiasi movimento successivo del mouse (o tocco) dopo 1 o 2 oltre una delle estremità del cursore
- mouse-up finale (o touch-end)
La tabella seguente mostra come almeno tre diversi browser desktop differiscono nel loro comportamento rispetto a quale dei suddetti scenari rispondono:
Soluzione:
La onRangeChange
funzione fornisce una risposta cross-browser coerente e prevedibile alle interazioni intervallo / cursore. Forza tutti i browser a comportarsi secondo la seguente tabella:
In IE11, il codice consente essenzialmente a tutto di funzionare secondo lo status quo, ovvero consente "change"
all'evento di funzionare nel suo modo standard e l' "input"
evento è irrilevante in quanto non si attiva comunque. In altri browser, l' "change"
evento viene efficacemente messo a tacere (per evitare che vengano attivati eventi extra e talvolta non apparentemente evidenti). Inoltre, il"input"
evento attiva il suo ascoltatore solo quando cambia il valore dell'intervallo / cursore. Per alcuni browser (ad esempio Firefox) ciò si verifica perché l'ascoltatore è effettivamente messo a tacere negli scenari 1, 4 e 5 dall'elenco precedente.
(Se si richiede veramente l'attivazione di un ascoltatore in uno degli scenari 1, 4 e / o 5, è possibile provare a incorporare "mousedown"
/ "touchstart"
, "mousemove"
/ "touchmove"
e / o "mouseup"
/ "touchend"
eventi. Tale soluzione va oltre lo scopo di questa risposta.)
Funzionalità nei browser mobili:
Ho testato questo codice nei browser desktop ma non in tutti i browser mobili. Tuttavia, in un'altra risposta su questa pagina MBourne ha dimostrato che la mia soluzione qui "... sembra funzionare in tutti i browser che ho trovato (Win desktop: IE, Chrome, Opera, FF; Android Chrome, Opera e FF, iOS Safari) " . (Grazie MBourne.)
Uso:
Per utilizzare questa soluzione, includere la onRangeChange
funzione dal riepilogo sopra (semplificato / minimizzato) o lo snippet di codice demo di seguito (funzionalmente identico ma più autoesplicativo) nel proprio codice. Invocalo come segue:
onRangeChange(myRangeInputElmt, myListener);
dove si myRangeInputElmt
trova l' <input type="range">
elemento DOM desiderato ed myListener
è la funzione listener / handler su cui si desidera richiamare "change"
eventi simili.
Il tuo listener può essere privo di parametri se lo desideri o può utilizzare il event
parametro, ovvero una delle seguenti funzioni funzionerebbe, a seconda delle tue esigenze:
var myListener = function() {...
o
var myListener = function(evt) {...
(La rimozione del listener di eventi input
dall'elemento (ad es. Utilizzo removeEventListener
) non viene risolta in questa risposta.)
Descrizione della demo:
Nel frammento di codice seguente, la funzione onRangeChange
fornisce la soluzione universale. Il resto del codice è semplicemente un esempio per dimostrarne l'utilizzo. Qualsiasi variabile che inizia con my...
è irrilevante per la soluzione universale ed è presente solo per il bene della demo.
Gli spettacoli demo valore gamma / cursore così come il numero di volte lo standard "change"
, "input"
e personalizzate "onRangeChange"
eventi sono alimentate (righe A, B e C, rispettivamente). Quando esegui questo frammento in diversi browser, tieni presente quanto segue mentre interagisci con l'intervallo / dispositivo di scorrimento:
- In IE11, i valori nelle righe A e C cambiano entrambi negli scenari 2 e 3 sopra mentre la riga B non cambia mai.
- In Chrome e Safari, i valori nelle righe B e C cambiano entrambi negli scenari 2 e 3 mentre la riga A cambia solo per lo scenario 5.
- In Firefox, il valore nella riga A cambia solo per lo scenario 5, la riga B cambia per tutti e cinque gli scenari e la riga C cambia solo per gli scenari 2 e 3.
- In tutti i browser di cui sopra, le modifiche nella riga C (la soluzione proposta) sono identiche, vale a dire solo per gli scenari 2 e 3.
Codice demo:
// 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>
Credito:
Mentre l'implementazione qui è in gran parte la mia, è stata ispirata dalla risposta di MBourne . L'altra risposta suggeriva che gli eventi "input" e "change" potevano essere uniti e che il codice risultante avrebbe funzionato sia nei browser desktop che mobili. Tuttavia, il codice in quella risposta comporta la generazione di eventi "extra" nascosti, il che è di per sé problematico e gli eventi generati differiscono tra i browser, un ulteriore problema. La mia implementazione qui risolve questi problemi.
parole chiave:
Gli eventi del dispositivo di scorrimento dell'intervallo del tipo di input JavaScript cambiano la compatibilità del browser di input cross-browser desktop mobile no-jQuery
onchange
non spara. È stato nella risoluzione di questo problema che ho trovato questa domanda.