切换时忽略 blur/focusout 事件 tabs/windows

Ignore blur/focusout events when switching tabs/windows

我有一个表单,只要其中一个字段成为焦点(即, 用户单击或标签进入),一个 div 包含有关的额外信息显示该字段。非常简单:onFocus 在此信息窗格上设置 display: none;,然后 onBlur 将其删除。

我只希望在单击同一页面上的其他元素时触发这些事件,但在切换到另一个 window 或选项卡时它们也会触发。每次 <Alt>-<Tab>.

看到内容在页面上来来去去超级烦人

JS有什么办法区分这两种模糊事件吗?


编辑: I made a codepen to illustrate the problem。打开它,单击文本输入字段,然后按 alt-tab 到另一个 window 以查看一些文本消失。

这里是有问题的代码:

<input id="foo" />

<p>
  Lorem ipsum dolor <span id="bar">sit amet consectetur</span>
</p>
.hidden {
  display: none;
}
const inputField = document.getElementById('foo')
const hiddenSpan = document.getElementById('bar')

inputField.addEventListener('focus', () => hiddenSpan.classList.add('hidden'))
inputField.addEventListener('blur', () => hiddenSpan.classList.remove('hidden'))

window 模糊时的预期行为是保留输入的焦点。

调查表明,当取消选择 window 时,首先会向活动元素发送模糊事件,然后向 window 发送模糊事件。

这两个 blur 事件快速连续发生,因此您可以利用它在适当的时候重新聚焦输入:

  • 记录最后一次输入blur的时间
  • 拦截window模糊事件
  • 如果输入模糊和 window 模糊之间的时间很短,重新聚焦输入

工作示例:

  const inputField = document.getElementById('input');
  const inputSpan = document.getElementById('bar');
  const windowSpan = document.getElementById('window-span');

  let inputBlurTime = 0;

  inputField.addEventListener('focus', () => {
    inputSpan.innerHTML = ' FOCUSED';
  });

  inputField.addEventListener('blur', evt => {
    inputSpan.innerHTML = ' BLURRED';
    inputBlurTime = new Date().getTime();
  });

  window.addEventListener('focus', () => {
    windowSpan.innerHTML = ' FOCUSED';
  });

  window.addEventListener('blur', () => {
    windowSpan.innerHTML = ' BLURRED';
    const windowBlurTime = new Date().getTime();
    if (windowBlurTime - inputBlurTime < 100) {
      windowSpan.innerHTML += ' (re-focusing input)';
      inputField.focus();
      inputSpan.innerHTML = ' RE-FOCUSED';
    }
  });
<h4>Preserve Input Focus on Window Blur</h4>
<input id="input" />

<p>Input <span id="bar">INIT</span></p>
<p>Window <span id="window-span">INIT</span></p>

Credit to Reddit user /u/jcunews1 for this answer:使用document.activeElement判断焦点是否真正离开了目标元素.

const inputField = document.getElementById('foo');
const hiddenSpan = document.getElementById('bar');

inputField.addEventListener('focus', () => hiddenSpan.classList.add('hidden'));
inputField.addEventListener('blur', (event) => {
  if (event.target !== document.activeElement) { // This is where the magic happens!
    hiddenSpan.classList.remove('hidden');
  }
});
.hidden {
  display: none;
}
<input id="foo" />

<p>
  Lorem ipsum dolor <span id="bar">sit amet consectetur</span>
</p>