昨天剛在公司解完案例,今天又在日常生活遇到實例,老天爺這暗示明顯無比,趕緊來篇筆記,以防出門雷劈!

最近迷上蝦皮拍賣。能跟 LINE 一樣跟賣家溝通超方便,尤其問完問題馬上接到賣家的實體照片真令人感動,不用出門與人面對面又保有臨櫃交談的即時性,真是阿宅的救星。遇到一個賣家很妙,凌晨四點多發訊息通知我補寄商品今天會到(不知對方有沒有早起的拎杯秒回「謝謝」嚇到? XD)。

在中華郵政網站輸入包裹號碼,按下「運輸資料」查詢鈕… 登楞!

JavaScript 試圖彈出視窗被 Chrome 瀏覽器封鎖了!

簡單來說,這是踩中「瀏覽器會封鎖不是使用者點擊直接觸發的 window.open」地雷。(詳細說明可參考: showModalDialog與IE快顯封鎖

無聊追進郵局包裹查詢程式幫忙 Debug 順便練功。網頁是用 Angular 寫的:

<a class="css_btn_class_gray ng-scope" ng-click="showInfo('Base64編碼');">運輸資料</a>

按鈕時會觸發 API 查詢

$scope.showInfo = function(b64Str){ $scope.queryAPI(b64Str); };

$scope.queryAPI 呼叫 AJAX 查詢,接收結果後呼叫 setTimeout(function() { $scope.createTRSView(); }, 500); 開啟結果視窗:

    $scope.queryAPI = function(b64Str){
        $scope.sendRecv("EB500100", "queryAPI", API_Url, vo,
            function(tota, isError) {
                //查詢處理(省略)
                //延遲0.5秒
                setTimeout(function() {
                    $scope.createTRSView();
                }, 500);
            }
        });
    };
    
    $scope.createTRSView = function(){
        var template = "<html><head><title>郵袋籃車查詢</title>…略…";
        var mw = window.open("", "_blank");
        $scope.mw = mw;
        if(mw) {
            mw.document.write(template.format($("#trs_template").html()));
        } else {
            alert("無法開啟查詢視窗");
        }
    };

由以上邏輯可發現,window.open 與 onclick 事件間隔了兩層非同步,第一層是呼叫 API 回傳結果(背後使用的是 $http Promise 機制),之後透過 setTimeout 又是另一層非同步,window.open 動作怎麼都不可能算成 onclick 的直接觸發行為,註定要被瀏覽器攔截!

遇到這種情境要怎麼處理呢?我想到幾種做法:

  1. 改為直接操作 XmlHttpRequest 以同步方式呼叫(網頁卡住等待伺服器傳回結果再繼續執行),收到結果後直接 window.open,避開 XHR 非同步執行、$http Promise 與 setTimeout 三層非同步,以符合 window.open 被包在 onclick 事件中的條件。 不過,這做法與 AJAX 精神背道而馳,我歸類為餿主意。
  2. 在 onclick 事件就 window.open 將結果視窗先開好(得先顯示「查詢中…」之類的動畫,不然會很乾),待 AJAX 呼叫完成後再將結果填入,但無法視 AJAX 呼叫結果決定要不要開視窗是一大缺點。
  3. 避用 window.open(),改以 IFrame 內嵌或直接在 DOM 建立 div 放入結果。

我個人偏好第 3 種做法,練功完畢。


Comments

Be the first to post a comment

Post a comment