focusout, blur 이벤트 차이와 이해 :: 마이구미
이 글은 focusout 와 blur 의 차이점을 알아보고, 이벤트 발생에 있어, 주의사항을 다뤄본다.
다음과 같은 의문 또는 버그가 발생했다면, 글이 도움이 될 것이다.
focusout 과 blur 의 차이를 알고 싶다.
focusout 또는 blur 를 사용중 다음과 같은 에러가 발생했다.
"Uncaught DOMException: Failed to execute 'remove' on 'Element': The node to be removed is no longer a child of this node. Perhaps it was moved in a 'blur' event handler?"
focusout 과 blur 의 차이
focusout 는 의미 그대로 엘리먼트가 포커스를 잃었을 때 발생되는 이벤트이다.
같은 용도로 사용되는 이벤트로 blur 가 있는 것이다.
둘 사이의 차이점은 버블링 여부이다. => 버블링은 다른 글을 참고바란다. (http://mygumi.tistory.com/315)
focusout 는 버블링이 일어나고, blur 는 버블링이 일어나지 않는다.
세트로는 {focusin - focusout} 와 {focus - blur} 로 분류된다.
다음 예제 코드를 통해 보다 쉽게 이해할 수 있을 것이다.
See the Pen focusout vs blur by leejunghyun (@mygumi) on CodePen.
위 예제의 출력값을 보면 focusout 은 자식 -> 부모 순으로 출력되는 것으로 보아 버블링을 확인할 수 있다.
blur 의 경우에는 포커스를 잃은 자식만이 출력되는 것을 확인할 수 있다.
Error => Failed to execute 'remove' on 'Element': ......
위와 같은 에러는 remove 또는 removeChild 와 같은 엘리멘트를 삭제하는 메소드를 사용할 때 주로 발생한다.
다음과 같은 시나리오를 보자.
자세한 코드는 다음 링크를 통해 확인하면 된다. => Codepen Example
- 입력창을 만든다.
- 생성된 입력창은 ESC 키 또는 포커스를 잃게 되면 삭제된다.
위 과정을 위해서는 keyup, blur(또는 focusout) 이벤트를 사용하면 된다.
input.addEventListener("keyup", e => { if (e.keyCode === 27) { e.target.remove(); } }); input.addEventListener("blur", e => { input.remove(); })
이와 같은 예제에서 ESC 키를 눌렀을 때, 언급했던 에러가 발생되는 것이다.
왜 발생하는 것일까?
에러를 해석하면 제거할 엘리먼트가 존재하지 않는다는 식으로 말한다.
하지만 코드를 보면 전혀 그렇게 될리가 없다고 생각된다.
이유는 엘리먼트가 제거될 때, 포커스를 잃었다는 이벤트를 발생하게 된다.
* blur 뿐만 아니라 focusout 도 이와 같이 발생된다.
이것을 그림으로 표현하면 다음과 같다.
위와 같은 흐름 때문에 제거할 엘리먼트가 존재하지 않기 때문에 에러가 발생하게 된다.
문제를 해결하기 위해서는 많은 방법이 있겠지만, 2가지를 다뤄본다.
1. 플래그 변수 활용
input.addEventListener("keyup", e => { if (e.keyCode === 27) { removed = true; // Please set it before the remove(). e.target.remove(); } }); input.addEventListener("blur", e => { if (removed) { return; } input.remove(); })
2. 다른 이벤트에 제거 위임
input.addEventListener("keyup", e => { e.target.blur(); });
keyup 이벤트에서 제거하지 않고, 바로 포커스를 잃는 이벤트를 발생시켜 제거를 위임한다.
자세한 건 예제 코드 링크를 참고하길바란다.