web-dev-qa-db-ja.com

クリックによってトリガーされるマウス離脱

私は絶対位置のdivを持っており、マウスがその上に移動したとき、およびマウスが離れたときを追跡しようとしています。残念ながら、ボックス内のテキストをクリックすると、時々mouseleaveイベントがトリガーされます。

デモ: js fiddle

どうすればこれを防ぐことができますか?

JS

let tooltip = document.createElement('div');
tooltip.innerHTML = 'HELLO WORLD';
tooltip.setAttribute('class', 'tooltip');
tooltip.style.display = 'none';

tooltip.onclick = evt => {
    console.log('click')
    evt.stopPropagation();
}
tooltip.ondblclick = evt => {
    console.log('double click')
    evt.stopPropagation();
}

tooltip.onmouseenter = () => {
    console.log('tooltip mouse OVER');
}

tooltip.onmouseleave = () => {
    console.log('tooltip mouse OUT')
}

tooltip.style.left = '290px';
tooltip.style.top = '50px';
tooltip.style.display = 'block';
document.body.appendChild(tooltip);

HTML

<div style="width: 300px; height: 300px; background-color: lightblue">

</div>

CSS

.tooltip {
    position: absolute;
    /*display: none;*/
    left: 100;
    top: 100;
    min-width: 80px;
    height: auto;
    background: none repeat scroll 0 0 #ffffff;
    border: 1px solid #6F257F;
    padding: 14px;
    text-align: center;
}
20
Adam Rackis

これはバグのようです(Chromeでクリックすると、マウスを押し下げたり、上げたりすると、お互いに急速に発生します)。

イベントが発生したときにマウスがまだ要素上にあるかどうかを確認して、この問題を回避することをお勧めします。

tooltip.onmouseleave = (e) => {
    if (tooltip === document.elementFromPoint(e.clientX, e.clientY)) {
        console.log('false positive');
        return;
    }
    console.log('tooltip mouse OUT')
}

欠点は、ブラウザウィンドウがフォーカスを失うと、誤検知と見なされることです。それが問題である場合は、 この答え を確認してください。

10
trincot

以前はここで回答とコメントを見ていましたが、最近mouseleaveイベントが誤ってトリガーされたかどうかを確認する方法を見つけました

mouseleaveハンドラーにチェックを追加しました:

private handleMouseLeave(event: MouseEvent) {
    if(event.relatedTarget || event.toElement){
        // do whatever
    }
    // otherwise ignore
}

Chrome v64でのテストから、高速クリックによってnullイベントがトリガーされると、これらの値は両方ともmouseleaveになります。relatedTargetは古いブラウザとの互換性のためのものです

注:マウスがnull要素から出てブラウザ(タブ、メニューなど)に入る場合、またはブラウザウィンドウから離れた場合も、これらの値は両方ともtargetになります。私が操作しているスライド式のメニューであり、ブラウザーウィンドウを閉じても、私の特定のケースではメニューが閉じないため、これは問題ではありませんでした。

:最新のFirefoxリリース(2018年2月)では、メニューをクリックするたびにmouseleaveがトリガーされるようです。それを調べる必要があります

11
Drenai

私もこのバグに遭遇しました。私の場合、ラベルでラップされたチェックボックスをリストに追加し、リストをdivでラップしました。 <hr>タグのリストアイテムもいくつか使用しました。チェックボックスとラベルをすばやくクリックすると、ラッピングdivでmouseleaveイベントがトリガーされることがあります。クリックされたすべての要素がdiv.wrapperの子であるため、これは発生しません。

...
  wrapper.addEventListener(
    'mouseleave',
    (e) => {
      logger('mouseleave fired');
      console.log('mouseleave fired', e);
    },
    false
  );
...

jsfiddleデモ

これが複製のGIFです。手がかり領域(ある程度の強度と動きが許可されている)内をクリックし、ラベルと入力ボックスからのイベントをクリックすると、2つのmouseleaveイベントが発生しますエラーが発生し、マウスが本当に青い領域を離れると、3分の1になります。

Sample reproduction

4
Fish Pudding

@trincotの答えはほとんど私にとってうまくいきました。私の場合、私はポップオーバーを扱っています。ボタンをクリックすると、トリガーボタンの上にポップオーバーが表示されます。したがって、document.elementFromPoint(e.clientX, e.clientY)は、トリガーボタンではなくpopover要素を返します。これが私がこれを解決した方法です:

mouseleave(ev: MouseEvent) {
    const trigger: HTMLElement = document.getElementById('#myTrigger');
    const triggerRect = trigger.getBoundingClientRect();
    const falsePositive = isWithingARect(ev.clientX, ev.clientY, triggerRect);

    if (!falsePositive) {
        // do what needs to be done
    }
}

function isWithingARect(x: number, y: number, rect: ClientRect) {
  const xIsWithin = x > rect.left && x < rect.right;
  const yIsWithin = y > rect.top && y < rect.bottom;
  return xIsWithin && yIsWithin;
}
0
Dmitry