onclick () 및 onblur () 주문 문제


102

사용자 지정 드롭 다운 메뉴를 표시하는 입력 필드가 있습니다. 다음 기능을 원합니다.

  • 사용자가 입력 필드 외부의 아무 곳이나 클릭하면 메뉴가 제거되어야합니다.
  • 보다 구체적으로 사용자가 메뉴 내의 div를 클릭하면 메뉴를 제거 하고 어떤 div를 클릭했는지에 따라 특별한 처리가 수행되어야합니다.

내 구현은 다음과 같습니다.

입력 필드에는 사용자가 입력 필드 외부를 클릭 할 때마다 onblur()메뉴를 삭제 하는 이벤트가 있습니다 (상위 항목 innerHTML을 빈 문자열로 설정). 메뉴 내부의 div onclick()에는 특수 처리를 실행하는 이벤트 도 있습니다 .

문제는 onclick()메뉴를 클릭 할 때 이벤트가 실행되지 않는다는 것입니다. 입력 필드 onblur()가 먼저 실행되고 onclick()s!

메뉴 div ' onclick()onmousedown()onmouseup()이벤트 로 나누고이 답변 에서 제안 된 것과 유사하게 마우스를 위로하면 마우스를 내리는 전역 플래그를 설정 하여 문제를 해결했습니다 . onmousedown()이전 onblur()에 실행되기 때문에 onblur()메뉴 div 중 하나를 클릭하면 플래그가 설정 되지만 화면의 다른 곳이 클릭 된 경우에는 설정되지 않습니다. 메뉴를 클릭하면 메뉴 onblur()를 삭제하지 않고 즉시 돌아온 다음가 onclick()실행될 때까지 기다리면 메뉴 를 안전하게 삭제할 수 있습니다.

더 우아한 해결책이 있습니까?

코드는 다음과 같습니다.

<div class="menu" onmousedown="setFlag()" onmouseup="doProcessing()">...</div>
<input id="input" onblur="removeMenu()" ... />

var mouseflag;

function setFlag() {
    mouseflag = true;
}

function removeMenu() {
    if (!mouseflag) {
        document.getElementById('menu').innerHTML = '';
    }
}

function doProcessing(id, name) {
    mouseflag = false;
    ...
}

답변:


168

나는 당신과 똑같은 문제를 겪고 있었는데, 내 UI는 당신이 설명하는 것과 똑같이 디자인되었습니다. 나는 단순히 onClick메뉴 항목을 onMouseDown. 나는 아무것도하지 않았다. 아니요 onMouseUp, 플래그가 없습니다. 이렇게하면 추가 작업없이 브라우저가 이러한 이벤트 처리기의 우선 순위에 따라 자동으로 다시 정렬되도록하여 문제가 해결되었습니다.

이것이 당신에게도 효과가 없었을 이유가 있습니까?


3
이것은 실제로 작동합니다. 나는 FF에서 그것을 테스트했고 그것은 매력처럼 작동합니다.
Supreme Dolphin

3
효과가있다. Firefox, Chrome 및 Internet Explorer에서 테스트 onmousedown하기 전에 실행 된 것처럼 보입니다 onblur.
Tonatio 2016-08-16

11
이것은 동작을 약간 자연스럽게 변경한다는 점에 유의할 가치가 있습니다. 클릭 상호 작용은 마우스를 위로 올리는 대신 마우스 아래로 처리 할 때 처리됩니다. 대부분의 사람들에게는 괜찮을 수 있지만 (이 경우에는 자신이 포함됨) 몇 가지 단점이 있습니다. 가장 주목할만한 것은 잘못 클릭 한 경우 버튼을 클릭 한 다음 드래그하여 onclick이 호출되는 것을 방지합니다. 버튼이 순수하지 않은 기능 (삭제, 게시 등)을 수행하는 경우이를 보존하고 플래그 접근 방식을 사용할 수 있습니다.
Brizee

2
onClick 대신 mouseDown을 설정하는 순간 접근성은 어떻습니까? 모든 <button> 요소에 대한 접근성을 죽입니다 : /
ncubica

2
@ ian-macfarlane이 아래에 나열한 답변은이 문제를 해결하는 올바른 방법입니다. 요약 ... onMouseDownevent.preventDefault() 하고 onClick원하는 동작과 함께.
Conal Trinitrotoluene Da Costa

47

onClick로 바꾸면 안됩니다 onMouseDown.

이 접근 방식은 다소 효과가 있지만 두 가지 이벤트는 사용자의 눈에 서로 다른 기대치를 갖는 근본적으로 다른 이벤트입니다. onMouseDown대신 사용하면 onClick이 경우 소프트웨어의 예측 가능성이 손상됩니다. 따라서 두 이벤트는 서로 바꿔서 사용할 수 없습니다.

예를 들어, 실수로 버튼을 클릭 할 때 사용자는 마우스 클릭을 누른 상태에서 커서를 요소 외부로 끌고 마우스 버튼을 놓아서 결과적으로 아무 동작도하지 않을 것으로 예상합니다. onClick이렇게합니다. onMouseDown사용자가 마우스를 누르고있는 것을 허용하지 않고 대신 사용자에 대한 의지없이 즉시 작업을 트리거합니다. onClick컴퓨터에서 작업을 트리거 할 것으로 예상되는 표준입니다.

이 상황 event.preventDefault()에서 onMouseDown이벤트를 요청 하십시오 . onMouseDown기본적으로 흐림 이벤트를 발생시키고를 preventDefault호출 할 때 발생하지 않습니다 . 그런 다음 부를 onClick기회가 있습니다. 흐림 이벤트는 onClick.

결국 onClick이벤트는 동일한 요소 내에서 둘 다 발생하는 경우에만 onMouseDown및 의 조합입니다 onMouseUp.


7
이것은 나에게 완벽한 솔루션이었고 의견은 완전히 정확합니다. onMouseDown을 대체하는 것은 사용자 경험에 혼란 스럽습니다. 투표했습니다.
Paul Bartlett

5
이것이 정답이어야합니다. 이것을 게시 해 주셔서 감사합니다
user1189352

1
요소 내부를 클릭하여 텍스트를 선택할 수없는 등 약간의 부작용도 있습니다. 이것이 문제가 될 너무 많은 경우를 생각할 수 없습니다.
Rei Miyasaka

1
내가 사용하는 경우 event.preventDefault()onMouseDown이벤트 내 onFocus이벤트가 호출되지 않습니다
배트맨

4

에 교체 onmousedownonfocus. 따라서이 이벤트는 포커스가 텍스트 상자 안에있을 때 트리거됩니다.

에 교체 onmouseuponblur. 텍스트 상자에서 포커스를 빼는 순간 onblur가 실행됩니다.

나는 이것이 당신이 필요로 할 것이라고 생각합니다.

업데이트 :

onfocus 함수를 실행할 때-> onblur에서 적용 할 클래스를 제거하고 onfocus에서 실행하려는 클래스를 추가합니다.

onblur 함수를 실행할 때-> onfocus에서 적용 할 클래스를 제거하고 onblur에서 실행하려는 클래스를 추가합니다.

플래그 변수가 필요하지 않습니다.

업데이트 2 :

onmouseout 및 onmouseover 이벤트를 사용할 수 있습니다.

onmouseover- 커서가 그 위에있을 때 감지합니다.

onmouseout- 커서가 떠날 때 감지합니다.


명확성을 위해 내 질문을 편집했습니다. 이 솔루션은 플래그 설정의 필요성을 어떻게 방지합니까? 또한, 나는 사용 onmousedown()하고 onmouseup()메뉴가 아닌 텍스트 상자 / 입력 필드에.
1 ''

이 답변이 옳다는 것을 모르기 때문에 아직 받아 들일 수 없습니다. 위의 의견에서 제기 한 우려 사항에 대해 답변 해 주시겠습니까?
1 ''

물론, 마우스가 메뉴 위에있을 때 플래그를 설정하기 위해 현재 수행중인 작업과 유사하게 onmouseover 및 onmouseout을 사용하는 방법을 알 수 있습니다. 그러나 나는 여전히 onmouseout에서 지워지는 onmouseover에 플래그를 설정해야한다고 생각하므로 클릭했을 때 메뉴 위에 있는지 여부를 알 수 있습니다. 깃발을 설정할 필요가없는 방법을 생각할 수 있습니까?
1 ''

나는 내가 results..just에게 코드를 볼 해달라고하면 바로 관련 부분을 넣고 그 완전히 확인 ... ... 바이올린에 넣어 ... 당신의 코드를 볼 수
심평원 딴


2

보다 우아한 (하지만 성능이 떨어지는) 솔루션 :

대신 메뉴 사용을 제거하기 위해 입력의 위해 onblur를 사용 document.onclick후 화재, onblur.

그러나 이것은 또한 입력 자체를 클릭 할 때 메뉴가 제거됨을 의미하며 이는 바람직하지 않은 동작입니다. 문서 클릭 이벤트에 클릭이 전파되지 않도록하려면 input.onclickwith event.stopPropagation()를 설정 하십시오.


5
하지만이 답변의 문제는 블러를 유발 한 클릭 이벤트가 아니라는 것입니다. 사용자가 입력을 탭하면 어떻게됩니까? 그러면 흐림 이벤트가 트리거되지만 플라이 아웃 메뉴는 계속 표시됩니다.
Christoph

0

onfocus로 onclick 변경

onblur와 onclick이 잘 어울리지 않더라도 분명히 onfocus와 yes onblur입니다. 메뉴가 닫힌 후에도 onfocus는 내부를 클릭 한 요소에 대해 여전히 유효하기 때문입니다.

나는했고 효과가 있었다.


-7

다음 과 같이 핸들러 setInterval내부 에서 함수 를 사용할 수 있습니다 onBlur.

<input id="input" onblur="removeMenu()" ... />

function removeMenu() {
    setInterval(function(){
        if (!mouseflag) {
            document.getElementById('menu').innerHTML = '';
        }
    }, 0);
}

setInterval함수는 onBlur 호출 스택에서 함수를 제거합니다. 시간을 0으로 설정했기 때문에이 함수는 다른 이벤트 핸들러가 완료된 직후에 호출됩니다.


5
setInterval그것은 나쁜 생각이며, 당신은 그것을 취소하지 않으므로 지속적으로 기능을 실행하고 사이트가 최대 CPU를 사용하게 할 가능성이 높습니다. setTimeout이 기술을 사용하려는 경우 대신 사용하십시오 (권장하지 않음). 특정 이벤트의 타이밍에 따라 앱을 만드는 것은 위험한 일입니다.
케이시

6
위험은 로빈슨입니다! 위험! 경쟁 조건이 앞서 있습니다!
GoreDefex

나는 원래이 솔루션을 생각해 냈지만 (잘 setTimeout아닙니다 setInterval) 250타이밍이 올바른 순서로 발생하기 전에 ms 까지 올려야했습니다 . 이 버그는 여전히 느린 장치에 존재하며 지연을 눈에 띄게 만듭니다. 나는 시도하고 onMouseDown잘 작동합니다. 터치 이벤트도 필요한지 궁금합니다.
Brennan Cheung

onClick을 정말로 사용하고 싶다면 간격과 같은 것이 나쁘지 않습니다. 그러나 간격이 아닌 조건이있는 재귀 시간 제한을 원하므로 영원히 실행되지는 않습니다. 이것은 Selenium 조건부 대기에서 사용하는 것과 동일한 패턴입니다.
Will Cain
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.