1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

要素が子要素を持っていても dragLeave イベントを正しく発火させる

Last updated at Posted at 2022-12-17

はじめに

React でファイルが要素の上にドラッグされたら要素を青色にしたい時は以下のように書くことが多いかと思います。

import React, {useState} from "react"

const Component = () => {
    const [isHoverd, setHoverd] = useState(false);

    const onDragEnter = (event: React.DragEvent<HTMLDivElement>) => {
      setHoverd(true);
    };
    const onDragLeave = (event: React.DragEvent<HTMLDivElement>) => {
      setHoverd(true);
    };
 

    return(
        <div onDragEnter={onDragEnter}
             onDragLeave={onDragLeave}
             className={isHoverd ? "bg-blue-class" : ""}
             id="drop-area"
        >
            ここにドロップしてね
        </div>
    )
}

子要素へ DragEnter すると自身の dragleave が発火しちゃう問題

上の例なら動作するんですが drop-area が子要素を持ってる場合だと問題がありまして、 drop-area 上にいても child-element に上にファイルが乗ると drop-areadragleave が発火されちゃうんですよね...

    <div id="drop-area">
        <div id="child-element" /div> <- ここに Enter するとまずい
    </div>

この動作の詳細な説明はこちらの記事がとても分かりやすいです。

改善その 1

この記事のやり方以外にもいい方法はないかと調べ以下の記事のやり方が僕的に好みだったので実装してみました。

やっていることはこんな感じです。(onDragLeave 以外同じなので省略してます)

    const onDragLeave = (event: React.DragEvent<HTMLDivElement>) => {
      if (
        event.relatedTarget &&
        event.currentTarget.contains(event.relatedTarget as Node)
      )
    return;
      setHoverd(true);
    };

何をやっているかというと、発生したイベントの直前に発生したイベントのエレメントを返す relatedTarget プロパティを使用して、 isHoverdtrue にしたくない drop-area から child-element へのファイルの移動時はなにもしないようにしています。

WebKit だと正常に動作しない問題

上の実装で Chrome とかだと動作するんですが、safari だとなんと正常に動作しないようです。CanIuse にも言及されてないし実際に動作検証しないと気づかなそう...

onDragEnteronDragLeave イベント時のみ relatedTargetnull になるバグ?があるみたいです。 10 年前から報告されてるとかもはや仕様レベル

どうしたか

react-areauseDrop を使用することで解消しました。解消したというのも何なら若干語弊があって useDrop のソースコードを読んでる時にこのバグの存在を知った感じです。

ライブラリを極力使用したくない場合は GitHubのこの部分に実装が書いてあるので参考になるかと思います。

さいごに

ブラウザごとに色々気をつけないといけないのはなかなか大変ですね。
また、useDrop は今回実装した onDragLeave の判定とかも元からやってくれてたりしていいですね。 onDragEnter, onDragLeave のイベント名も onDropEnter, onDropLeave にしたりしてるのが個人的には好きです。実行するのは drag してるほうじゃなくで drop されるほうですしこっちのほうがわかりやすいように思いました。

useDropuseDrag と合わせて他にも感動した機能があったりしたのでいつかご紹介したいと思っています。

最後まで読んでいただきありがとうございました! 間違ってるところ等々ありましたらぜひコメントください!

1
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?