[React] iframe에서 클릭 이벤트 버블링 발생시키기

우동이·2024년 8월 21일
0

React

목록 보기
7/7
  • iframe 내에서 클릭 이벤트가 발생했을 때, 부모 엘리먼트에 적용된 클릭 이벤트가 동작하지 않았다. ( 버블링 미발생 )
  • 이유가 무엇인지 알아보고 해결 방법을 찾아보자.

1. iframe에서 버블링이 발생하지 않는 이유

  • 일반적으로 이벤트 버블링은 단일 문서 트리에서만 발생한다. iframe으로 생성된 엘리먼트는 별도의 문서 트리를 가지기 때문에 이벤트 전파가 동작하지 않습니다. ( 이러한 결과는 생각해보면 당연한 것 같기도..? )
  • 관련 내용 참고하기
function App() {
  const srcDoc = `
    <!DOCTYPE html>
    <html>
      <button>클릭</button>
    </html>
  `;

  const handleClick = () => {
    console.log("부모 클릭 이벤트");
  };

  return (
    <div className="App" onClick={handleClick}>
      {/* iframe에서 클릭 이벤트가 발생해도 상위로 이벤트가 전파되지 않습니다. */}
      <iframe srcDoc={srcDoc} />
    </div>
  );
}

export default App;

2. iframe에서 이벤트 버블링 발생시켜보기

  • 크게 2가지 단계로 해결할 수 있다.

1단계: iframe 내부 widow에 클릭 이벤트 적용하기

  • iframeElement.contentWindow를 통해 iframe 하위에 있는 전역 widnow에 접근할 수 있습니다. ( 관련 내용 )
  • contentWindow에 클릭 이벤트를 적용하고, 이벤트 핸들러에서 커스텀 이벤트를 생성합니다.
  • 필요에 따라 커스텀 이벤트를 만들 때 좌표값을 계산하여 포함시킬 수 있습니다. ( 나중에 좌표를 사용할 경우 추가 )

2단계 클릭 이벤트 핸들러에서 자기 자신에게 이벤트를 dispatch하기

  • 위에서 생성한 커스텀 이벤트를 자기 자신에게 dispatch 합니다.
  • 이 방법을 통해 iframe 엘리먼트에서 클릭 이벤트가 발생하고, 부모 엘리먼트까지 이벤트가 전파됩니다. ( 버블링 발생 )

3. 코드로 살펴보기

  • 유틸 훅으로 분리하여 useIFrameClickEventBubbling를 구현한 결과 입니다.
import { useEffect } from "react";

function useIFrameClickEventBubbling(iframeElement) {
  useEffect(() => {
    if (!iframeElement) {
      return;
    }

    const iframeWindow = iframeElement.contentWindow;

    if (!iframeWindow) {
      return;
    }

    const handleClick = (event) => {
      const { top, left } = iframeElement.getBoundingClientRect();

      const customEvent = new MouseEvent("click", {
        bubbles: true,
        // 좌표를 계산해서 이벤트 객체에 추가
        clientX: event.clientX + left,
        clientY: event.clientY + top,
      });

      // ifraem 엘리먼트에 이벤트 dispatch
      iframeElement.dispatchEvent(customEvent);
    };
    
    // iframe 하위 Window에 클릭 이벤트 적용
    iframeWindow.addEventListener("click", handleClick);

    () => {
      if (iframeWindow) {
        iframeWindow.removeEventListener("click", handleClick);
      }
    };
  }, [iframeElement]);
}

export default useIFrameClickEventBubbling;
  • 적용 결과
import { useRef, useState, useEffect } from "react";
import useIFrameClickEventBubbling from "./useIFrameClickEventBubbling";

function App() {
  const ref = useRef(null);
  const [element, setElement] = useState();
  
  const srcDoc = `
    <!DOCTYPE html>
    <html>
      <button>클릭</button>
    </html>
  `;

  const handleClick = () => {
    console.log("부모 클릭");
  };

  useEffect(() => {
    if (ref.current) {
      setElement(ref.current);
    }
  }, [setElement]);

  useIFrameClickEventBubbling(element);

  return (
    // iframe 내부에 존재하는 버튼을 클릭하면 버블링이 발생하여 부모의 클릭 이벤트 발생
    <div className="App" onClick={handleClick}>
      <iframe srcDoc={srcDoc} ref={ref} />
    </div>
  );
}

export default App;

마치며

  • 코드 자체는 간단하지만, 이 접근 방식은 다양한 형식으로 변형하여 여러 문제를 해결할 수 있지 않을까? 생각이 들었다.
profile
아직 나는 취해있을 수 없다...

1개의 댓글

comment-user-thumbnail
2024년 8월 22일

👍👍

답글 달기