it-swarm.com.de

onchange -Ereignis bei Eingabetyp = Bereich wird beim Ziehen nicht in Firefox ausgelöst

Wenn ich mit <input type="range"> gespielt habe, löst Firefox nur ein Onchange-Ereignis aus, wenn der Schieberegler an einer neuen Position abgelegt wird, an der Chrome und andere Onchange-Ereignisse auslösen, während der Schieberegler gezogen wird.

Wie kann ich es beim Ziehen in Firefox schaffen?

HTML

<span id="valBox"></span>
<input type="range" min="5" max="10" step="1" onchange="showVal(this.value)">

SKRIPT

function showVal(newVal){
  document.getElementById("valBox").innerHTML=newVal;
}
213
Prasanth K C

Offensichtlich sind Chrome und Safari falsch: onchange sollte nur ausgelöst werden, wenn der Benutzer die Maus loslässt. Um fortlaufende Updates zu erhalten, sollten Sie das Ereignis oninput verwenden, das Live-Updates in Firefox, Safari und Chrome sowohl über die Maus als auch über die Tastatur erfasst.

oninput wird jedoch nicht im IE10 unterstützt. Daher sollten Sie die beiden Ereignishandler wie folgt kombinieren:

<span id="valBox"></span>
<input type="range" min="5" max="10" step="1" 
   oninput="showVal(this.value)" onchange="showVal(this.value)">

Weitere Informationen finden Sie in diesem Bugzilla-Thread .

390
Frederik

ZUSAMMENFASSUNG:

Ich biete hier eine browserübergreifende No-jQuery-Funktion für Desktop und Mobile, um konsequent auf Bereichs/Schieberegler-Interaktionen zu reagieren, was in aktuellen Browsern nicht möglich ist. Grundsätzlich werden alle Browser gezwungen, das Ereignis on("change"... Von IE11 für ihre Ereignisse on("change"... Oder on("input"... Zu emulieren. Die neue Funktion ist ...

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

... wobei r Ihr Bereichseingabeelement und f Ihr Hörer ist. Der Listener wird nach jeder Interaktion aufgerufen, die den Bereichs-/Schiebereglerwert ändert, jedoch nicht nach Interaktionen, die diesen Wert nicht ändern, einschließlich anfänglicher Maus- oder Berührungsinteraktionen an der aktuellen Schiebereglerposition oder nach dem Loslassen eines der Enden des Schiebereglers.

Problem:

Ab Anfang Juni 2016 unterscheiden sich die verschiedenen Browser in Bezug auf die Reaktion auf die Verwendung des Bereichs/Schiebereglers. Fünf Szenarien sind relevant:

  1. anfängliches Drücken der Maustaste (oder Antippen) an der aktuellen Position des Schiebereglers
  2. anfängliches Drücken der Maustaste (oder Berühren) an einer neuen Position des Schiebereglers
  3. jede nachfolgende Mausbewegung (oder Berührung) nach 1 oder 2 entlang des Schiebereglers
  4. jede nachfolgende Mausbewegung (oder Berührung) nach 1 oder 2 nach jedem Ende des Schiebereglers
  5. abschließendes Mouse-Up (oder Touch-End)

Die folgende Tabelle zeigt, wie sich mindestens drei verschiedene Desktop-Browser in Bezug auf die oben genannten Szenarien in ihrem Verhalten unterscheiden:

table showing browser differences with respect to which events they respond to and when

Lösung:

Die Funktion onRangeChange bietet eine konsistente und vorhersehbare Reaktion zwischen verschiedenen Browsern auf die Interaktionen zwischen Bereich und Schieberegler. Es zwingt alle Browser, sich gemäß der folgenden Tabelle zu verhalten:

table showing behaviour of all browsers using the proposed solution

In IE11 ermöglicht der Code im Wesentlichen, dass alles gemäß dem Status Quo funktioniert, d. H., Dass das Ereignis "change" Auf seine Standardweise funktioniert und das Ereignis "input" Irrelevant ist, da es sowieso nie ausgelöst wird. In anderen Browsern wird das Ereignis "change" Effektiv stummgeschaltet (um zu verhindern, dass zusätzliche und manchmal nicht leicht erkennbare Ereignisse ausgelöst werden). Darüber hinaus wird der Listener des Ereignisses "input" Nur ausgelöst, wenn sich der Wert des Bereichs/Schiebereglers ändert. Bei einigen Browsern (z. B. Firefox) tritt dies auf, weil der Listener in den Szenarien 1, 4 und 5 aus der obigen Liste effektiv stummgeschaltet ist.

(Wenn Sie wirklich benötigen, dass ein Listener in Szenario 1, 4 und/oder 5 aktiviert ist, können Sie versuchen, "mousedown"/"touchstart", "mousemove"/"touchmove" Und/oder "mouseup"/"touchend" - Ereignisse Eine solche Lösung würde den Rahmen dieser Antwort sprengen.)

Funktionalität in mobilen Browsern:

Ich habe diesen Code in Desktop-Browsern getestet, aber nicht in mobilen Browsern. Jedoch in eine andere Antwort auf dieser Seite MBourne hat gezeigt, dass meine Lösung hier "... in jedem Browser funktioniert, den ich finden konnte (Windows-Desktop: IE, Chrome, Opera, FF) ; Android Chrome, Opera und FF, iOS Safari) ". (Danke MBourne.)

Verwendung:

Um diese Lösung zu verwenden, fügen Sie die Funktion onRangeChange aus der obigen Zusammenfassung (vereinfacht/verkleinert) oder den folgenden Demo-Code-Ausschnitt (funktional identisch, aber selbsterklärender) in Ihren eigenen Code ein. Rufen Sie es wie folgt auf:

onRangeChange(myRangeInputElmt, myListener);

dabei ist myRangeInputElmt Ihr gewünschtes <input type="range"> - DOM-Element und myListener die Listener/Handler-Funktion, die Sie bei "change" - ähnlichen Ereignissen aufrufen möchten.

Ihr Listener kann auf Wunsch ohne Parameter sein oder den event -Parameter verwenden, d. H., Je nach Ihren Anforderungen würde eine der folgenden Möglichkeiten funktionieren:

var myListener = function() {...

oder

var myListener = function(evt) {...

(Das Entfernen des Ereignis-Listeners aus dem input -Element (z. B. mit removeEventListener) wird in dieser Antwort nicht angesprochen.)

Demo Beschreibung:

Im folgenden Code-Snippet bietet die Funktion onRangeChange die universelle Lösung. Der Rest des Codes ist nur ein Beispiel, um seine Verwendung zu demonstrieren. Jede Variable, die mit my... Beginnt, ist für die universelle Lösung irrelevant und nur für die Demo vorhanden.

Die Demo zeigt den Bereichs-/Schiebereglerwert sowie die Häufigkeit, mit der die Standardereignisse "change", "input" Und benutzerdefinierten Ereignisse "onRangeChange" Ausgelöst wurden (Zeilen A, B bzw. C). . Beachten Sie beim Ausführen dieses Snippets in verschiedenen Browsern Folgendes, wenn Sie mit dem Bereich/Schieberegler interagieren:

  • In IE11 ändern sich die Werte in den Zeilen A und C in den obigen Szenarien 2 und 3, während sich Zeile B nie ändert.
  • In Chrome und Safari ändern sich die Werte in Zeile B und C in Szenario 2 und 3, während sich Zeile A nur für Szenario 5 ändert.
  • In Firefox ändert sich der Wert in Zeile A nur für Szenario 5, Zeile B für alle fünf Szenarios und Zeile C nur für Szenarios 2 und 3.
  • In allen obigen Browsern sind die Änderungen in Zeile C (die vorgeschlagene Lösung) identisch, d. H. Nur für die Szenarien 2 und 3.

Demo-Code:

// 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>

Gutschrift:

Während die Implementierung hier weitgehend meine eigene ist, wurde sie von MBournes Antwort inspiriert. Diese andere Antwort deutete darauf hin, dass die Ereignisse "input" und "change" zusammengeführt werden könnten und der resultierende Code sowohl in Desktop- als auch in mobilen Browsern funktionieren würde. Der Code in dieser Antwort führt jedoch dazu, dass versteckte "zusätzliche" Ereignisse ausgelöst werden, was an und für sich problematisch ist, und die Ereignisse unterscheiden sich zwischen den Browsern, ein weiteres Problem. Meine Implementierung hier löst diese Probleme.

Schlüsselwörter:

JavaScript Eingabetyp Bereich Schieberegler Ereignisse Änderung der Kompatibilität des Eingabe-Browsers Cross-Browser-Desktop Mobil No-JQuery

32
Andrew Willems

UPDATE: Ich lasse diese Antwort hier als ein Beispiel, wie Mausereignisse verwendet werden, um Bereichs-/Schieberegler-Interaktionen in Desktop-Browsern (nicht jedoch in mobilen Browsern) zu verwenden. Allerdings habe ich jetzt auch eine völlig andere und meiner Meinung nach bessere Antwort anderswo auf dieser Seite geschrieben, die einen anderen Ansatz für die Bereitstellung einer browserübergreifenden Desktop -and-Mobile -Lösung für dieses Problem verwendet Problem.

Ursprüngliche Antwort:

Summary: Eine Browser-übergreifende, reine JavaScript-Lösung (d. H. No-jQuery), die das Lesen von Bereichseingabewerten zulässt, ohne dass on('input'... und/oder on('change'... verwendet werden, die zwischen Browsern inkonsistent arbeiten.

Seit heute (Ende Februar 2016) gibt es immer noch Inkonsistenzen in Browsern, daher werde ich hier eine neue Lösung finden.

Das Problem: Bei Verwendung einer Bereichseingabe, d. H. Eines Schiebereglers, liefert on('input'... in Mac und Windows Firefox, Chrome und Opera sowie Mac Safari fortlaufend aktualisierte Bereichswerte, während on('change'... den Bereichswert nur bei Mausklick meldet. Im Gegensatz dazu funktioniert on('input'... in Internet Explorer (v11) überhaupt nicht und on('change'... wird kontinuierlich aktualisiert.

Ich berichte hier zwei Strategien, um in allen Browsern unter Verwendung von Vanilla JavaScript (d. H. Ohne jQuery) identische Werte für den kontinuierlichen Bereichswert zu erhalten, indem die Ereignisse "Mousedown", "Mousemove" und (möglicherweise) "Mouseup" verwendet werden.

Strategie 1: Kürzer aber weniger effizient

Wenn Sie einen kürzeren Code gegenüber einem effizienteren Code bevorzugen, können Sie diese 1st-Lösung verwenden, die mousedown und mousemove verwendet, nicht jedoch Mouseup. Dadurch wird der Schieberegler bei Bedarf ausgelesen, er wird jedoch bei Mouse-Over-Ereignissen unnötig weiter ausgelöst, auch wenn der Benutzer nicht geklickt hat und daher den Schieberegler nicht zieht. Es liest im Wesentlichen den Bereichswert sowohl nach "mousedown" als auch während "mousemove" -Ereignissen, wobei jedes mit requestAnimationFrame etwas verzögert wird.

var rng = document.querySelector("input");

read("mousedown");
read("mousemove");
read("keydown"); // include this to also allow keyboard control

function read(evtType) {
  rng.addEventListener(evtType, function() {
    window.requestAnimationFrame(function () {
      document.querySelector("div").innerHTML = rng.value;
      rng.setAttribute("aria-valuenow", rng.value); // include for accessibility
    });
  });
}
<div>50</div><input type="range"/>

Strategie 2: Länger aber effizienter

Wenn Sie effizienteren Code benötigen und längere Codelänge tolerieren können, können Sie die folgende Lösung verwenden, die Mousedown, Mousemove und Mouseup verwendet. Dies liest auch den Schieberegler nach Bedarf, stoppt das Lesen jedoch angemessen, sobald die Maustaste losgelassen wird. Der wesentliche Unterschied besteht darin, dass nach 'mousedown' erst nach 'mousemove' zu hören beginnt und nach 'mouseup' nicht mehr nach 'mousemove' gesucht wird.

var rng = document.querySelector("input");

var listener = function() {
  window.requestAnimationFrame(function() {
    document.querySelector("div").innerHTML = rng.value;
  });
};

rng.addEventListener("mousedown", function() {
  listener();
  rng.addEventListener("mousemove", listener);
});
rng.addEventListener("mouseup", function() {
  rng.removeEventListener("mousemove", listener);
});

// include the following line to maintain accessibility
// by allowing the listener to also be fired for
// appropriate keyboard events
rng.addEventListener("keydown", listener);
<div>50</div><input type="range"/>

Demo: Ausführlichere Erklärung der Notwendigkeit und Durchführung der oben genannten Problemumgehungen

Im folgenden Code werden zahlreiche Aspekte dieser Strategie ausführlicher beschrieben. Erklärungen sind in die Demonstration eingebettet:

var select, inp, listen, unlisten, anim, show, onInp, onChg, onDn1, onDn2, onMv1, onMv2, onUp, onMvCombo1, onDnCombo1, onUpCombo2, onMvCombo2, onDnCombo2;

select   = function(selctr)     { return document.querySelector(selctr);      };
inp = select("input");
listen   = function(evtTyp, cb) { return inp.   addEventListener(evtTyp, cb); };
unlisten = function(evtTyp, cb) { return inp.removeEventListener(evtTyp, cb); };
anim     = function(cb)         { return window.requestAnimationFrame(cb);    };
show = function(id) {
	return function() {
    select("#" + id + " td~td~td"   ).innerHTML = inp.value;
    select("#" + id + " td~td~td~td").innerHTML = (Math.random() * 1e20).toString(36); // random text
  };
};

onInp      =                  show("inp" )                                      ;
onChg      =                  show("chg" )                                      ;
onDn1      =                  show("mdn1")                                      ;
onDn2      = function() {anim(show("mdn2"));                                   };
onMv1      =                  show("mmv1")                                      ;
onMv2      = function() {anim(show("mmv2"));                                   };
onUp       =                  show("mup" )                                      ;
onMvCombo1 = function() {anim(show("cmb1"));                                   };
onDnCombo1 = function() {anim(show("cmb1"));   listen("mousemove", onMvCombo1);};
onUpCombo2 = function() {                    unlisten("mousemove", onMvCombo2);};
onMvCombo2 = function() {anim(show("cmb2"));                                   };
onDnCombo2 = function() {anim(show("cmb2"));   listen("mousemove", onMvCombo2);};

listen("input"    , onInp     );
listen("change"   , onChg     );
listen("mousedown", onDn1     );
listen("mousedown", onDn2     );
listen("mousemove", onMv1     );
listen("mousemove", onMv2     );
listen("mouseup"  , onUp      );
listen("mousedown", onDnCombo1);
listen("mousedown", onDnCombo2);
listen("mouseup"  , onUpCombo2);
table {border-collapse: collapse; font: 10pt Courier;}
th, td {border: solid black 1px; padding: 0 0.5em;}
input {margin: 2em;}
li {padding-bottom: 1em;}
<p>Click on 'Full page' to see the demonstration properly.</p>
<table>
  <tr><th></th><th>event</th><th>range value</th><th>random update indicator</th></tr>
  <tr id="inp" ><td>A</td><td>input                                </td><td>100</td><td>-</td></tr>
  <tr id="chg" ><td>B</td><td>change                               </td><td>100</td><td>-</td></tr>
  <tr id="mdn1"><td>C</td><td>mousedown                            </td><td>100</td><td>-</td></tr>
  <tr id="mdn2"><td>D</td><td>mousedown using requestAnimationFrame</td><td>100</td><td>-</td></tr>
  <tr id="mmv1"><td>E</td><td>mousemove                            </td><td>100</td><td>-</td></tr>
  <tr id="mmv2"><td>F</td><td>mousemove using requestAnimationFrame</td><td>100</td><td>-</td></tr>
  <tr id="mup" ><td>G</td><td>mouseup                              </td><td>100</td><td>-</td></tr>
  <tr id="cmb1"><td>H</td><td>mousedown/move combo                 </td><td>100</td><td>-</td></tr>
  <tr id="cmb2"><td>I</td><td>mousedown/move/up combo              </td><td>100</td><td>-</td></tr>
</table>
<input type="range" min="100" max="999" value="100"/>
<ol>
  <li>The 'range value' column shows the value of the 'value' attribute of the range-type input, i.e. the slider. The 'random update indicator' column shows random text as an indicator of whether events are being actively fired and handled.</li>
  <li>To see browser differences between input and change event implementations, use the slider in different browsers and compare A and&nbsp;B.</li>
  <li>To see the importance of 'requestAnimationFrame' on 'mousedown', click a new location on the slider and compare C&nbsp;(incorrect) and D&nbsp;(correct).</li>
  <li>To see the importance of 'requestAnimationFrame' on 'mousemove', click and drag but do not release the slider, and compare E&nbsp;(often 1&nbsp;pixel behind) and F&nbsp;(correct).</li>
  <li>To see why an initial mousedown is required (i.e. to see why mousemove alone is insufficient), click and hold but do not drag the slider and compare E&nbsp;(incorrect), F&nbsp;(incorrect) and H&nbsp;(correct).</li>
  <li>To see how the mouse event combinations can provide a work-around for continuous update of a range-type input, use the slider in any manner and note whichever of A or B continuously updates the range value in your current browser. Then, while still using the slider, note that H and I provide the same continuously updated range value readings as A or B.</li>
  <li>To see how the mouseup event reduces unnecessary calculations in the work-around, use the slider in any manner and compare H and&nbsp;I. They both provide correct range value readings. However, then ensure the mouse is released (i.e. not clicked) and move it over the slider without clicking and notice the ongoing updates in the third table column for H but not&nbsp;I.</li>
</ol>

29
Andrew Willems

Ich poste dies als Antwort, weil es seine eigene Antwort ist und nicht einen Kommentar unter einer weniger nützlichen Antwort. Ich finde diese Methode viel besser als die akzeptierte Antwort, da sie alle Js in einer vom HTML getrennten Datei halten kann.

Antwort von Jamrelian in seinem Kommentar unter der akzeptierten Antwort.

$("#myelement").on("input change", function() {
    //do something
});

Seien Sie sich jedoch dieses Kommentars von Jaime bewusst

Beachten Sie, dass Sie bei dieser Lösung in Chrome zwei Aufrufe an den Handler erhalten (einen pro Ereignis). Wenn Sie sich darum kümmern, müssen Sie sich dagegen schützen.

In diesem Fall wird das Ereignis ausgelöst, wenn Sie die Maus nicht mehr bewegen, und dann erneut, wenn Sie die Maustaste loslassen.

25
Daniel Tonon

Die Lösungen von Andrew Willem sind nicht mit mobilen Geräten kompatibel. 

Hier ist eine Modifikation seiner zweiten Lösung, die in Edge, IE, Opera, FF, Chrome, iOS Safari und mobilen Entsprechungen funktioniert (die ich testen konnte):

Update 1: "requestAnimationFrame" Teil entfernt, da ich damit einverstanden bin, dass es nicht notwendig ist:  

var listener = function() {
  // do whatever
};

slider1.addEventListener("input", function() {
  listener();
  slider1.addEventListener("change", listener);
});
slider1.addEventListener("change", function() {
  listener();
  slider1.removeEventListener("input", listener);
}); 

Update 2: Antwort auf die aktualisierte Antwort von Andrews 2. Juni 2016:  

Danke, Andrew - das scheint in jedem Browser zu funktionieren, den ich finden konnte (Windows-Desktop: IE, Chrome, Opera, FF; Android Chrome, Opera und FF, iOS Safari).

Update 3: if ("oninput in slider) lösung

Das Folgende scheint in allen oben genannten Browsern zu funktionieren. (Ich kann die ursprüngliche Quelle jetzt nicht finden.) Ich habe dies verwendet, scheiterte jedoch später an IE, und ich suchte nach einer anderen Quelle, daher landete ich hier.

if ("oninput" in slider1) {
    slider1.addEventListener("input", function () {
        // do whatever;
    }, false);
}

Aber bevor ich Ihre Lösung überprüfte, bemerkte ich, dass dies erneut in IE funktionierte.

4
MBourne

Für ein gutes Browser-übergreifendes Verhalten und für weniger verständlichen Code ist es am besten, das Attribut onchange in Kombination mit einem Formular zu verwenden:

Dies ist eine reine HTML-/Javascript-Lösung, die auch inline verwendet werden kann.

function showVal(){
 document.getElementById("valBox").innerHTML=document.getElementById("inVal").value;

}
<form onchange="showVal()">
  <input type="range" min="5" max="10" step="1" id="inVal">
  </form>

<span id="valBox">
  </span>

1
Cryptopat

Noch ein anderer Ansatz: Setzen Sie einfach ein Flag auf ein Element, das anzeigt, welche Art von Ereignis behandelt werden soll:

function setRangeValueChangeHandler(rangeElement, handler) {
    rangeElement.oninput = (event) => {
        handler(event);
        // Save flag that we are using onInput in current browser
        event.target.onInputHasBeenCalled = true;
    };

    rangeElement.onchange = (event) => {
        // Call only if we are not using onInput in current browser
        if (!event.target.onInputHasBeenCalled) {
            handler(event);
        }
    };
}
0
Nikita