input type = range üzerindeki onchange olayı sürüklerken firefox'ta tetiklenmiyor


252

Birlikte oynadığımda <input type="range">, Firefox yalnızca kaydırıcıyı kaydırıcı sürüklenirken Chrome'un ve başkalarının onchange olaylarını tetiklediği yeni bir konuma düşürdüğümüzde bir değiştirme olayını tetikler.

Firefox'ta sürüklerken bunu nasıl gerçekleştirebilirim?

function showVal(newVal){
    document.getElementById("valBox").innerHTML=newVal;
}
<span id="valBox"></span>
<input type="range" min="5" max="10" step="1" onchange="showVal(this.value)">


4
Aralık öğesinin odağı varsa, ok tuşlarını kullanarak kaydırıcıyı hareket ettirebilirsiniz. Ve bu durumda da onchangeateş etmiyor. Bu sorunu giderirken bu soruyu buldum.
Sildoreth

Yanıtlar:


452

Görünüşe göre Chrome ve Safari yanlış: onchangeyalnızca kullanıcı fareyi serbest bıraktığında tetiklenmelidir. Sürekli güncellemeler almak için oninput, hem fareden hem de klavyeden Firefox, Safari ve Chrome'da canlı güncellemeleri yakalayacak etkinliği kullanmalısınız .

Bununla birlikte, oninputIE10'da desteklenmez, bu nedenle en iyi seçeneğiniz, iki olay işleyiciyi birleştirmektir:

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

Daha fazla bilgi için bu Bugzilla dizisine göz atın .


113
Veya $("#myelement").on("input change", function() { doSomething(); });jQuery ile.
Alex Vang

19
Sadece bu çözümle, kromda işleyiciye iki olay (her olay için bir tane) alacağınızı unutmayın, bu yüzden bunu önemsiyorsanız, buna karşı korunmanız gerekir.
Jaime

3
Mobil kromda (v34) ateşlenmeyen 'değişiklik' olayıyla ilgili sorunlar yaşıyordum, ancak denkleme 'girdi' eklemek, başka hiçbir yerde zararlı bir etkisi olmamakla birlikte benim için düzeltti.
brins0

7
@Jaime: " Eğer ilgilenirsen, o zaman ona karşı korunmalısın . " Nasıl yapabilirim, lütfen?

2
Ne yazık ki, ve onchange()gibi mobil web'de çalışmaz . Başka bir öneriniz var mı? Android ChromeiOS Safari
Alston

41

ÖZET:

Burada, mevcut tarayıcılarda mümkün olmayan bir şey olan aralık / kaydırıcı etkileşimlerine sürekli olarak yanıt vermek için no-jQuery çapraz tarayıcı masaüstü ve mobil yeteneği sağlıyorum. Temel olarak tüm tarayıcıları IE11'in on("change"...etkinliğinion("change"...on("input"... olaylarını veya olayları . Yeni fonksiyon ...

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

...nerede r aralık giriş elemanınız ve fdinleyiciniz. Dinleyici, aralık / kaydırıcı değerini değiştiren herhangi bir etkileşimden sonra ancak geçerli kaydırıcı konumundaki ilk fare veya dokunma etkileşimleri dahil veya kaydırıcının her iki ucundan çıktıktan sonra bu değeri değiştirmeyen etkileşimlerden sonra çağrılmaz.

Sorun:

Haziran 2016 başından itibaren, farklı tarayıcılar aralık / kaydırıcı kullanımına nasıl yanıt verdikleri açısından farklılık gösterir. Beş senaryo önemlidir:

  1. geçerli kaydırıcı konumunda ilk fare aşağı (veya dokunmatik başlat)
  2. yeni bir kaydırıcı konumunda fareyle üzerine gelme (veya dokunmaya başlama)
  3. kaydırıcı boyunca 1 veya 2'den sonraki herhangi bir fare (veya dokunma) hareketi
  4. kaydırıcının her iki ucundan 1 veya 2 sonra herhangi bir sonraki fare (veya dokunma) hareketi
  5. son mouse-up (veya dokunma ucu)

Aşağıdaki tabloda, en az üç farklı masaüstü tarayıcısının yukarıdaki senaryolardan hangilerine yanıt verdiklerine göre davranışlarında nasıl farklılıklar gösterdiği gösterilmiştir:

hangi olaylara ve ne zaman yanıt verdiklerine ilişkin tarayıcı farklılıklarını gösteren tablo

Çözüm:

onRangeChangeFonksiyon aralığı / sürgü etkileşimleri için istikrarlı ve tahmin edilebilen bir çapraz tarayıcı tepkisi sağlar. Tüm tarayıcıları aşağıdaki tabloya göre davranmaya zorlar:

önerilen çözümü kullanan tüm tarayıcıların davranışlarını gösteren tablo

IE11'de, kod temelde her şeyin statükoya göre çalışmasına izin verir, yani "change"olayın standart şekilde çalışmasına izin verir ve olay "input"hiçbir zaman tetiklenmediğinden önemsizdir. Diğer tarayıcılarda, "change"etkinlik etkili bir şekilde susturulur (fazladan ve bazen kolayca görünmeyen olayların tetiklenmesini önlemek için). Ayrıca, "input"olay dinleyicisini yalnızca aralık / kaydırıcının değeri değiştiğinde tetikler. Bazı tarayıcılarda (örn. Firefox), bu durum dinleyicinin yukarıdaki listeden 1, 4 ve 5 senaryolarında etkili bir şekilde susturulması nedeniyle oluşur.

(Senaryo 1, 4 ve / veya 5'te gerçekten bir dinleyicinin etkinleştirilmesini istiyorsanız, "mousedown"/ "touchstart", "mousemove"/ "touchmove"ve / veya "mouseup"/ "touchend"olaylarını birleştirmeyi deneyebilirsiniz . Böyle bir çözüm bu cevabın kapsamı dışındadır.)

Mobil Tarayıcılarda İşlevler:

Bu kodu masaüstü tarayıcılarda test ettim ancak mobil tarayıcılarda test etmedim. Ancak, bu sayfadaki başka bir cevapta MBourne buradaki çözümümün "... bulabildiğim her tarayıcıda çalıştığını gösteriyor (Masaüstü kazan: IE, Chrome, Opera, FF; Android Chrome, Opera ve FF, iOS Safari) " . (Teşekkürler MBourne.)

Kullanımı:

Bu çözümü kullanmak için, onRangeChangeyukarıdaki özetten (basitleştirilmiş / küçültülmüş) işlevi veya aşağıdaki demo kod snippet'ini (işlevsel olarak özdeş ancak daha açıklayıcı) kendi kodunuza ekleyin. Aşağıdaki gibi çağırın:

onRangeChange(myRangeInputElmt, myListener);

myRangeInputElmtistediğiniz <input type="range">DOM öğesi nerede ve benzeri etkinliklerde myListenerçağrılmasını istediğiniz dinleyici / işleyici işlevidir "change".

Dinleyiciniz istenirse parametresiz olabilir veya eventparametreyi kullanabilir , yani ihtiyaçlarınıza bağlı olarak aşağıdakilerden biri işe yarayabilir:

var myListener = function() {...

veya

var myListener = function(evt) {...

(Olay dinleyicisini inputöğeden kaldırmak (örn. Kullanmak removeEventListener) bu cevapta ele alınmamıştır.)

Demo Açıklaması:

Aşağıdaki kod snippet'inde işlev onRangeChange, evrensel bir çözüm sağlar. Kodun geri kalanı sadece kullanımını göstermek için bir örnektir. my...İle başlayan herhangi bir değişken evrensel çözümle ilgisizdir ve sadece demo için mevcuttur.

Demo aralığı / kaydırıcı değeri olarak kez standart sayısı gösterir "change", "input"ve özel "onRangeChange"etkinlikleri tetiklenene (sıra, sırasıyla A, B ve C). Bu snippet'i farklı tarayıcılarda çalıştırırken, aralık / kaydırıcıyla etkileşime girerken aşağıdakilere dikkat edin:

  • IE11'de, A ve C satırlarındaki değerlerin her ikisi de yukarıdaki senaryo 2 ve 3'te değişirken, B satırı asla değişmez.
  • Chrome ve Safari'de, B ve C satırlarındaki değerlerin ikisi de senaryo 2 ve 3'te değişirken, satır A yalnızca senaryo 5 için değişir.
  • Firefox'ta, A satırındaki değer yalnızca senaryo 5 için değişir, B satırı beş senaryo için değişir ve C satırı yalnızca senaryo 2 ve 3 için değişir.
  • Yukarıdaki tüm tarayıcılarda, C satırındaki (önerilen çözüm) değişiklikler aynıdır, yani yalnızca senaryo 2 ve 3 için.

Demo Kodu:

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

Kredi:

Buradaki uygulama büyük ölçüde kendim olsa da, MBourne'nin cevabından ilham aldı . Bu diğer yanıt, "giriş" ve "değişiklik" olaylarının birleştirilebileceğini ve ortaya çıkan kodun hem masaüstü hem de mobil tarayıcılarda çalışabileceğini gösterdi. Bununla birlikte, bu yanıttaki kod, kendi başına sorunlu olan gizli "ekstra" olayların tetiklenmesine neden olur ve tetiklenen olaylar tarayıcılar arasında farklıdır, bu da bir başka sorundur. Buradaki uygulamam bu sorunları çözüyor.

Anahtar Kelimeler:

JavaScript giriş türü aralığı kaydırıcı olayları giriş tarayıcı uyumluluğunu değiştir çapraz tarayıcı masaüstü mobil no-jQuery


31

Bunu bir cevap olarak gönderiyorum çünkü daha az faydalı bir cevap altında yorum yapmak yerine kendi cevabı olmayı hak ediyor. HTML'den ayrı bir dosyada tüm js tutabildiğinden, bu yöntemi kabul edilen yanıttan çok daha iyi buluyorum.

Jamrelian'ın cevabı, kabul edilen cevap altındaki yorumunda verilen cevap.

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

Yine de Jaime tarafından bu yorumun farkında olun

Bu çözümle, kromda, işleyiciye iki olay (her etkinlik için bir tane) alacağınızı unutmayın, bu yüzden bununla ilgilenirseniz, buna karşı korunmanız gerekir.

İçinde olduğu gibi, fareyi hareket ettirmeyi bıraktığınızda olayı tetikleyecek ve daha sonra fare düğmesini bıraktığınızda tekrar tetikleyecektir.

Güncelleme

Fonksiyonun iki kez tetiklenmesine neden olan changeolay ve inputolayla ilgili olarak, bu hemen hemen bir sorun değildir.

Ateşleme işleviniz varsa, etkinlik tetiklendiğinde inputbir sorun olması son derece düşüktür change.

inputbir aralık giriş kaydırıcısını sürükledikçe hızla patlar. Sonunda bir işlev çağrısı daha yapmaktan endişe etmek, inputolay olan su okyanusuna kıyasla tek bir damla sudan endişe etmek gibidir .

changeEtkinliği dahil etmenin bile nedeni , tarayıcı uyumluluğu (esas olarak IE ile) içindir.


artı 1 internet explorer ile çalışan tek çözüm için !! Diğer tarayıcılarda da test ettiniz mi?
Jessica

1
Bu jQuery yani çalışma şansı oldukça düşüktür. Hiçbir yerde çalışmadığını görmedim. Hatta harika mobil cihazlarda çalışır.
Daniel Tonon

30

GÜNCELLEME: Bu yanıtı, masaüstü (mobil değil) tarayıcılarda aralık / kaydırıcı etkileşimlerini kullanmak için fare olaylarını nasıl kullanacağımıza bir örnek olarak burada bırakıyorum. Bununla birlikte, şimdi de tamamen farklı bir yazı yazdım ve inanıyorum ki, bu sayfada başka bir yere, bu soruna çapraz tarayıcı masaüstü ve mobil çözüm sunmak için farklı bir yaklaşım kullanan daha iyi bir cevap .

Orijinal cevap:

Özet: Tarayıcılar arasında tutarsız bir şekilde çalışan on('input'...ve / veya on('change'...çalışmayan aralık giriş değerlerini okumaya izin veren çapraz tarayıcı, düz JavaScript (yani no-jQuery) çözümü .

Bugün itibariyle (Şubat 2016 sonlarında) hala tarayıcı tutarsızlığı var, bu yüzden burada yeni bir çözüm sağlıyorum.

Sorun: Bir aralık girişi, yani bir kaydırıcı kullanıldığında, on('input'...Mac ve Windows Firefox, Chrome ve Opera ile Mac Safari'de aralıklı olarak güncellenen aralık değerleri sağlarken, on('change'...yalnızca fareyle üzerine getirildiğinde aralık değerini bildirir. Buna karşılık, Internet Explorer'da (v11) on('input'...hiç çalışmaz ve on('change'...sürekli güncellenir.

Burada, fareyle üzerine gelme, fareyle üzerine gelme ve (muhtemelen) fare olaylarını kullanarak vanilya JavaScript'i (yani jQuery yok) kullanan tüm tarayıcılarda aynı sürekli aralık değeri raporlaması elde etmek için 2 strateji rapor ediyorum.

Strateji 1: Daha kısa ama daha az verimli

Daha verimli kod yerine daha kısa kod tercih ederseniz, fareyle üzerine gelme ve fareyle üzerine gelme ve fareyle üzerine gelme kullanan bu 1. çözümü kullanabilirsiniz. Bu, kaydırıcıyı gerektiği gibi okur, ancak herhangi bir fareyle üzerine gelme olayı sırasında, kullanıcı tıklamamış ve dolayısıyla kaydırıcıyı sürüklemese bile gereksiz şekilde tetiklemeye devam eder. Esas olarak, 'mousedown' sonrasında ve 'mousemove' olayları sırasında aralık değerini okur ve her kullanımı biraz geciktirir requestAnimationFrame.

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

Strateji 2: Daha uzun ama daha verimli

Daha verimli koda ihtiyacınız varsa ve daha uzun kod uzunluğuna tolerans gösterebiliyorsanız, fareyle üzerine gelme, fareyle kaldırma ve fareyle kullanılan aşağıdaki çözümü kullanabilirsiniz. Bu ayrıca kaydırıcıyı gerektiği gibi okur, ancak fare düğmesi bırakıldığında uygun şekilde okumayı durdurur. Temel fark şudur ki, sadece 'fareyle üzerine' basıldıktan sonra 'fareyle üzerine gelme' dinlemeye başlar ve 'fareyle üzerine geldikten sonra' fareyle üzerine gelme 'dinlemeyi bırakır.

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: Yukarıdaki çözümlere duyulan ihtiyacın ve uygulanmasının daha kapsamlı açıklaması

Aşağıdaki kod, bu stratejinin birçok yönünü daha iyi göstermektedir. Açıklamalar gösteriye dahil edilmiştir:


Gerçekten iyi bir bilgi. Belki bir ara temaslı etkinlikler de ekleyebilirsin? touchmoveyeterli olmalı ve sanırım mousemovebu durumda olduğu gibi tedavi edilebilir .
nick

@nick, mobil tarayıcı işlevselliği sağlayan farklı bir çözüm için lütfen bu sayfadaki diğer cevabıma bakın.
Andrew Willems

Hepsi fare merkezli olarak klavye navigasyon olayları kovmayacak gibi bu erişilebilir bir cevap değil
LocalPCGuy

@LocalPCGuy, haklısın. Cevabım, kaydırıcıların yerleşik klavye kontrol edilebilirliğini bozdu. Klavye kontrolünü geri yüklemek için kodu güncelledim. Kodumun yerleşik erişilebilirliği bozmasının başka yollarını biliyorsanız, bana bildirin.
Andrew Willems

7

Andrew Willem'in çözümleri mobil cihazlarla uyumlu değildir.

İşte Edge, IE, Opera, FF, Chrome, iOS Safari ve mobil eşdeğerlerinde (test edebileceğim) çalışan ikinci çözümünün bir modifikasyonu:

Güncelleme 1: Gerekli olmadığını kabul ettiğim için "requestAnimationFrame" kısmı kaldırıldı:

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

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

Güncelleme 2: Andrew'un 2 Haziran 2016 güncellenmiş cevabına yanıt:

Teşekkürler, Andrew - bulabildiğim her tarayıcıda çalışıyor gibi görünüyor (Masaüstü kazan: IE, Chrome, Opera, FF; Android Chrome, Opera ve FF, iOS Safari).

Güncelleme 3: if ("kaydırıcıda giriş) çözümü

Aşağıdaki tüm tarayıcılarda çalışıyor gibi görünüyor. (Şimdi orijinal kaynağı bulamıyorum.) Bunu kullanıyordum, ancak daha sonra IE'de başarısız oldu ve bu yüzden farklı bir tane aramaya gittim, bu yüzden buraya geldim.

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

Ama çözümünüzü kontrol etmeden önce, bunun IE'de tekrar çalıştığını fark ettim - belki de başka bir çatışma vardı.


1
Çözümünüzün iki farklı masaüstü tarayıcısında çalıştığını doğruladım: Mac Firefox ve Windows IE11. Kişisel olarak herhangi bir mobil olanağı kontrol edemedim. Ancak bu, cevabımda mobil cihazları içerecek şekilde genişleten harika bir güncelleme gibi görünüyor. (Ben "giriş" ve "değişim" bir masaüstü sistemde hem fare girişini ve bir mobil sistemde dokunmatik girişi kabul varsayalım.) Geniş ölçüde uygulanabilir olması gerekir ve tanıma ve uygulama almaya değer çok güzel bir iş!
Andrew Willems

1
Daha fazla kazdıktan sonra çözümünüzün birkaç dezavantajı olduğunu fark ettim. İlk olarak, requestAnimationFrame gereksiz olduğuna inanıyorum. İkinci olarak, kod fazladan olayları tetikler (örneğin bazı tarayıcılarda fare gibi en az 3 olay). Üçüncü olarak, tetiklenen olaylar bazı tarayıcılar arasında farklılık gösterir. Bu nedenle, "giriş" ve "değişiklik" etkinliklerinin bir kombinasyonunu kullanma fikrinize dayanarak size ayrı bir cevap sağladım ve size orijinal ilhamı verdim.
Andrew Willems

2

İyi bir tarayıcılar arası davranış ve anlaşılabilir kod için en iyisi onchange özniteliğini bir formun kombinasyonunda kullanmaktır:

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

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

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

Kullanarak aynı oninput , değeri, doğrudan değiştirilir.

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

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

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


0

Yine başka bir yaklaşım - sadece bir olayın hangi tür olayın ele alınması gerektiğini gösteren bir bayrak ayarlayın:

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

-3

Sürekli tetiklemek için JavaScript "ondrag" etkinliğini kullanabilirsiniz. Aşağıdaki nedenlerden dolayı "girdi" den daha iyidir:

  1. Tarayıcı desteği.

  2. "Ondrag" ve "change" olayı arasında ayrım yapılabilir. "giriş" hem sürükleme hem de değiştirme için tetiklenir.

JQuery'de:

$('#sample').on('drag',function(e){

});

Referans: http://www.w3schools.com/TAgs/ev_ondrag.asp

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.