프로젝트 진행 도중 javascript를 이용하여 키보드 입력없이 강제로 change 이벤트를 발생시켜야 할 일이 있었습니다.

 

jQuery를 이용하면 .trigger함수를 이용해서 발생시킬수 있지만 굳이 바닐라 자바스크립트로 그 방법을 찾아서 해결하고싶었습니다.

 

click이나 focus는 직접 행동해주는 메서드가 존재하지만 change는 직접 행동해주는 메서드를 찾지 못했습니다. 혹시 있다면 알려주세요

 

그래서 이벤트 생성자를 통해 직접 이벤트를 발생시켜주어야 했습니다.

// 이벤트 발생 예제 //
const ele = document.getElementById('my-input');
ele.dispatchEvent(new Event('이벤트명'));

 

위 예제를 통해 자바스크립트를 이용해 직접 이벤트를 발생시키는 방법을 알 수 있습니다.

 

 

이벤트 생성자

 

이벤트 생성자는 어떻게 사용하는지 좀 더 알아보겠습니다.

new Event(typeArg[, eventInit]);

 

  • typeArg: 이벤트의 이름을 나타냅니다.
  • eventInit: 이벤트에 해당하는 옵션을 정해줍니다. 아래와 같은 객체의 형태로 넘겨줄 수 있습니다.
    • bubbles:  Boolean 값을 받습니다. 해당 event가 bubble인지 아닌지 결정합니다. 기본값은 false 입니다.
    • cancelable: Boolean 값을 받습니다.event가 캔슬 될 수 있는지 없는지 결정합니다. 기본값은false 입니다.
    • composed: Boolean 값을 받습니다. event가 shadow root 바깥의 eventListener 들도 trigger 할 것인지 결정합니다. 기본값은 false 입니다.

이벤트 버블링은 일어나게 하는 것이 정상적인 동작에 바람직하므로 bubbles에 값을 주겠습니다.

 

그렇다면 어떤 이벤트명을 사용해야하며, 리액트에서는 어떻게 적용해야할 지 알아보겠습니다.

 

 

리액트에서 강제로 발생시키기

 

간단한 리액트 예제를 보겠습니다.

import React, { useState } from 'react';

function App() {
  const [input, setInput] = useState('');

  const handleChange = () => {
    alert('값이 변경되었습니다')
  };

  const handleClick = () => {
    setInput('아무 글씨');
  };

  return (
    <div>
      <input type="text" value={input} readOnly onChange={handleChange} />
      <button onClick={handleClick}>값 변경</button>
    </div>
  );
}

 

위의 예제와 같은 상황이 제가 글을 포스트하는 이유인데요, input은 readOnly로 키보드 입력을 할 수 없는 상황입니다. 하지만 onChange를 이용해서 value가 변경될 때마다 알림을 주고 싶은 상황입니다.

 

"버튼을 클릭하면 value가 변경되니까 onChange이벤트가 발생하겠지?"

 

이게 저의 생각이였습니다. 근데 실행해보면 동작을 안합니다... 왜일까..?

 

이유는 간단했습니다. 버튼을 클릭했을 때 리액트는 setInput으로 인해 input을 리랜더링하게 됩니다. 그렇기 때문에 value가 변경되는 이벤트가 발생하는 것이 아닌 value가 바뀐 input을 다시 랜더링 해주는 것입니다. 그래서 값이 변경되는 것은 확인할 수 있지만 이벤트는 발생하지 않습니다.

 

그렇다면 어떻게 이벤트를 발생시킬까..?

 

그렇게 이벤트를 발생시키는 방법에 대해 찾아보게됩니다..

 

우선 input의 경우 네이티브 change 이벤트가 발생될 경우는 value가 변경되고 focus out 되었을 때 발생하게 됩니다.

 

하지만 리액트에서는 네이티브 change 이벤트와 발생이 조금 다릅니다. 리액트에서는 입력으로 인한 value가 변경될 때마다 change 이벤트가 발생됩니다. 근데 우리는 입력으로 인한 value의 변경이 아니라 스크립트로 직접 값을 변경하기 때문에 값을 변경하고 직접 이벤트를 발생시켜주어야합니다.

 

앞서 예제에서 보았듯이 value에 state를 두면 value 변경전에 리랜더링이 일어나 이벤트를 발생시킬수 없습니다.

 

그래서 value에 state를 없애고 ref를 이용한 직접적인 엘리먼트 값에 접근하게 됩니다.

import React, { useRef } from 'react';

function App() {
  const inputRef = useRef(null);

  const handleChange = () => {
    alert('값이 변경되었습니다')
  };

  const handleClick = () => {
    if (inputRef.current) {
      inputRef.current.setAttribute('value', '아무 글씨');
      inputRef.current.dispatchEvent(new Event('change', { bubbles: true }));
    }
  };

  return (
    <div>
      <input type="text" ref={inputRef} readOnly onChange={handleChange} />
      <button onClick={handleClick}>값 변경</button>
    </div>
  );
}

 

위의 코드를 이용하면 value를 원하는 값으로 변경할 수 있고, 동시에 change 이벤트도 발생시킬 수 있습니다.

 

마치며

 

사실 react-trigger-change라는 라이브러리를 사용하면 쉽게 해결할 수 있었습니다. 하지만 뭔가 자바스크립트를 써서 직접 해결해보고자 하는 욕구가 있었네요.. 이런 라이브러리까지 있다는 건 수요가 있다는 건데 change()라는 메서드가 있었으면 참 편했을텐데..

 

여튼 그래도 이벤트의 대해 발생과정을 네이티브와 리액트를 비교해볼 수 있는 좋은 기회였습니다.

틀린 내용이 있다면 언제나 코멘트 환영합니다! 적극적으로 달아주세요.

 

간편하게 라이브러리를 사용해보고 싶은 사람들을 위해 링크올려두겠습니다.

https://www.npmjs.com/package/react-trigger-change