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之编码规范 推荐
May 23 Javascript
基于jquery实现的省市区级联无ajax
Sep 24 Javascript
jquery高级编程的最佳实践详解
Mar 23 Javascript
一段非常简单的js判断浏览器的内核
Aug 17 Javascript
浅谈JSON和JSONP区别及jQuery的ajax jsonp的使用
Nov 23 Javascript
实例详解JSON数据格式及json格式数据域字符串相互转换
Jan 07 Javascript
详解利用exif.js解决ios手机上传竖拍照片旋转90度问题
Nov 04 Javascript
AngularJS实现tab选项卡的方法详解
Jul 05 Javascript
JavaScript+HTML5实现的日期比较功能示例
Jul 12 Javascript
详解VueJS应用中管理用户权限
Feb 02 Javascript
vue2.0项目集成Cesium的实现方法
Jul 30 Javascript
vue实现防抖的实例代码
Jan 11 Vue.js
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中使用xmlreader读取xml数据示例
2014/12/29 PHP
PHP程序中使用adodb连接不同数据库的代码实例
2015/12/19 PHP
Symfony实现行为和模板中取得request参数的方法
2016/03/17 PHP
iframe 自适应高度[在IE6 IE7 FF下测试通过]
2009/04/13 Javascript
jquery中 $.expr使用实例介绍
2014/06/09 Javascript
jQuery中:first-child选择器用法实例
2014/12/31 Javascript
jquery处理页面弹出层查询数据等待操作实例
2015/03/25 Javascript
js实现缓冲运动效果的方法
2015/04/10 Javascript
jQuery实现图片向左向右切换效果的简单实例
2016/05/18 Javascript
D3.js实现文本的换行详解
2016/10/14 Javascript
javascript基于原型链的继承及call和apply函数用法分析
2016/12/15 Javascript
JavaScript中的toString()和toLocaleString()方法的区别
2017/02/15 Javascript
浅谈Vue.use到底是什么鬼
2020/01/21 Javascript
[03:40]2014DOTA2国际邀请赛 B神专访:躲箭真的很难
2014/07/13 DOTA
Python微信操控itchat的方法
2019/05/31 Python
PyQt5根据控件Id获取控件对象的方法
2019/06/25 Python
Django后台admin的使用详解
2019/07/08 Python
python 解决print数组/矩阵无法完整输出的问题
2020/02/19 Python
python中lower函数实现方法及用法讲解
2020/12/23 Python
html2canvas生成清晰的图片实现打印的示例代码
2019/09/30 HTML / CSS
KIKO MILANO俄罗斯官网:意大利领先的化妆品和护肤品品牌
2021/01/09 全球购物
澳大利亚家具商店:Freedom
2020/12/17 全球购物
学习心得体会
2014/01/01 职场文书
庆八一活动方案
2014/01/25 职场文书
主管会计岗位职责
2014/03/13 职场文书
《水乡歌》教学反思
2014/04/24 职场文书
高中教师考核方案
2014/05/18 职场文书
小学语文教研活动总结
2014/07/01 职场文书
车辆年检委托书范本
2014/10/14 职场文书
2014年宣传部个人工作总结
2014/12/06 职场文书
销售经理岗位职责范本
2015/04/02 职场文书
事业单位财务人员岗位职责
2015/04/14 职场文书
2016党员干部廉政准则学习心得体会
2016/01/20 职场文书
门面租赁合同范文
2019/08/06 职场文书
创业计划书之美容店
2019/09/16 职场文书
小程序后台PHP版本部署运行 LNMP+WNMP
2021/04/01 Servers