JS判断鼠标进入容器的方向与window.open新窗口被拦截的问题


Posted in Javascript onDecember 23, 2016

一、鼠标进入容器方向的判定

判断鼠标从哪个方向进入元素容器是一个经常碰到的问题,如何来判断呢?

首先想到的是:获取鼠标的位置,然后经过一大堆的if..else逻辑来确定。这样的做法比较繁琐,下面介绍两种比较方便的方法:

第一种方法,利用圆和反正切三角函数

如下图所示:

JS判断鼠标进入容器的方向与window.open新窗口被拦截的问题

以div容器的中心点作为圆心,以高和宽的最小值作为直径画圆,将圆以[π/4,3π/4),[3π/4,5π/4),[5π/4,7π/4),[-π/4,π/4)划分为四个象限。

代码如下:

$(".box").on("mouseenter mouseleave",function(e){

/** 获取容器宽高 **/
var w = $(this).width();
var h = $(this).height();

/** 计算X和Y相对于圆心点的距离,如果不是正方形,按照X,Y谁小按谁进行比例缩放**/
var x = (e.pageX - $(this).offset().left - (w/2)) * ( w > h ? (h/w) : 1 );
var y = (e.pageY - $(this).offset().top - (h/2)) * ( h > w ? (w/h) : 1 );

/** 根据X,Y的值,做反正切atan2计算,返回值在[-π,π]之间 ,这里加上180,剔除负值**/
/** 如果不加180,则0,1,2,3对应下左上右**/
/** 除以90并四舍五入,使得可以以45度为分割线,获取象限**/
/** 加3与4取模,将0,1,2,3对应t,r,b,l既上右下左**/
var direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180 ) / 90 )+3)%4;
switch(direction) {
 case 0:
 /** 上 **/
 break;
 case 1:
 /** 右 **/
 break;
 case 2:
 /** 下 **/
 break;
 case 3:
 /** 左 **/
 break;
}});

这个方法中的Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180 )/90)+3)% 4公式比较难理解,首先得到鼠标坐标经过换算后的值,然后算出该坐标的弧度,接着换算成度数,加180去掉负数,随后转移象限将0123对应TRBL,如果不加180去掉负数,0123对应BLTR,有点不合CSS的习惯。

第二种方法,利用斜率

如下图所示:

JS判断鼠标进入容器的方向与window.open新窗口被拦截的问题

以浏览器左上角做原点,画坐标轴,向下为负,向右为正,和数学坐标系一致。中间的div的左上角坐标(x1,y1),右下角坐标(x2,y2),中心点的坐标(cx,cy)。如图两点的斜率为k(k<0),关于x轴对称的斜率为-k。

需要注意一点的是所有的Y轴坐标都是负数,因为就是将容器置于坐标系的第四象限。

$(".box").on("mouseenter mouseleave", function(e) {
 var w = $(this).width();
 h = $(this).height(),
 x1 = $(this).offset().left,
 y1 = -$(this).offset().top,
 x2 = x1 + w,
 y2 = y1 - h,
 cx = (x1 + x2) / 2,
 cy = (y1 + y2) / 2,
 k = (y2 - y1) / (x2 - x1),
 k1 = (-e.pageY - cy) / (e.pageX - cx),
 direction = -1;
 if ((k1 < -k) && (k < k1)) {
 direction = e.pageX > cx?1:3;
 } else {
 direction = -e.pageY > cy?0:2;//大家理解代码的时候一定记住,Y坐标都是负的
 }
 //0123对应TRBL
});

如上代码所示:当鼠标的位置与容器中心点所形成的斜率在(k,-K)之间,必然是左右移入或移出,如果鼠标的X坐标大于中心点CX,则是右边进入,否则为左边进入;若斜率不在(k,-k)之间,则是上下进入或出去,只要判断鼠标的Y坐标与中心点CY的大小关系即可,大于则是下边,相反就是上边。

二、window.open新窗口被拦截的问题

当我们使用window.open()方法打开一个窗口时,部分浏览器会检测是否是用户主动行为,若不是,则会阻止窗口的打开,例如在异步Ajax的回调函数中调用。

新窗口被拦截检测

窗口被阻止打开,如不给出提示,用户体验将会很不好,那如何检测窗口被阻止?

如下代码所示:

var newWin = null,
 isBlock = !1;
/** 新窗口被某些扩展阻止打开,会抛出错误,因此使用try..catch **/
try {
 newWin = window.open('http://www.baidu.com', '_blank');
 /** 新窗口被阻止时,返回值是undefined或null**/
 (!newWin) && (isBlock = !0);
} catch (ex) {
 isBlock = !0;
}
if (isBlock) alert('您阻止了窗口的打开。');

为何新窗口被拦截

浏览器设计者出于安全的考虑,window.open 命令在用户操作(trusted events)时, 才会正常的打开应该页面而不会被浏览器拦截。什么是trusted events?

The isTrusted read-only property of the Event interface is a boolean that is true when the event was generated by a user action, and false when the event was created or modified by a script or dispatched via dispatchEvent.

当前事件是由用户行为触发(例如鼠标点击按钮触发操作),便是trusted events,而用自定义事件dispatchEvent触发的事件则不是trusted events。

因此使用JS代码自动触发window.open() ,第二个参数不为_self,打开新窗口在大部分浏览器中会被拦截。如果第二个参数为_self,则不会被拦截,如window.open("http://www.baidu.com","_self")

如何Ajax回调中避免被拦截

很多人的需求是点击按钮发送Ajax请求,请求数据回来后,再使用window.open来打开新的窗口,由于是异步操作,直接window.open ,肯定会被拦截。这时我们可以变通以下,先打开一个空窗口,然后等数据回来后替换为需要的地址

如下所示:

<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>弹窗拦截测试</title>
 <style type="text/css">
 #btn{ width:100px; height: 30px; line-height: 30px; text-align:center; background-color:#0087dc; transition:all .2s; color:#fff; border-radius:3px;cursor:pointer; }
 #btn:hover{ background-color:#0060b2; }
 </style>
</head>
<body>
 <div id="btn">打开新窗口</div>
 <script type="text/javascript">
 btn.addEventListener('click',(e)=>{
  var xhr = new XMLHttpRequest();
  var newWin = window.open('about:blank');
  xhr.onreadystatechange = ()=>{
  if(xhr.readyState == 4){
   if(xhr.status == 200){
   newWin.location.href="http://www.baidu.com";
   }
  }
  };
  xhr.open('post','/dnslookup',!1);//异步方式
  xhr.setRequestHeader('content-type','application/x-www-form-urlencoded');
  xhr.send('host=www.baidu.com&rrtype=A');
 },!0);
 </script>
</body>
</html>

服务端代码如下:

var http = require('http'),
 url = require('url'),
 dns = require('dns'),
 qs = require('querystring'),
 fs = require('fs');

function router(req,res,pathname){
 switch(pathname){
 case '/dnslookup':
  lookup(req,res);
  break;
 default:
  showIndex(req,res);
 }
}
function showIndex(req,res){
 var pagePath = __dirname+'/'+'block.html';
 var html = fs.readFileSync(pagePath);
 res.end(html);
}
function lookup(req,res){
 var postData = '';
 req.on('data',function(data){
 postData+=data;
 });
 req.on('end',function(data){
 var json = qs.parse(postData);
 var hostname = json.host;
 var rrtype = json.rrtype;
 dns.resolve(hostname,rrtype,function(err,adresses){
  if(err){
  res.end(JSON.stringify({errcode:1,ips:[]}));
  }
  res.end(JSON.stringify({errcode:0,ips:adresses}));
 });
 
 });
}
http.createServer(function(req,res){
 var pathname = url.parse(req.url).pathname;
 req.setEncoding("utf8");
 res.writeHead(200,{'Content-Type':'text/html'});
 router(req,res,pathname);
}).listen(3000);

如上所示便可解决在Ajax回调中新窗口被拦截的问题。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流。

Javascript 相关文章推荐
JavaScript入门之基本函数详解
Oct 21 Javascript
js常用代码段收集
Oct 28 Javascript
火狐textarea输入法的bug的触发及解决
Jul 24 Javascript
ajaxFileUpload.js插件支持多文件上传的方法
Sep 02 Javascript
JavaScript中使用typeof运算符需要注意的几个坑
Nov 08 Javascript
浅谈js中的三种继承方式及其优缺点
Aug 10 Javascript
Bootstrap响应式侧边栏改进版
Sep 17 Javascript
Validform验证时可以为空否则按照指定格式验证
Oct 20 Javascript
关于vue中 $emit的用法详解
Apr 12 Javascript
详解创建自定义的Angular Schematics
Jun 06 Javascript
详解django模板与vue.js冲突问题
Jul 07 Javascript
Vue toFixed保留两位小数的3种方式
Oct 23 Javascript
Bootstrap和Java分页实例第一篇
Dec 23 #Javascript
Node.js利用Net模块实现多人命令行聊天室的方法
Dec 23 #Javascript
Bootstrap select多选下拉框实现代码
Dec 23 #Javascript
Bootstrap select实现下拉框多选效果
Dec 23 #Javascript
详解微信小程序开发—你期待的分享功能来了,微信小程序序新增5大功能
Dec 23 #Javascript
JavaScript用构造函数如何获取变量的类型名
Dec 23 #Javascript
JS中with的替代方法与String中的正则方法详解
Dec 23 #Javascript
You might like
用PHP实现WEB动态网页静态
2006/10/09 PHP
php防攻击代码升级版
2010/12/29 PHP
php+mysql实现无限级分类
2015/11/11 PHP
php通过两层过滤获取留言内容的方法
2016/07/11 PHP
PHP 二维array转换json的实例讲解
2018/08/21 PHP
PHP字符串和十六进制如何实现互相转换
2020/07/16 PHP
用JavaScript调用WebService的示例
2008/04/07 Javascript
JS回调函数的应用简单实例
2014/09/17 Javascript
详解JavaScript中void语句的使用
2015/06/04 Javascript
纯CSS3代码实现滑动开关效果
2015/08/19 Javascript
全面解析Bootstrap中tooltip、popover的使用方法
2016/06/13 Javascript
jQuery实现删除li节点的方法
2016/12/06 Javascript
JS实现留言板功能
2017/06/17 Javascript
vue做网页开场视频的实例代码
2017/10/20 Javascript
微信小程序选择图片和放大预览图片功能
2017/11/02 Javascript
基于Bootstrap实现城市三级联动
2017/11/23 Javascript
微信小程序实现打开内置地图功能【附源码下载】
2017/12/07 Javascript
[56:29]Secret vs Optic 2018国际邀请赛小组赛BO2 第一场 8.18
2018/08/19 DOTA
linux系统使用python监测系统负载脚本分享
2014/01/15 Python
解决python3运行selenium下HTMLTestRunner报错的问题
2018/12/27 Python
使用python将mysql数据库的数据转换为json数据的方法
2019/07/01 Python
python构造函数init实例方法解析
2020/01/19 Python
Python3打包exe代码2种方法实例解析
2020/02/17 Python
Python修改列表值问题解决方案
2020/03/06 Python
Windows下Anaconda和PyCharm的安装与使用详解
2020/04/23 Python
Python经典五人分鱼实例讲解
2021/01/04 Python
CSS3打造百度贴吧的3D翻牌效果示例
2017/01/04 HTML / CSS
德国净水壶和滤芯品牌:波尔德PearlCo(家用净水器)
2020/04/29 全球购物
结婚邀请函范文
2014/01/14 职场文书
个人委托书怎么写
2014/09/17 职场文书
2014年领班工作总结
2014/11/25 职场文书
单位实习鉴定评语
2015/01/04 职场文书
高一军训口号
2015/12/25 职场文书
《家庭教育》读后感3篇
2019/12/18 职场文书
详解Java线程池是如何重复利用空闲线程的
2021/06/26 Java/Android
SQL Server2019安装的详细步骤实战记录(亲测可用)
2022/06/10 SQL Server