Javascript 条件鼠标事件在重叠的同级元素之间传递
有没有标准的Javascript技术或库来管理不相关的重叠HTML元素之间的条件鼠标事件传递 例如,我在一堆HTML元素前面有一个部分透明的WebGL画布(由Three.js管理)(由e.js CSS3渲染器管理,但这不应该相关)。这些HTML元素已注册mouseover和mouseout事件。我希望漂浮在这些元素前面的3D对象能够阻止鼠标事件 我已经知道如何使用光线投射器来确定鼠标是否在3D对象上。我不知道的是,当3D对象不在画布之间时,如何允许鼠标事件“通过”画布到达底层HTML元素 我读过一些关于解决方案的书,在这些解决方案中,您将遍历DOM树,直到找到鼠标下面的元素。但这似乎过于复杂和缓慢。如果可能的话,我想做的是假装画布暂时不在那里,这样事件就可以自然地通过了Javascript 条件鼠标事件在重叠的同级元素之间传递,javascript,html,mouseover,event-passthrough,Javascript,Html,Mouseover,Event Passthrough,有没有标准的Javascript技术或库来管理不相关的重叠HTML元素之间的条件鼠标事件传递 例如,我在一堆HTML元素前面有一个部分透明的WebGL画布(由Three.js管理)(由e.js CSS3渲染器管理,但这不应该相关)。这些HTML元素已注册mouseover和mouseout事件。我希望漂浮在这些元素前面的3D对象能够阻止鼠标事件 我已经知道如何使用光线投射器来确定鼠标是否在3D对象上。我不知道的是,当3D对象不在画布之间时,如何允许鼠标事件“通过”画布到达底层HTML元素 我读
为了避免重新发明轮子,如果已经有一个用于此的库,那就太好了。因为您不想像David Thomas建议的那样使用指针事件,那么鼠标将始终指向具有最高z索引的元素,或者具有相同z索引的最后一个同级元素(当相对堆叠在其他元素之上时) 话虽如此,我唯一能想到的办法是:
我为你找到了一个非常简单的解决方案。由于您在一篇评论中提供了一个JSFIDLE示例,我想我只是稍微调整一下代码,让您看看 我所做的很简单。我检查鼠标是否与div重叠,并且在Three.js raycaster未命中任何框时调用函数“changeTheDivBox”
var divBox = document.getElementById("background");
divBox.addEventListener("mouseover", mouseOver, false);
divBox.addEventListener('mouseout', mouseOut, false);
var mouseOverBox = false; // Are you currently hovering over the "background" div?
function mouseOver () {
mouseOverBox = true;
}
function mouseOut () {
mouseOverBox = false;
}
function changeTheDivBox () { // This function is called when your raycaster does not hit any boxes
if ( mouseOverBox ) {
divBox.style.backgroundColor = 'blue';
}
else {
divBox.style.backgroundColor = 'green';
}
}
基本上:
Is the mouse overlapping the <div> ?
Yes.
Is the mouse (raycaster) hitting any boxes?
No.
Then Change the <div>'s color!
鼠标是否与鼠标重叠?
对
鼠标(raycaster)是否碰到任何盒子?
不
那就换个颜色吧!
我做的另一件事是使用css属性指针事件
。这使您可以通过使任何选择的元素不注册到鼠标事件来单击divs“再向后”。我添加了指针事件:无代码>
到主体
以禁用所有鼠标事件,然后我添加了指针事件:auto将>编码到div元素以在其上重新启用它们。所以鼠标现在只检测该分区上的事件。非常好
我保留了代码的其余部分。我个人更喜欢使用循环来不断检查重叠/光线投射是否仍然有效,而不是依赖于mouseIn/mouseOutSo,但这是您的示例,所以请尝试一下:)
就像个人喜好一样。我建议您避免将函数放在HTML中。它们并不总是像预期的那样工作。(此
对象一直引用窗口),它们会使代码更加混乱。我个人更喜欢将JavaScript保存在标记中。无论如何,EventListener比内联函数调用更强大:)您在代码中设置了z-index:-1#背景和显示的代码>绝对值代码>。这意味着:它位于body z层的后面,鼠标事件将以body或上面层中的任何其他对象为目标
您可以设置指针事件:无代码>的主体,甚至所有元素,但这可能是一件坏事与其他代码包括在网站以后。更喜欢用更高的z指数定位其他绝对覆盖元素,它们是:#container和#container>canvas
请参见路径是如何传播DOM事件的:
在捕获阶段,事件首先传播到视图,然后传播到、html和body,然后再传播到鼠标光标下显示的目标。而在鼓泡阶段,传播以相反的方式返回到视图
渲染的场景对象不在此DOM事件路径中。您已经对文档事件侦听器中的命中进行了光线跟踪。在传播到达#background元素之前执行此操作,如果已知事件已被处理,则在某个安全的地方执行此操作。您已经尝试了preventDefault()
。不幸的是,mousemove事件是不可取消的,因此这没有任何效果。稍后调用的事件侦听器检查preventedDefault
将得到false
。preventDefault()
的意思是防止在用户代理传播后执行默认操作,例如双击鼠标指针下的单词。如果应该执行默认操作,则不能使用preventDefault()
告诉其他侦听器该事件已被处理
您可以将Event的自定义属性设置为true。它至少可以在firefox中使用,但是W3C并没有严格指定主机对象。这可能会导致其他浏览器或将来出现一些错误。如果不应调用其他侦听器,则可以使用stopPropagation()
。在某些情况下,这可能会导致一些JavaScript框架出现不良行为或bug
另一种方法是在可供所有事件侦听器访问的范围内设置变量,例如全局范围或更好的封装匿名函数。这可能会保存最新处理的事件,并且在传播完成之前不会更改(之前不会触发下一个事件)
如果光线跟踪器未命中场景对象,还可以在文档的侦听器中处理以#背景为目标的事件<代码>事件。目标
保存被鼠标事件击中的最顶端的DOM元素
您的修改代码与演示
Is the mouse overlapping the <div> ?
Yes.
Is the mouse (raycaster) hitting any boxes?
No.
Then Change the <div>'s color!
<!DOCTYPE html>
<html>
<head>
<title>events passthrough</title>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style type="text/css">
body {
font-family: Monospace;
margin: 0px;
overflow: hidden;
}
div#background {
position: absolute;
top: 40px;
left: 40px;
width: 100px;
height: 100px;
background-color: pink;
/*z-index: -1; /* this is behind the body */
}
#container>canvas
{ position: absolute;
z-index: 100;
}
/* Explicitly disable mouse events on covering element. */
/* If z-index of background object is below zero then also disable body */
/* body, */
#container, #container canvas
{ pointer-events:none;
}
/* but let enabled all other elements */
*
{ pointer-events:auto;
}
</style>
</head>
<body>
<script type="text/javascript" src="https://mrdoob.github.io/three.js/build/three.min.js"></script>
<script type="text/javascript" src="https://mrdoob.github.io/three.js/examples/js/libs/stats.min.js"></script>
<div id="background"></div>
<div id="container"></div>
<script type="text/javascript">
var lastHandledEvent;
var container, stats;
var camera, scene, projector, renderer;
var particleMaterial;
var objects = [];
// don't run DOM relevant scripts before construction of DOM is guaranteed
document.addEventListener("DOMContentLoaded", function()
{
init();
animate();
}, false);
function init() {
container = document.getElementById( 'container' );
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 10, 300, 500 );
scene = new THREE.Scene();
var geometry = new THREE.BoxGeometry( 100, 100, 100 );
for ( var i = 0; i < 10; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { color: Math.random() * 0xffffff, opacity: 0.5 } ) );
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.scale.x = Math.random() * 2 + 1;
object.scale.y = Math.random() * 2 + 1;
object.scale.z = Math.random() * 2 + 1;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
scene.add( object );
objects.push( object );
}
var PI2 = Math.PI * 2;
particleMaterial = new THREE.SpriteCanvasMaterial( {
color: 0x000000,
program: function ( context ) {
context.beginPath();
context.arc( 0, 0, 0.5, 0, PI2, true );
context.fill();
}
} );
projector = new THREE.Projector();
renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setSize( window.innerWidth, window.innerHeight );
// renderer.domElement.style.position = 'absolute'; // is done in CSS
// renderer.domElement.style.zIndex = 100; // is done in CSS
container.appendChild( renderer.domElement );
// register document mousemove in capturing phase
// if you want to use another handlers subsequently
document.addEventListener('mousemove', onDocumentMouseMove, true );
document.querySelector('#background').addEventListener('mousemove', onMouseMove, false);
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
var background = document.getElementById('background');
function onDocumentMouseMove( event )
{
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 )
{
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
//event.preventDefault(); // mousemove is not cancelable
// extending event objects works at least in Firefox, not sure if crossbrowser combatible
// always be careful when extending DOM
event.handled = true; // add custom property
lastHandledEvent = event; // another way: store in a variable accessible by all handlers
// to remember this one was the last handled event
console.log('event marked as handled in document listener');
//event.stopPropagation(); // or could stop propagation, however,
// often not a good praxis in conjuction with frameworks
}
else if(event.target === background)
console.log('background could be handled in document listener');
}
function onMouseMove( event )
{
// if(event.defaultPrevented // mousemove not cancable, always false
if(event.handled) // TODO: check crossbrowser compatibility
// of custom properties on event objects.
// In doubt use a var outside the functions.
console.log('other listener: event.handled is: '+event.handled);
if(lastHandledEvent === event) // the safer way: use variable accessible
{
console.log('NOT handling event in other listener');
return;
}
console.log(event.target, '...or handled in other listener');
event.target.style.backgroundColor = '#'+('00000' + (Math.random() * 0xffffff).toString(16)).slice(-6);
}
function animate() {
requestAnimationFrame( animate );
render();
}
var radius = 600;
var theta = 0;
function render() {
theta += 0.1;
camera.position.x = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.y = radius * Math.sin( THREE.Math.degToRad( theta ) );
camera.position.z = radius * Math.cos( THREE.Math.degToRad( theta ) );
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
</script>
</body>
</html>