Reactプロジェクトで画像にマウスオーバーしてポップアップを出す方法

プログラミング

「画像にマウスオーバーするとポップアップが出る」ということを実現する方法は何通りかあると思います。

この記事ではReactのcreatePortalを使用して、画像にポップアップを出す方法をご紹介。

プログラムの概要

画像をマウスオーバーするとポップアップが出る画像

表示された画像にマウスオーバーするととセリフのようなポップアップが出て、マウスのポインターが外れると、ポップアップが消える、といっただけのプログラムです。

Popup.js

// Popup.js

import { useState } from "react";
import { createPortal } from "react-dom";
import { Image, Text } from "@chakra-ui/react";

import { Link } from "react-router-dom";
import DOG_IMG_AKITA from "../image/akita.jpg";
import DOG_IMG_NAYAMU from "../image/nayamu.jpg";
import DOG_IMG_TANOSHI from "../image/tanoshi.jpg";

const Popup = () => {
  const [popup, setPopup] = useState(false);
  const [insTag, setInsTag] = useState("");
  const [insMSG, setInsMSG] = useState("");

  const PopupPortal = ({ children }) => {
    const target = document.querySelector(insTag);
    return createPortal(children, target);
  };

  const popupMSG = (cls,message) => {
    setInsTag(cls);
    setInsMSG(message);
    setPopup(true);
  }

  return (
    <div>
      <div className="imgContainer">
        <Image
          boxSize="100px"
          objectFit="cover"
          src={DOG_IMG_AKITA}
          m={2}
          alt="犬 飽きた"
          onMouseOver={() => popupMSG('.akita','飽きたなぁ~')}
          onMouseLeave={() => setPopup(false)}
        />
        <div className="akita"></div>
      </div>
      <div className="imgContainer">
        <Image
          boxSize="100px"
          objectFit="cover"
          src={DOG_IMG_NAYAMU}
          m={2}
          alt="犬 悩む"
          onMouseOver={() => popupMSG('.nayamu','むずかしいなぁ~')}
          onMouseLeave={() => setPopup(false)}
        />
        <div className="nayamu"></div>
      </div>
      <div className="imgContainer">
        <Image
          boxSize="100px"
          objectFit="cover"
          src={DOG_IMG_TANOSHI}
          m={2}
          alt="「たのしいなぁ~」"
          onMouseOver={() => popupMSG('.tanoshi','たのしいなぁ~')}
          onMouseLeave={() => setPopup(false)}
        />
        <div className="tanoshi"></div>
      </div>

      {popup && (
        <PopupPortal>
          <div className="msg">
            <Text>{insMSG}</Text>
          </div>
        </PopupPortal>
      )}
      <div className="link-css">
        <Link to={`/`}>homeへ</Link>
      </div>
    </div>
  );
};

export default Popup;

ポップアップを実現しているコードが上記のPopup.jsです。

このコンポーネントをApp.jsでimportして実現しています。

では、以下にプログラムの詳細を説明していきます。

useState

import { useState } from "react";

・・・

  const [popup, setPopup] = useState(false); // 状態管理用false:ポップを出さない。true:ポップを出す。
  const [insTag, setInsTag] = useState(""); // ポップをどこに出すかを保存しておく
  const [insMSG, setInsMSG] = useState(""); // ポップのメッセージを保存しておく

ポップアップを出すか/出さないかの判定。

ポップアップをどこに出すのか。

ポップアップの内容の格納。

にuseStateを使いました。

createPortal

ページの何処にどのようなポータル出すかを制御します。

import { createPortal } from "react-dom";

・・・

  const PopupPortal = ({ children }) => {
    const target = document.querySelector(insTag);
    return createPortal(children, target);
  };

「insTag」で示された場所に、「children」の内容を表示する、といった意味。

      {popup && ( {// popupがtrueなら<PopupPotal>以下を表示する。}
        <PopupPortal>
          <div className="msg">
            <Text>{insMSG}</Text>
          </div>
        </PopupPortal>
      )}

popupがtrueなら<PopupPortal>以下を表示する処理。

{insMSG}には、マウスオーバーした画像のメッセージが格納されている。

マウスオーバー、マウスリーフイベント

  const popupMSG = (cls,message) => {
    setInsTag(cls);
    setInsMSG(message);
    setPopup(true);
  }

・・・

      <div className="imgContainer">
        <Image
          boxSize="100px"
          objectFit="cover"
          src={DOG_IMG_TANOSHI}
          m={2}
          alt="「たのしいなぁ~」"
          onMouseOver={() => popupMSG('.tanoshi','たのしいなぁ~')}
          onMouseLeave={() => setPopup(false)}
        />
        <div className="tanoshi"></div>
      </div>

onMouseOverイベントが発生すると、

  • どのdom
  • どのようなメッセージのポップアップを出すか

を引数として、popupMSGがコールされる。

popupMSG()では、

  • mouseOverなので、popupをtrueにする
  • insTagにpopupを出力するdom情報の格納
  • insMSGにメッセージ内容を格納

をしています。

onMouseLeaveでは、ポップアップを消すためにpopupをfalseにしています。

css

どんな感じのポップアップにするかは、<PopupPortal>で出力するコンポーネントのcssに依存します。

参考に、cssのコードは示しますが、ここはcssの知識を駆使して、お好みのデザインをしてください。

.imgContainer {
  position: relative;
}
.msg {
  position: absolute;
  top:10%;
  left: 55%;
  z-index: 100;
  border: solid 1px ;
  border-radius: 8px;
  background-color: rgba(255, 255, 255, 0.3);
}
神谷
神谷

言い訳ですが、目的がcreatePortalでのポップアップ表示の実現なので、デザインはおざなりです。

最後に

一からポップアップのプログラムを作りたい方がいる場合も考えて、参考のためにディレクトリ構造やApp.jsのコードを紹介しておきます。

ディレクトリ構造

createPortalのサンプルプログラムのディレクトリ

当該プログラムのsrcディレクトリ配下の構造です。

createPortalを使ったサンプルプログラムです。ポップアップの他にも、モーダルとトーストのサンプルも含まれています。

App.js

import React from "react";
import "./App.css";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "./components/Home";
import Modal from "./components/Modal";
import Popup from "./components/Popup";
import Toast from "./components/Toast";

function App() {
  return (
    <div className="App">
      <BrowserRouter>
        <Routes>
          <Route path={"/"} element={<Home />} />
          <Route path={"/modal"} element={<Modal />} />
          <Route path={"/popup"} element={<Popup />} />
          <Route path={"/toast"} element={<Toast />} />
        </Routes>
      </BrowserRouter>
    </div>
  );
}

export default App;

最後に

このブログに掲載されているコードは、著作権者(私)に帰属します。コードを自由に使用、複製、改変、配布、販売、サブライセンスできます。また、コードを使用した結果、何らかの損害が発生した場合でも、著作権者は一切責任を負いません。

タイトルとURLをコピーしました