原生andriod浏览器回退后dom(click)事件全体失效问题探究
问题描述
今天同事遇到一个神一样的BUG:
在原生浏览器下,为dom元素绑定一个click事件,其中有个a标签外链,点击a后进入其他页面,点击浏览器后退后,页面点击事件全体失效!
我于是用ios测了下没事,用andriod其他浏览器试了下也没事,就是原生的有问题,怀疑是特定的手机有问题,又陆续换了几台,发现原生的都有问题
于是便开始找解决方案,下面就来聊下今天的漫长之路,这里先给会出问题的代码:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title></title> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 7 </head> 8 <body> 9 <div id="ttt"> 10 ttt</div> 11 <br /> 12 <a href="http://www.baidu.com" name="n">百度一下</a> 13 <script type="text/javascript"> 14 var i = 0; 15 var appendDiv = function (msg) { 16 var div = document.createElement('div'); 17 if (msg) { 18 div.innerHTML = msg; 19 } else { 20 div.innerHTML = i++; 21 } 22 document.body.appendChild(div); 23 }; 24 document.addEventListener('click', function (e) { 25 appendDiv('click') 26 }); 27 </script> 28 </body> 29 </html>
DOM事件丢失
第一步想到的当然是事件丢失了,或者就是不执行了,于是乎写了一段代码:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title></title> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 7 </head> 8 <body> 9 <div id="ttt"> 10 ttt</div> 11 <br /> 12 <a href="http://www.baidu.com" name="n">百度一下</a> dsfsdffd<br> 13 <script type="text/javascript"> 14 var i = 0; 15 16 setInterval(function () { 17 var div = document.createElement('div'); 18 div.innerHTML = i++; 19 document.body.appendChild(div); 20 21 var type = 'click'; //要触发的事件类型 22 var event = document.createEvent('MouseEvents'); 23 event.initMouseEvent(type); 24 document.dispatchEvent(event); 25 26 }, 1000); 27 var appendDiv = function (msg) { 28 var div = document.createElement('div'); 29 if (msg) { 30 div.innerHTML = msg; 31 } else { 32 div.innerHTML = i++; 33 } 34 document.body.appendChild(div); 35 }; 36 document.addEventListener('click', function (e) { 37 appendDiv('click') 38 }); 39 </script> 40 </body> 41 </html>
我定时器不停地向浏览器打印数字,而且触发document的click事件,他工作的蛮好的,但是当我点击百度一下再回来时候,便不执行了
因为我们没法在代码层面上获取dom的事件信息,所以暂时只能这样做,而我的判断是,没错!dom事件丢失了
Window事件未丢失
然后我又在这上面纠缠了好久,想试试windows的事件是否丢失,于是写下了以下代码:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title></title> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 7 </head> 8 <body> 9 <div id="ttt"> 10 ttt</div> 11 <br /> 12 <a href="http://www.baidu.com" name="n">百度一下</a> dsfsdffd<br> 13 <script type="text/javascript"> 14 var i = 0; 15 16 setInterval(function () { 17 var div = document.createElement('div'); 18 div.innerHTML = i++; 19 document.body.appendChild(div); 20 21 var type = 'click'; //要触发的事件类型 22 var event = document.createEvent('MouseEvents'); 23 event.initMouseEvent(type); 24 document.dispatchEvent(event); 25 26 }, 1000); 27 var appendDiv = function (msg) { 28 var div = document.createElement('div'); 29 if (msg) { 30 div.innerHTML = msg; 31 } else { 32 div.innerHTML = i++; 33 } 34 document.body.appendChild(div); 35 }; 36 document.addEventListener('click', function (e) { 37 appendDiv('click') 38 }); 39 40 window.onresize = function () { 41 appendDiv('onresize') 42 } 43 </script> 44 </body> 45 </html>
我点击回来后,发现事件还在,于是陷入深深的沉思.沉思.沉思.思.思.思.............
问题解决
最后我无意间将这个问题解决了,而且解决的方案匪夷所思:
1 <!DOCTYPE html> 2 <html> 3 <head> 4 <meta charset="utf-8" /> 5 <title></title> 6 <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 7 </head> 8 <body> 9 <div id="ttt"> 10 ttt</div> 11 <br /> 12 <a href="http://www.baidu.com" name="n">百度一下</a> dsfsdffd<br> 13 <script type="text/javascript"> 14 var t = document.getElementById('ttt'); 15 var i = 0; 16 setInterval(function () { 17 var div = document.createElement('div'); 18 div.innerHTML = i++; 19 document.body.appendChild(div); 20 }, 1000); 21 var appendDiv = function (msg) { 22 var div = document.createElement('div'); 23 if (msg) { 24 div.innerHTML = msg; 25 } else { 26 div.innerHTML = i++; 27 } 28 document.body.appendChild(div); 29 }; 30 document.addEventListener('click', function (e) { 31 appendDiv('click') 32 }); 33 </script> 34 </body> 35 </html>
整个解决方案耗费我两个多小时,而最终却是这么一段不起眼的代码:
var t = document.getElementById('ttt'); // var btnfree = document.getElementsByTagName('a'); 无效 // var n = document.getElementsByName('name');无效
现在问题是解决了,我却更疑惑了,一个大大的问号在我脑里回旋不去,尼玛在玩我啊......这是为什么??????
问题原理猜想
注意,此处完全是扯淡时间
andriod硬件加速
andriod原生浏览器本身使用了硬件加速功能,或者说andriod对自身的浏览器做了很好的处理
我们在页面上看到的页面具有一个dom树,而我们的事件js保存在另一个地方,而此时硬件加速为我们提供了一个类似png的中间件
他负责了通讯,但是在我们该网页转入后台时,这之间的映射关系却被破坏了
而我们js代码中若是多了这么一段代码:
var t = document.getElementById('ttt');
他的映射关系又建立起来了,如果是这样的话,是说的过去的!!!
PS:以上的理论显然无法说服我
垃圾回收/后台挂起
不得已的情况下,我想到了js的垃圾回收,dom结构活生生的在页面上,页面不会回收,里面的i一直在用也不会回收
但是我们的DOM树好像并没有神引用在页面中保存,因为没有浏览器就正好将所有的事件全部销毁了。
而后面我们在js中保存了一个dom树,他就没有销毁????
PS:这里我可以将t给delete了试试,但是我在家没有环境,只得明天再试了,今天暂时到这里
问题追踪
var t = document.getElementById('ttt'); t = null;
最后这样加一句,问题又会复现,所以垃圾回收的概率较高。
结语
当然,这是andriod浏览器本身一个BUG,但是如果我们可以从这种小BUG中发现大问题,或者原理性的东西,那真的该好好的研究一番了!!!
若是您有任何想法,请不吝赐教!!!