javascript code snippet -- Forwarding Mouse Events Through Layers
Anyone who has worked with web apps has likely created a masking element at some point, and the great thing about a masking element is that it intercepts user interaction, letting us create a pseudo-modal user interface. The masking element enables us to mask the entire screen, bringing focus to a particular element, or just create a window like effect. This behavior is demonstrated in the ExtJS librariesExt.Window when modal is set to true, among other places.
Yeah, so what?
The problem comes when we want to mask a part of the screen, but also want the elements behind that mask to continue responding to user interaction. This behavior is counter to most native behavior. What we are left with, is having to forward mouse events through the masking layer to the layer below, an option that simply does not exist in the standard JavaScript/DOM API.
需要实现如下的效果,有一个浮动层,需要层级在它之下的一个元素也能照常响应相应的事件
一个100*100的元素,边框为1px solid #406c99,它有两个事件(鼠标移入、鼠标移出):
onmouseover="this.style.borderColor='#f00';"
onmouseout="this.style.borderColor='#406c99';"
在不做特殊处理的情况下,它的事件将会是无法触发的,现在想让它正常触发,效果如下:
解决这样的问题有以下方案:
1、纯使用CSS的属性pointer-events,设置其为none (默认为auto)
优点:无需额外的代码
缺点:不支持IE(IE不支持此属性,IE9是否支持有待考评..)
2、捕捉事件获取鼠标的位置X、Y,然后触发层级较低元素的相应事件 (平时我们用调试工具选取页面中的元素,高亮显示的区域就是依据这个原理)
优点:兼容各浏览器
缺点:需要编写Javascript,效率并不高
这样获取有也有两种处理方法:
循环获取每一个元素的位置,然后对比鼠标的X、Y,效率低,不推荐;这里推荐使用elementFromPoint(浏览器都支持),直接传入X、Y,便可直接获取相应的DOM元素
比较折中的办法是,针对非IE的浏览器直接使用方案1,IE使用方案2进行优化。这种应用场景,可能会是一个新的产品上线了,需要引导用户如何去使用,会使用蒙板遮住整个页面,然后让某一元素可点击。
elementFromPoint的使用例子(移动鼠标时,如果那一点在某一元素的占位区域则添加3像素的红色边框,鼠标移开该元素时清除边框)
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=utf-8">
<script type="text/javascript" >
var selElem =null;
var origBorder ="";
function OnMouseMove (event) {
var posX = event.clientX,
posY =event.clientY;
var ua = navigator.userAgent,
isIE = /msie/i.test(ua),
isFF = /firefox/i.test(ua);
if(!isIE && !isFF) {
posX = event.pageX;
posY = event.pageY;
}
var info = document.getElementById("info");
info.innerHTML = event.clientX + ", " + event.clientY;
var overElem = document.elementFromPoint(posX, posY);
if(overElem && !overElem.tagName) {
overElem = overElem.parentNode;
}
if (selElem) {
if (selElem == overElem) {
return ;
}
selElem.style.border = origBorder;
selElem = null;
}
if (overElem && !/^(html|body)$/.test(overElem.tagName.toLowerCase()) ) {
selElem = overElem;
origBorder = overElem.style.border;
overElem.style.border = "3px solid red" ;
}
}
</script>
</head>
<body onmousemove="OnMouseMove (event);">
<div style="height:200px">
test test test test test test test
</div>
<div style="position:absolute; right:20px; top:30px;">
The current mouse position: <span id="info" style="border:1px solid #606060; padding:5px;"></span>
</div>
<br/><br/>
<textarea rows="4" style="width:200px; height:100px;">
test test test test test test test test test test test test test test
</textarea>
<div style="height:100px; margin-top:20px;">
test test test test test test testtest test test test test test testtest test test test test test testtest test test test test test test
</div>
</body>
</html>
遍历元素,然后找到相应的元素示例(效率比较低的一种)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<title>Sandbox</title>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<style type="text/css" media="screen">
body { padding: 0px; line-height: 1.8; color: rgb(0, 128, 0);">#000; }
.box {width: 50px; height: 50px; border: 1px solid white}
.highlight {}
#controls {position:absolute; top: 300px; color: white;}
</style>
</head>
<body>
<div id="container">
<div class="box" style="position:absolute; top: 25px; left: 25px;"></div>
<div class="box" style="position:absolute; top: 50px; left: 125px;"></div>
<div class="box" style="position:absolute; top: 100px; left: 25px;"></div>
<div class="box" style="position:absolute; top: 125px; left: 180px;"></div>
<div class="box" style="position:absolute; top: 225px; left: 25px;"></div>
<div class="box" style="position:absolute; top: 185px; left: 125px;"></div>
<div id="shield" style="position: absolute; width: 200px; top: 0px; opacity: 0.5; filter:alpha(opacity=50);"></div>
</div>
<div id="controls">
<input type="checkbox" checked="checked">Pass pointer events through</input>
Try clicking
</div>
<script>
$(".box").click(function(){
$(this).toggleClass("highlight");
});
function passThrough(e) {
$(".box").each(function() {
// check if clicked point (taken from event) is inside element
var mouseX = e.pageX;
var mouseY = e.pageY;
var offset = $(this).offset();
var width = $(this).width();
var height = $(this).height();
if (mouseX > offset.left && mouseX < offset.left+width
&& mouseY > offset.top && mouseY < offset.top+height)
$(this).click(); // force click event
});
}
$("#shield").click(passThrough);
var dthen = new Date();
setInterval(function(){
dNow = new Date();
$('#shield').css('height', ((dNow.getSeconds()+(dNow.getMilliseconds()/1000))*50)%300 +'px');
},10)
var doPassThrough = true;
$('input').click(function(){
doPassThrough = !doPassThrough;
if (doPassThrough){
$("#shield").click(passThrough);
} else {
$('#shield').unbind('click', passThrough);
}
});
</script>
</body>
</html>
在非IE浏览器中,控制pointer-events来达到想要的效果的示例
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<title>pointer-events test</title>
<meta name="generator" content="editplus" />
<meta name="author" content="" />
<meta name="keywords" content="" />
<meta name="description" content="" />
<meta http-equiv="content-Type" content="text/html;charset=utf-8">
<style type="text/css">
* {margin:0; padding:0;}
body {width:100%; height:100%;}
</style>
</head>
<body>
<div style="border:1px solid #406c99; width:100px; height:100px; margin-top:300px; margin-left:300px;" onmouseover="this.style.borderColor='#f00';" onmouseout="this.style.borderColor='#406c99';" title="hahaniu"></div>
<div style="position:absolute; top:0; left:0; width:100%; height:100%; opacity:.3; filter:alpha(opacity=30); overflow:hidden;" id="mask"></div>
<button style="position:absolute; z-index:9999; left:100px; top:80px; padding:2px;">开启pointer-events支持</button>
<script type='text/javascript'>
var isOpen = false;
document.getElementsByTagName("button")[0].onclick = function(evt) {
evt = evt || window.event;
this.innerHTML = (isOpen ? "开启" : "关闭") + "pointer-events支持";
document.getElementById("mask").style.pointerEvents = isOpen ? "" : "none";
isOpen = !isOpen;
}
</script>
</body>
</html>