드래그하는 동안 입력 유형 = 범위의 onchange 이벤트가 firefox에서 트리거되지 않습니다


252

내가 함께 플레이했을 때 <input type="range">Firefox는 슬라이더를 드래그하는 동안 Chrome과 다른 사람들이 onchange 이벤트를 트리거하는 새로운 위치로 슬라이더를 떨어 뜨린 경우에만 onchange 이벤트를 트리거합니다.

Firefox에서 드래그 할 때 어떻게해야합니까?

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
범위 요소에 초점이 있으면 화살표 키를 사용하여 슬라이더를 이동할 수 있습니다. 그리고이 경우에도 onchange발생하지 않습니다. 이 질문을 찾은 것은 해당 문제를 해결하는 데있었습니다.
Sildoreth

답변:


452

분명히 Chrome과 Safari는 잘못되었습니다. onchange사용자가 마우스를 놓을 때만 트리거되어야합니다. 지속적인 업데이트를 받으려면 oninput이벤트 를 사용해야합니다.이 이벤트는 마우스와 키보드 모두에서 Firefox, Safari 및 Chrome의 실시간 업데이트를 캡처합니다.

그러나 oninputIE10에서는 지원되지 않으므로 다음과 같이 두 이벤트 핸들러를 결합하는 것이 가장 좋습니다.

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

자세한 내용은 이 Bugzilla 스레드 를 확인하십시오.


113
또는 $("#myelement").on("input change", function() { doSomething(); });jQuery를 사용하십시오.
알렉스 방비

19
이 솔루션을 사용하면 크롬에서 처리기에 대한 두 번의 호출 (이벤트 당 하나씩)이 발생하므로 관심이 있다면 조심해야합니다.
Jaime

3
모바일 크롬 (v34)에서 '변경'이벤트가 발생하지 않는 문제가 있었지만 방정식에 '입력'을 추가하면 다른 곳에서는 해로운 영향을 미치지 않으면 서 문제가 해결되었습니다.
brins0

7
@Jaime : " 만약 당신이 그것에 관심이 있다면, 당신은 그것에 대해 경계 할 필요가 있습니다. "어떻게해야합니까?

2
슬프게도 and onchange()같은 모바일 웹에서는 작동하지 않습니다 . 다른 제안? Android ChromeiOS Safari
Alston

41

요약:

나는 현재 브라우저에서 불가능한 범위 / 슬라이더 상호 작용에 일관되게 응답 할 수있는 jQuery가없는 브라우저 간 데스크탑 및 모바일 기능을 제공합니다. 기본적으로 모든 브라우저가 IE11의 on("change"...이벤트 on("change"...또는 이벤트에 대해 에뮬레이션하도록 강제 on("input"...합니다. 새로운 기능은 ...

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

... r범위 입력 요소이며 f청취자입니다. 리스너는 범위 / 슬라이더 값을 변경하는 모든 상호 작용 후에 호출되지만 현재 슬라이더 위치에서의 초기 마우스 또는 터치 상호 작용을 포함하여 또는 슬라이더의 한쪽 끝에서 벗어날 때 해당 값을 변경하지 않는 상호 작용 후에는 호출되지 않습니다.

문제:

2016 년 6 월 초 현재, 브라우저마다 범위 / 슬라이더 사용에 응답하는 방식이 다릅니다. 다섯 가지 시나리오가 관련됩니다.

  1. 현재 슬라이더 위치에서 초기 마우스 다운 (또는 터치 시작)
  2. 새로운 슬라이더 위치에서 초기 마우스 다운 (또는 터치 시작)
  3. 슬라이더를 따라 1 또는 2 이후의 후속 마우스 (또는 터치) 움직임
  4. 슬라이더의 한쪽 끝을지나 1 또는 2 이후의 후속 마우스 (또는 터치) 이동
  5. 최종 마우스 업 (또는 터치 엔드)

다음 표는 응답하는 위의 시나리오와 관련하여 적어도 3 개의 서로 다른 데스크탑 브라우저의 동작이 어떻게 다른지 보여줍니다.

응답하는 이벤트 및시기에 대한 브라우저 차이점을 보여주는 표

해결책:

onRangeChange기능은 범위 / 슬라이더 상호 작용에 일관되고 예측 가능한 크로스 브라우저 응답을 제공합니다. 다음 표에 따라 모든 브라우저가 작동하도록합니다.

제안 된 솔루션을 사용하는 모든 브라우저의 동작을 보여주는 표

IE11에서이 코드는 기본적으로 모든 현상이 현상에 따라 작동 할 수 있도록합니다. 즉, "change"표준 방식으로 이벤트가 작동 하도록 허용 하며 "input"이벤트는 결코 발생하지 않으므로 관련이 없습니다. 다른 브라우저에서는 "change"이벤트가 효과적으로 묵음 처리됩니다 (추가적이고 때로는 준비되지 않은 이벤트가 발생하지 않도록). 또한 "input"이벤트는 범위 / 슬라이더의 값이 변경 될 때만 리스너를 시작합니다. 일부 브라우저 (예 : Firefox)의 경우 위 목록의 시나리오 1, 4 및 5에서 리스너가 효과적으로 침묵하기 때문에이 문제가 발생합니다.

(시나리오 1, 4 및 / 또는 5에서 리스너를 활성화해야하는 경우 "mousedown"/ "touchstart", "mousemove"/ "touchmove"및 / 또는 "mouseup"/ "touchend"이벤트를 통합 할 수 있습니다. 이러한 솔루션은이 답변의 범위를 벗어납니다.)

모바일 브라우저의 기능 :

데스크톱 브라우저에서는이 코드를 테스트했지만 모바일 브라우저에서는 테스트하지 않았습니다. 그러나이 페이지의 또 다른 대답에서 MBourne은 여기에서 내 솔루션이 "... 모든 브라우저에서 작동하는 것으로 보입니다 (Win 데스크톱 : IE, Chrome, Opera, FF; Android Chrome, Opera 및 FF, iOS Safari) " . (MBourne 감사합니다.)

용법:

이 솔루션을 사용하려면 onRangeChange자신의 코드에 위의 요약 (간체 / 최소화) 또는 아래의 데모 코드 스 니펫 (기능적으로 동일하지만 더 자명 한)의 함수를 포함 시키십시오. 다음과 같이 호출하십시오.

onRangeChange(myRangeInputElmt, myListener);

where myRangeInputElmt는 원하는 <input type="range">DOM 요소이며 -like 이벤트에서 myListener호출하려는 리스너 / 핸들러 함수 "change"입니다.

청취자는 원하는 경우 매개 변수가 없거나 event매개 변수를 사용할 수 있습니다. 즉, 필요에 따라 다음 중 하나가 작동합니다.

var myListener = function() {...

또는

var myListener = function(evt) {...

( input요소 에서 이벤트 리스너 제거 (예 :) 사용 removeEventListener)는이 답변에서 다루지 않습니다.

데모 설명 :

아래 코드 스 니펫에서이 함수 onRangeChange는 범용 솔루션을 제공합니다. 나머지 코드는 그 사용법을 보여주는 예제 일뿐입니다. 로 시작하는 모든 변수 my...는 범용 솔루션과 관련이 없으며 데모를 위해서만 존재합니다.

데모 범위 / 슬라이더 값뿐만 아니라 시간 기준 횟수 방송 "change", "input"커스텀 "onRangeChange"이벤트를 소성 한 (행 각각 A, B 및 C). 다른 브라우저에서이 스 니펫을 실행할 때 범위 / 슬라이더와 상호 작용할 때 다음 사항에 유의하십시오.

  • IE11에서는 위의 시나리오 2와 3에서 행 A와 C의 값이 모두 변경되지만 행 B는 변경되지 않습니다.
  • Chrome과 Safari에서 행 B와 C의 값은 시나리오 2와 3에서 모두 변경되지만 행 A는 시나리오 5에서만 변경됩니다.
  • Firefox에서 행 A의 값은 시나리오 5에 대해서만 변경되고 행 B는 5 개의 시나리오 모두에 대해 변경되며 행 C는 시나리오 2와 3에 대해서만 변경됩니다.
  • 위의 모든 브라우저에서 C 행 (제안 된 솔루션)의 변경 사항은 동일합니다 (예 : 시나리오 2 및 3).

데모 코드 :

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

신용:

여기서 구현은 주로 내 것이지만 MBourne의 대답 에서 영감을 얻었습니다 . 다른 답변은 "입력"및 "변경"이벤트를 병합 할 수 있으며 결과 코드는 데스크톱 및 모바일 브라우저 모두에서 작동 할 것이라고 제안했습니다. 그러나이 답변의 코드를 사용하면 숨겨진 "추가"이벤트가 발생하며 그 자체로 문제가 발생하며 발생하는 이벤트는 브라우저마다 다릅니다. 내 구현은 이러한 문제를 해결합니다.

키워드 :

JavaScript 입력 유형 범위 슬라이더 이벤트 변경 입력 브라우저 호환성 크로스 브라우저 데스크탑 모바일 no-jQuery


31

나는 이것이 덜 유용한 답변 아래에있는 의견이 아닌 자체 답변이 될 수 있기 때문에 이것을 답변으로 게시하고 있습니다. 이 방법은 모든 j를 HTML과 별도의 파일로 유지할 수 있기 때문에 허용 된 답변보다 훨씬 낫습니다.

Jamrelian이 그의 답변에서 허용 된 답변 아래에 제공 한 답변.

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

그래도 Jaime 의이 의견에 유의하십시오.

이 솔루션을 사용하면 크롬에서 처리기에 대한 두 번의 호출 (이벤트 당 하나씩)이 발생하므로 관심이 있다면 조심해야합니다.

마우스 이동을 중지하면 마우스 버튼을 놓으면 이벤트가 시작됩니다.

최신 정보

기능이 두 번 트리거 되는 change이벤트 및 이벤트와 관련하여 input이것은 거의 문제가되지 않습니다.

기능이 켜진 input경우 change이벤트가 발생할 때 문제가 발생할 가능성이 거의 없습니다 .

input범위 입력 슬라이더를 드래그하면 빠르게 발사됩니다. 마지막에 하나 이상의 함수 호출 발사를 걱정하는 것은 input이벤트 인 바다와 비교할 때 물 한 방울에 대해 걱정하는 것과 같습니다 .

change이벤트를 포함시키는 이유 는 브라우저 호환성 (주로 IE와의 호환성)을 위해서입니다.


plus 1 인터넷 익스플로러와 호환되는 유일한 솔루션 !! 다른 브라우저에서도 테스트 했습니까?
Jessica

1
jQuery이므로 작동하지 않을 확률이 매우 낮습니다. 나는 그것이 어디에서나 작동하지 않는 것을 보지 못했습니다. 심지어 모바일에서도 작동합니다.
Daniel Tonon

30

업데이트 : 데스크탑 (그러나 모바일은 아님) 브라우저에서 마우스 이벤트를 사용하여 범위 / 슬라이더 상호 작용을 사용하는 방법에 대한 예제 로이 답변을 남겨두고 있습니다. 그러나 나는 이제 완전히 다른 것을 작성 했으며이 페이지의 다른 곳 에서이 문제에 대한 크로스 브라우저 데스크탑 및 모바일 솔루션을 제공하는 다른 접근법을 사용하는 것이 더 좋습니다 .

원래 답변 :

요약 : 크로스 브라우저, 일반 자바 스크립트 (즉, 노 jQuery를) 솔루션을 사용하지 않고 범위의 입력 값을 읽을 수 있도록 on('input'...및 / 또는 on('change'...일관성 브라우저 사이에 작동하는.

오늘 (2016 년 2 월 말) 현재 브라우저 불일치가 있으므로 여기에 새로운 해결 방법을 제공하고 있습니다.

문제 : 범위 입력 (예 : 슬라이더)을 사용하는 경우 on('input'...Mac 및 Windows Firefox, Chrome 및 Opera 및 Mac Safari에서 지속적으로 업데이트 된 범위 값을 제공하는 반면 on('change'...마우스를 올리면 범위 값만보고합니다. 반대로 Internet Explorer (v11) on('input'...에서는 전혀 작동하지 않으며 on('change'...지속적으로 업데이트됩니다.

여기서는 mousedown, mousemove 및 mouseup 이벤트를 사용하여 바닐라 JavaScript (예 : jQuery 없음)를 사용하는 모든 브라우저에서 동일한 연속 범위 값보고를 얻는 두 가지 전략을보고합니다.

전략 1 : 짧지 만 비효율적

보다 효율적인 코드보다 짧은 코드를 선호하는 경우 mousesdown 및 mousemove를 사용하지만 mouseup은 사용하지 않는이 첫 번째 솔루션을 사용할 수 있습니다. 이것은 필요에 따라 슬라이더를 읽지 만, 사용자가 클릭하지 않아서 슬라이더를 끌지 않더라도 마우스 오버 이벤트 동안 불필요하게 계속 발사합니다. 본질적으로 'mousedown'이후 및 'mousemove'이벤트 동안 범위 값을 읽고을 사용하여 각각 약간 지연 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"/>

전략 2 : 길지만 효율적

보다 효율적인 코드가 필요하고 더 긴 코드 길이를 견딜 수있는 경우 mousedown, mousemove 및 mouseup을 사용하는 다음 솔루션을 사용할 수 있습니다. 또한 필요에 따라 슬라이더를 읽지 만 마우스 버튼을 놓으면 바로 읽지 않습니다. 근본적인 차이점은 'mousedown'다음에 'mousemove'청취 만 시작하고 'mouseup'다음에 'mousemove'청취를 중지한다는 것입니다.

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

데모 : 위의 해결 방법의 필요성 및 구현에 대한 자세한 설명

다음 코드는이 전략의 다양한 측면을보다 완벽하게 보여줍니다. 데모에는 설명이 포함되어 있습니다.


정말 좋은 정보입니다. 언젠가 연락 이벤트를 추가 할 수도 있습니까? touchmove충분해야하며, mousemove이 경우 처럼 취급 될 수 있다고 생각합니다 .
nick

@nick, 모바일 브라우저 기능을 제공하는 다른 솔루션에 대해서는이 페이지의 다른 답변을 참조하십시오.
Andrew Willems

그들은 모두 마우스 중심이기 때문에 키보드 탐색이 이벤트를 발생하지 않으므로 이것은 접근 대답하지 않습니다
LocalPCGuy

@LocalPCGuy, 당신이 맞아요. 내 대답은 슬라이더의 내장 키보드 제어 기능을 손상 시켰습니다. 키보드 컨트롤을 복원하기 위해 코드를 업데이트했습니다. 내 코드가 내장 접근성을 손상시키는 다른 방법을 알고 있다면 알려주십시오.
앤드류 Willems

7

Andrew Willem의 솔루션은 모바일 장치와 호환되지 않습니다.

다음은 Edge, IE, Opera, FF, Chrome, iOS Safari 및 모바일에서 작동하는 두 번째 솔루션의 수정 사항입니다 (테스트 할 수 있음).

업데이트 1 : 필요하지 않은 "requestAnimationFrame"부분이 제거되었습니다.

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

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

업데이트 2 : Andrew의 2016 년 6 월 2 일 업데이트 된 답변에 대한 답변 :

고마워, Andrew-내가 찾을 수있는 모든 브라우저 (Win 데스크톱 : IE, Chrome, Opera, FF; Android Chrome, Opera 및 FF, iOS Safari)에서 작동하는 것으로 보입니다.

업데이트 3 : if ( "슬라이더 입력시) 솔루션

다음은 위의 모든 브라우저에서 작동하는 것으로 보입니다. (지금은 원본 소스를 찾을 수 없습니다.)이를 사용하고 있었지만 IE에서 실패하여 다른 소스를 찾았으므로 여기에 있습니다.

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

그러나 솔루션을 확인하기 전에 IE에서 다시 작동하는 것으로 나타났습니다. 다른 충돌이있을 수 있습니다.


1
귀하의 솔루션이 Mac Firefox 및 Windows IE11의 두 가지 다양한 데스크탑 브라우저에서 작동 함을 확인했습니다. 개인적으로 모바일 가능성을 확인할 수 없었습니다. 그러나 이것은 모바일 장치를 포함하도록 넓히는 대답에 대한 훌륭한 업데이트처럼 보입니다. ( "입력"과 "변경"은 데스크탑 시스템의 마우스 입력과 모바일 시스템의 터치 입력을 모두 수용한다고 가정합니다.) 광범위하게 적용 할 수 있고 인식 및 구현할 가치가있는 매우 훌륭한 작업입니다!
Andrew Willems

1
더 파고 난 후에는 솔루션에 몇 가지 단점이 있음을 깨달았습니다. 먼저 requestAnimationFrame이 불필요하다고 생각합니다. 둘째, 코드는 추가 이벤트를 발생시킵니다 (예 : 일부 브라우저에서 마우스 업시 3 개 이상의 이벤트). 셋째, 발생하는 정확한 이벤트는 일부 브라우저마다 다릅니다. 따라서 "입력"및 "변경"이벤트의 조합을 사용하는 초기 아이디어를 기반으로 별도의 답변을 제공하여 원래의 영감을 얻었습니다.
Andrew Willems 2016 년

2

브라우저 간 동작이 좋고 이해하기 쉬운 코드를 사용하려면 onchange 속성을 양식과 함께 사용하는 것이 가장 좋습니다.

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>

oninput을 사용하는 것과 동일하게 값이 직접 변경됩니다.

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

또 다른 접근법-이벤트 유형을 처리 해야하는 요소 신호에 플래그를 설정하십시오.

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

JavaScript "ondrag"이벤트를 사용하여 지속적으로 실행할 수 있습니다. 다음과 같은 이유로 "입력"보다 낫습니다.

  1. 브라우저 지원.

  2. "ondrag"와 "change"이벤트를 구별 할 수 있습니다. "입력"은 드래그와 변경 모두에 대해 발생합니다.

jQuery에서 :

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

});

참조 : http://www.w3schools.com/TAgs/ev_ondrag.asp

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.