问题描述
我正在尝试查找新呈现的元素在鼠标指针下。 (*)
这是我的代码:
<resources>
<resource>
<directory>assets</directory>
</resource>
<resource>
**<directory>./internal-assets</directory>**
<includes>
<include>**</include>
</includes>
<skipPngCrush>true</skipPngCrush>
</resource>
<resource>
<directory>data</directory>
</resource>
</resources>
btn.addEventListener('click',function () {
btn.remove();
for (let i = 0; i < 10; i++) {
lst.appendChild(document.createElement('li')).textContent = 'Element ' + i;
}
requestAnimationFrame(function () { requestAnimationFrame(function () {
const chosen = document.querySelector('li:hover');
alert(chosen && 'Your mouse on ' + chosen.textContent); // do something more with chosen
}); });
});
#btn { width: 200px; height: 200px; }
#lst { width: 200px; line-height: 20px; display: block; padding: 0; }
#lst li { display: block; height: 20px; width: 200px; overflow: hidden; }
#lst li:hover { background: #ccc; }
我很困惑,我需要2 <button id=btn>Click Me</button>
<ul id=lst><ul>
才能使我的代码正确执行。卸下一个raf,警报将改为显示requestAnimationFrame
。
代码对我来说也很难看。如何更优雅地实现它?
万一有人在意:我正在Firefox上运行我的代码。而且,作为我的Firefox扩展的一部分,该代码只需要定位到Firefox 60 +。
(*):背后的故事可能更复杂。但为了简单起见...
解决方法
这是您在此处发现的一个非常有趣的行为,即使我们强制重排或进行其他操作,浏览器似乎也不会在第二帧之前更新:hover
。
更糟糕的是,在Chrome中,如果您使用<button>
隐藏display:none
元素,它将一直停留在:hover
元素上,直到鼠标移动为止(而通常display:none元素不可访问:hover
。
规范中没有详细介绍:hover
的计算方式,因此很难说这本身就是一个“ bug”。
无论如何,最好的方法是通过document.elementsFromPoints方法找到该元素,该方法将同步工作。
btn.addEventListener('click',function ( evt ) {
btn.remove();
for (let i = 0; i < 10; i++) {
lst.appendChild(document.createElement('li')).textContent = 'Element ' + i;
}
const chosen = document.elementsFromPoint( evt.clientX,evt.clientY )
.filter( (elem) => elem.matches( "li" ) )[ 0 ];
alert(chosen && 'Your mouse on ' + chosen.textContent); // do something more with chosen
});
#btn { width: 200px; height: 200px; }
#lst { width: 200px; line-height: 20px; display: block; padding: 0; }
#lst li { display: block; height: 20px; width: 200px; overflow: hidden; }
#lst li:hover { background: #ccc; }
<button id=btn>Click Me</button>
<ul id=lst><ul>
,
我不能完全回答为什么您需要2架步枪的问题。
但是我可以通过async / await
为您提供更优雅的方式。创建一个名为nextTick
的小函数,该函数返回一个诺言。因此,您等待下一个帧。
因此,您可以首先等待按钮消失,创建元素,然后再次等待下一个绘画周期,以确保元素可访问
btn.addEventListener('click',async function () {
btn.remove();
await nextTick();
for (let i = 0; i < 10; i++) {
lst.appendChild(document.createElement('li')).textContent = 'Element ' + i;
}
await nextTick()
const chosen = document.querySelector('li:hover');
alert(chosen && 'Your mouse on ' + chosen.textContent); // do something more with chosen
});
function nextTick() {
return new Promise(requestAnimationFrame)
}
#btn { width: 200px; height: 200px; }
#lst { width: 200px; line-height: 20px; display: block; padding: 0; }
#lst li { display: block; height: 20px; width: 200px; overflow: hidden; }
#lst li:hover { background: #ccc; }
<button id=btn>Click Me</button>
<ul id=lst><ul>