問題描述
今天同事遇到一個神一樣的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 <p id="ttt">
10 ttt</p>
11 <br />
12 <a href="https://www.baidu.com" name="n">百度一下</a>
13 <script type="text/javascript">
14 var i = 0;
15 var appendDiv = function (msg) {
16 var p = document.createElement('p');
17 if (msg) {
18 p.innerHTML = msg;
19 } else {
20 p.innerHTML = i++;
21 }
22 document.body.appendChild(p);
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 <p id="ttt">
10 ttt</p>
11 <br />
12 <a href="https://www.baidu.com" name="n">百度一下</a> dsfsdffd<br>
13 <script type="text/javascript">
14 var i = 0;
15
16 setInterval(function () {
17 var p = document.createElement('p');
18 p.innerHTML = i++;
19 document.body.appendChild(p);
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 p = document.createElement('p');
29 if (msg) {
30 p.innerHTML = msg;
31 } else {
32 p.innerHTML = i++;
33 }
34 document.body.appendChild(p);
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 <p id="ttt">
10 ttt</p>
11 <br />
12 <a href="https://www.baidu.com" name="n">百度一下</a> dsfsdffd<br>
13 <script type="text/javascript">
14 var i = 0;
15
16 setInterval(function () {
17 var p = document.createElement('p');
18 p.innerHTML = i++;
19 document.body.appendChild(p);
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 p = document.createElement('p');
29 if (msg) {
30 p.innerHTML = msg;
31 } else {
32 p.innerHTML = i++;
33 }
34 document.body.appendChild(p);
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 <p id="ttt">
10 ttt</p>
11 <br />
12 <a href="https://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 p = document.createElement('p');
18 p.innerHTML = i++;
19 document.body.appendChild(p);
20 }, 1000);
21 var appendDiv = function (msg) {
22 var p = document.createElement('p');
23 if (msg) {
24 p.innerHTML = msg;
25 } else {
26 p.innerHTML = i++;
27 }
28 document.body.appendChild(p);
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');
他的映射關系又建立起來瞭,如果是這樣的話,是說的過去的!!!