js 頁面間的通信


  看了一下公司原來的代碼,原頁面ajax post返回一個頁面完整的HTML,然后再打開一個新頁面並輸出ajax返回的所有代碼到新頁面上,在新頁面上以表單提交的形式實現重定向。

  任憑我想了半天也沒想出來,怎么樣不借助node就直接用js生成新頁面並輸入數據到新頁面上以初始化。然后百度,必應搜索(公司電腦安全設置不能用greenshadow翻牆,郁悶!),關鍵詞不對,沒搜到想要的結果。趁着面試,問了一下考官,關鍵詞是“頁面之間的通信”,而且說是有三種方法!這不,折騰了幾天,算是小有成果,且把相關的東西從頭到尾理一下,一起分享。

  目錄

  一.window對象的open方法
    1.window對象
    2.window對象的open方法
      (1)window.open(URL,name,features,replace)
      (2)用window.open方法實現頁面通信
    3.window.showModalDialog
      (1)window.showModalDialog
      (2)window.showModelessDialog
      (3)兼容性
    4.window的height和width
      (1)原生js的window.innerWidth和window.outerWidth
      (2)jquery innerWidth,outerWidth,clientWidth
      (3)改變窗口的height和width
  二.利用iframe
    1.frame
    2.iframe
    3.利用iframe實現頁面通信
      (1)父頁面控制子頁面
      (2)子頁面操作父頁面
  三.利用postMessage
    1.web Workers
    2.postMessage
    3.利用postMessage實現頁面間通信
      (1)父窗口往子窗口傳遞信息
      (2)子窗口往父窗口傳遞信息
  四.小結
    1.window.open
    2.window.showModalDialog
    3.iframe
    4.open with postMessage
  五.附錄
    1.IE下本地測試腳本會出現啟用ActiveX提示
    2.彈窗被瀏覽器攔截

  

  由於我是帶着復習的目的邊學邊寫的,而且有些相關性不太大的圖占位實在太大。。。上面目錄中把最重要的核心部分標出來了,如果大家有什么建議或意見,歡迎留言~(我估摸着以后對js頁面間的通信了解深入了,這篇也要重寫0.0...)

 

  一.window對象的open方法

  1.window對象

  Window對象:在瀏覽器BOM中相當於ECMA中的global全局變量。但是在以下方面有所不同:

  A.全局變量不能通過delete操作符刪除,但是直接在window對象上定義的屬性可以

  B.嘗試訪問未聲明的變量會拋出錯誤,但是通過查詢window對象,可以知道某個可能未聲明的變量是否存在。

//這種沒有寫文件屬性的代碼直接在控制台輸入就行,下同

var a=window.xxx;    //xxx不存在則a為undifined

 

  w3cSchool對window對象的屬性和方法總結

 

  2.window對象的open方法

  (1)window.open(URL,name,features,replace)

      URL:(可選)為空則打開空白新窗口。

      Name:(可選)子窗口的句柄,聲明新窗口的名稱。

      Features (可選) 聲明新窗口要顯示的標准瀏覽器的特征(必須是打開空白窗口)。

      Replace(可選) 為true的話則替換瀏覽歷史中的當前條目(返回回不去),默認為false,創建新條目。

 

  參數的詳細介紹:

  Name:值為用戶按照標識符定義規則定義的字符串。除非最高層窗口(Top)是通過window.open()打開的,否則其window對象的name屬性不會包含任何值(空字符串)。(可以為_self、_parent、_top、_blank)

  如現在同一文件夾路徑下有test.html,parent.html,和child.html三個文件,瀏覽器打開test.html,在控制台輸入

window.open('parent.html','parent');
window.name

  此時會彈出parent.html窗口,並且在test.html控制台中顯示"",即test.html(Top)的window.name屬性value為空。然后在parent.html窗口的控制台中輸入

window.name

  可以看到"parent"字符串,也就是說parent.html的window對象name屬性value為"parent"。現在回到test.html,控制台輸入

//等同於<a href='child.html' target='parent'></a>
window.open('child.html','parent');

  運行之后可以發現test.html沒動靜,但是parent.html以_self的形式自動跳轉到了child.html。(這里在一個窗口控制了另一個窗口的跳轉,也算頁面通信的一種吧)

  小結一下:運行window.open('URL','Name');時,如果有名為Name的窗口或框架,則在該窗口或框架加載URL;否則,創建一個新窗口打開URL並命名其為Name。

 

  Features:如果第二個參數Name並不是一個已經存在的窗口或框架,那么open方法會根據第三個參數設定的字符串創建一個新窗口或新標簽頁。不打開新窗口時,會忽略第三個參數。也就是說這個參數是用來設置新打開窗口的屬性的。具體的屬性見w3cschool上截圖如下

  示例:

window.open('parent.html','parent','width=300,height=300,toolbar=no');

  

  (2)用window.open方法實現頁面通信

  window.open方法返回打開URL對應窗口的Window對象的引用,當URL為空或不存在的時候返回Window about blank(FF中測試), 當被攔截的時候返回undefined(opera中測試)。借這個返回值來實現頁面間的通信。

  注意順序 test.html -> parent.html -> child.html

  在test.html中用newWindow取得parent.html中Window的引用

var newWindow = window.open('parent.html', 'parent');

 

  通過newWindow在test.html中操作parent.html。(前提是parent.html是由test.html打開的)

  A.關閉parent.html 

newWindow.close();

  B.操作parent.html中的DOM

newWindow.document.body.innerHTML = 'your code here';

  C.控制parent.html跳轉(和上面的效果差不多)

newWindow.location.href='child.html';  //絕對URL也行 newWindow.location.href = 'http://www.baidu.com';

  D.parent.html通過window.opener反向操作test.html (在parent.html中)

window.opener.document.body.innerHTML = '哈哈,我也能操作你!';

  由於parent.html由test.html打開,因此parent.html的window.opener指向test.html的Window對象。為了維護這種聯系,並不能用window.opener.close()來關閉test.html,並且當test.html被手動關閉的時候,parent.html的window.opener變成了null。值得注意的是parent.html依舊可以控制test.html的跳轉,並且始終指向test.html的標簽頁(只要沒關)。

  有些瀏覽器(如IE8和Chrome)會在獨立的進程中運行每一個標簽頁。當一個標簽頁打開另一個標簽頁時,如果兩個Window對象之間需要彼此通信,那么新標簽頁就不能運行在獨立的進程中。在Chrome中,將新創建標簽頁的opener設置為null(解除引用關系),即表示在單獨的進程中運行新標簽頁。

  

  3.window.showModalDialog

  window.showModalDialog稱為模態對話框,window.showModelessDialog稱為非模態對話框。好吧,不得不承認,之前沒見過它們,out了~

  (1)window.showModalDialog

var returnV = window.showModelDialog(URL, args, features);

  參數:

    URL :文檔路徑
    args : 用於向對話框傳遞參數, 類型不限
    features: 可選參數, 用於描述對話框

  返回值:

    新打開窗口的window對象

 

  使用showModelDialog實現頁面通信

  父頁面向子頁面

//in test.html
var str = 'what?';
window.showModalDialog('parent.html', str, 'dialogWidth=200px;dialogHeight=200px');
//in parent.html
var arr = window.dialogArguments; 
console.log(arr); 

  子頁面向父頁面

//in test.html
var str = window.showModalDialog('parent.html', '', 'dialogWidth=200px;dialogHeight=200px');
alert(str);
//in parent.html
window.returnValue="test!";

  

  (2)window.showModelessDialog

  非模態對話框,打開時與模態對話框總是置頂不一樣,非模態對話框可以進行其它操作。

// in test.html
var str = window.showModelessDialog('parent.html', '', 'dialogWidth=200px;dialogHeight=200px');
alert(str.returnValue);
//in parent.html
window.returnValue="test!";

  

  (3)兼容性

  雖然我貌似把老古董翻出來了,但是測試模態對話框的兼容性不咋地啊。。。

  window.showModalDialog兼容性

    opera 28.0.1750.51      不兼容

    chrome 43.0.2357.65   不兼容

    IE,Firefox,360安全,360急速 支持

  window.showModelessDialog兼容性

    IE支持,但是上例返回object Window

    opera,chrome,firefox,360系列均不支持

  過了這么多年,模態對話框的支持依舊這么差,而且和alert()一樣強制置頂,用戶體驗也不好,沒能進入w3c的標准(上面圖里面明顯也沒有)。故而這里就不細細討論了,不建議使用,除非自己寫個兼容性良好的插件,把它封裝進去。如果有興趣,建議參考 http://www.cnblogs.com/jhxk/articles/1761347.html 。

  

  4.window的height和width

  這一個應該大家都見過,這里只是參考高級程序設計第三版小結一下。

  (1)原生js的window.innerWidth和window.outerWidth

  由於height和width實現其實是一樣的,只是方向不一樣罷了,這里就以width作為代表。

  高級程序設計第三版上寫着:

    FF, IE, oprea, safari中: innerWidth 返回頁面視圖區大小(減去邊框),outerWidth 返回瀏覽器窗口本身的尺寸

    Chrome中: innerWidth = outerWidth 為視口(viewport) 尺寸

  我測試了一下我的chrome,結果和上面的不一樣,不知道它說的是哪一個版本。我的測試版本 41.0.2272.76,結果是chrome的顯示結果和其余四種並沒有什么不同。

  另外,高三中獲取頁面視口大小的兼容性代碼

var pageWidth = window.innerWidth,
 pageHeight = window.innerHeight;
if (typeof pageWidth != 'number') {
    if (document.compatMode == 'CSS1Compat') {  //頁面是否處於標准模式
        pageWidth = document.documentElement.clientWidth;
        pageHeight = document.documentElement.clientHeight;
    } else {              //IE6混雜模式,chrome混雜模式兩個執行語句都可用
        pageWidth = document.body.clientWidth;
        pageHeight = document.body.clientHeight;
    }
}

  經FF和Chrome測試,在<!DOCTYPE html>的標准下,document.body.clientWidth返回body元素占位的寬度。沒有<!DOCTYPE html>,則document.body.clientWidth表示視口寬度。而document.documentElement.clientWidth一直返回視口寬度。

  

  (2)jquery innerWidth,outerWidth,clientWidth

  由於我用jquery用得比較多,當然也要測試一下咯。然而不管怎么測試,$(window).innerWidth == $(window).outerWidth。於是看一下源碼

//jqery1.11.0
// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
jQuery.each({
    Height: "height",
    Width: "width"
}, function(name, type) {
    jQuery.each({
        padding: "inner" + name,
        content: type,
        "": "outer" + name
    }, function(defaultExtra, funcName) {
        // margin is only for outerHeight, outerWidth
        jQuery.fn[funcName] = function(margin, value) {
            var chainable = arguments.length && (defaultExtra || typeof margin !== "boolean"),
                extra = defaultExtra || (margin === true || value === true ? "margin" : "border");

            return access(this, function(elem, type, value) {
                var doc;

                if (jQuery.isWindow(elem)) {
                    // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
                    // isn't a whole lot we can do. See pull request at this URL for discussion:
                    // https://github.com/jquery/jquery/pull/764
                    return elem.document.documentElement["client" + name]; //由此可見window對象一律返回clientHeight or clientWidth
                }

                // Get document width or height
                if (elem.nodeType === 9) {
                    doc = elem.documentElement;

                    // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
                    // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
                    return Math.max(
                        elem.body["scroll" + name], doc["scroll" + name],
                        elem.body["offset" + name], doc["offset" + name],
                        doc["client" + name]
                    );
                }

                return value === undefined ?
                    // Get width or height on the element, requesting but not forcing parseFloat
                    jQuery.css(elem, type, extra) :

                    // Set width or height on the element
                    jQuery.style(elem, type, value, extra);
            }, type, chainable ? margin : undefined, chainable, null);
        };
    });
});

  好吧,其實jquery也就判斷然后封裝一下。

  附一張w3cshool的NodeType圖,權當復習一下

  

  (3)改變窗口的height和width

  首先取得瀏覽器窗口左邊和上邊的位置,屬性為(screenTop與ScreenLeft原理相同,screenX與screenY原理相同,為了簡便,均寫一個)
    screenLeft: IE,safari,opera,Chrome
    screenX:    firefox

  具體表現
    IE,opera: 屏幕左邊到window對象表示的頁面可見區域的距離
    Chrome,FF,opera: 整個瀏覽器窗口相對於屏幕的坐標值
  此外FF,Safari,Chrome中始終返回TOP.screenX,Top.screenY。即使在頁面由於被設置了外邊距而發生偏移的情況下,相對於window對象使用screenX和screenY也都返回相同的值。而IE和opera則會給出框架相對於屏幕邊界的精確坐標值。

  高三中獲取窗口位置的函數:

var leftPos = (typeof window.screenLeft == 'number') ? window.screenLeft : window.screenX;
var topPos = (typeof window.screenTop == 'number') ? window.screenTop : window.screenY;

  改變窗口位置(測試的時候在FF和Chrome中均被禁用......)
    window.moveTo(x, y)  左上角移動到坐標(x,y)
    window.moveBy(x, y)  窗口左移x,下移y

  改變窗口大小(測試的時候在FF和Chrome中均被禁用......)
    window.resizeTo(x,y)  新大小寬x像素,高y像素
    window.resizeBy(x,y) 寬高變化只差(后來的減原來的)

  

  二.利用iframe

  說實話,frame,frameset,iframe我之前從未用過,雖然感覺挺神奇、挺有用,貌似在網站見到它們都是以插件的容器存在。

  1.frame  

  如果頁面中包含框架,那么每一個框架都有自己的window對象,並且保存在frames集合中。

  Top對象始終指向最高層框架,即頂級瀏覽器窗口。用Top對窗口進行選定一般不會出問題。

  Parent對象始終指向當前框架的直接上層框架,如果在沒有框架的情況下,則一定等於top(此時都等於window)。

    frame標簽定義 frameset 中的一個特定的窗口(框架)。

    frameset 中的每個框架都可以設置不同的屬性 (frame的集合)。frameset被用來組織多個窗口(框架)。每個框架存有獨立的文檔,都有自己的一套構造函數,雖然它們一一對應,但是並不相等。在其最簡單的應用中,frameset 元素僅僅會規定在框架集中存在多少列或多少行。您必須使用 cols 或 rows 屬性。

  W3Cschool重要提示:您不能與 <frameset></frameset> 標簽一起使用 <body></body> 標簽。不過,如果您需要為不支持框架的瀏覽器添加一個 <noframes> 標簽,請務必將此標簽放置在 <body></body> 標簽中!

  由於frame和frameset限制太多,靈活性不足,只在比較老的網站里面可能還能見到(雖然兼容性良好),因此把重心集中在iframe上。

  

  2.iframe

  iframe 元素會創建包含另外一個文檔的內聯框架(即行內框架)。在 HTML 4.1 Strict DTD 和 XHTML 1.0 Strict DTD 中,不支持 iframe 元素。

  最簡單的使用案例:

// in test.html
<!DOCTYPE html>
<html>
<body>
    <iframe src="parent.html"></iframe>
</body>
</html>
// in parent.html
<!DOCTYPE html>
<html>
<body>
    這里是首部模塊
</body>
</html>

  然后就可以在test.html中看到parent.html的內容(嵌進去了)。只需要稍微規范化一下(設置border等屬性),就和普通元素看起來一模一樣。功能挺類似JSP的include和jsp:include的。很明顯,iframe很靈活,可以在body標簽內任何位置,前途光明。以下是w3cschool上iframe的屬性圖:

  

  3.利用iframe實現頁面通信

  (1)父頁面控制子頁面

<!-- in test.html -->
<!DOCTYPE html>
<html>
<body onload="load()">
    <iframe src="parent.html"></iframe>
    <script type="text/javascript">
        function load() {
            top[0].document.body.innerHTML = 'test.html控制parent.html';
        }
    </script>
</body>
</html>
<!-- in parent.html -->
<!DOCTYPE html>
<html>
<body>
    這里是首部模塊
</body>
</html>

  parent.html和上文一樣保持不變。值得注意的是,有關對iframe的操作必須在iframe加載完以后,也就是必須利用onload或者document.readyState=="complete"或者jquery的$.ready()。否則操作在iframe加載前就執行了,iframe沒有變化。

  

  (2)子頁面操作父頁面

<!-- in test.html -->
<!DOCTYPE html>
<html>
<body onload="load()">
    <iframe src="parent.html"></iframe>
    <script type="text/javascript">
        function load() {
            top[0].document.body.innerHTML = 'test.html控制parent.html';
        }
    </script>
    <p id="text">yes</p>
</body>
</html>
<!-- in parent.html -->
<!
DOCTYPE html> <html> <head> <script type="text/javascript"> function load() { parent.document.getElementById('text').innerHTML = '相互作用'; } </script> </head> <body onload='load()'> aaa </body> </html>

  在子頁面控制台中輸入parent.document.body.innerHTML可以查看父頁面body的內容,里面可以看到子頁面就是一個iframe標簽,也就是說子頁面不能通過父頁面來操作自己(雖然沒有必要~~~)。盡管相互之間操作的句柄有很多種,但是建議一直使用top。如在上例中test.html對應top,而parent.html對應top[0],無論是在哪個頁面都不會變(相當於絕對URL)。

  

  三.利用postMessage

  隨着html5的崛起,很多的實用API都映入我們的眼簾,postMessage就是其中的一種。

  1.web Workers

  一般情況下,當在 HTML 頁面中執行腳本時,頁面的狀態是不可響應的,直到腳本已完成。

  web worker 是運行在后台的 JavaScript,獨立於其他腳本,不會影響頁面的性能(獨立的線程,稱為工作線程)。您可以繼續做任何願意做的事情:點擊、選取內容等等,而此時 web worker 在后台運行。除了IE主流瀏覽器均支持。

  分類:

    HTML5 中的 Web Worker 可以分為兩種不同線程類型,一個是專用線程 Dedicated Worker,一個是共享線程 Shared Worker。(有關更多H5多線程的信息,參照 http://www.ibm.com/developerworks/cn/web/1112_sunch_webworker/)

  

  2.postMessage

  在Web Worker中,postMessage是MessagePort對象的方法之一,用來推送數據(與之相對的為onmessage,用來接收數據)。

  postMessage(data,origin)

  參數:

    data:要傳遞的數據,html5規范該參數可以是JavaScript的任意基本類型或可復制的對象,然而部分瀏覽器只能處理字符串參數,所以傳遞參數的時候需要使用JSON.stringify()方法對對象參數序列化,在低版本IE中引用json2.js可以實現類似效果。

    origin:字符串參數,指明目標窗口的源,協議+主機+端口號[+URL],URL會被忽略,所以可以不寫,這個參數是為了安全考慮,postMessage()方 法只會將message傳遞給指定窗口,當然如果願意也可以建參數設置為"*",這樣可以傳遞給任意窗口,如果要指定和當前窗口同源的話設置為"/"。

  

  3.利用postMessage實現頁面間通信

  (1)父窗口往子窗口傳遞信息(好吧,還是利用了iframe 或 window.open(見四.4),只是傳遞信息的方式不一樣)

//in test.html
<!DOCTYPE html>
<html>
<head>
    <title>postMessage</title>
    <script type="text/javascript">
    function sendMsg(){
        //通過postMessage向子窗口發送數據
        document.getElementById('child').contentWindow.postMessage(document.getElementById('message').value,'/');
    }
    </script>
</head>
<body>
    <iframe src="parent.html" id="child"></iframe>
    <input type="text" id="message">
    <input type="button" value="sent msg" onclick="sendMsg()">
</body>
</html>
// in parent.html
 <html> 
 <head> 
     <title>postMessage child page</title> 
     <script type="text/JavaScript"> 
         //event 參數中有data屬性,就是父窗口發來的數據
         window.addEventListener('message', function(event) {
             //顯示數據
             document.body.innerHTML += event.data;
         }, false);
     </script> 
 </head> 
 <body> 
     
 </body> 
 </html>

  在test.html窗口輸入數據點擊send msg可以看到內嵌的iframe中顯示數據。

  

  (2)子窗口往父窗口傳遞信息

<!-- in test.html -->
<!DOCTYPE html>
<html>
<body onload="load()">
    <iframe src="parent.html"></iframe>
    <script type="text/javascript">
        function load() {
            window.addEventListener('message', function(event) {
                document.getElementById('text').innerHTML += event.data;
            }, false);
        }
    </script>
    <p id="text">yes</p>
</body>
</html>
<!-- in parent.html -->
<!DOCTYPE html> <html> <head> <script type="text/javascript"> function load() { parent.postMessage('子窗口往父窗口傳遞數據','/'); } </script> </head> <body onload='load()'> aaa </body> </html>

  可以在父窗口看到子窗口傳來的'子窗口往父窗口傳遞數據'。

  學習於:http://www.cnblogs.com/dolphinX/p/3464056.html ,http://www.ibm.com/developerworks/cn/web/1301_jiangjj_html5message/

  

  四、小結

  由於都是新學習,自己測試,寫得不好莫怪哈。

  最后我們回到最開始的那個問題:原頁面ajax post返回一個頁面完整的HTML,然后再打開一個新頁面並輸出ajax返回的所有代碼到新頁面上,在新頁面上以表單提交的形式實現重定向。

  解決方案:

  1.window.open

  具體實現:www.夢縈無雙.xyz/newWindow/open/buy.html (由於action為空,因此如果是新創建的新頁面,那么submit后是空白頁,否則相當於reload)

<!-- in buy.html -->
<!DOCTYPE html>
<html>
<body>
    <input type="button" value="openNewWindow" onclick="openWin()">
    <script type="text/javascript">
        function openWin() {
            var newWindow = window.open(),
                contentW = [
                    '<style type="text/css">',
                        '.hidden{',
                            'border: 0;',
                            'width: 0;',
                            'height: 0;',
                            'display: block;',
                            'overflow: hidden;',
                        '}',
                    '</style>',
                    '<p>Redirecting!</p>',
                    '<form id="J-form" action="" method="post" class="hidden" target="_self">', //設置submittarget為_blank會被攔截
                        '<input type="text" name="name" value="admin"/>',
                        '<input type="text" name="pwd" value="admin"/>',
                        '<input type="submit" name="submitForm">',
                    '</form>',
            ].join('');

            newWindow.document.body.innerHTML = contentW;
            setTimeout(function(){
                newWindow.document.getElementById('J-form').submit();        
            },3000);
        }
    </script>
</body>
</html>

 

  2.window.showModalDialog(差評,兼容性不好)

  具體實現:www.夢縈無雙.xyz/newWindow/showModalDialog/buy.html (chrome,opera不兼容)

<!-- in buy.html -->
<!DOCTYPE html>
<html>
<body>
    <input type="button" value="openNewWindow" onclick="openWin()">
    <script type="text/javascript">
        function openWin() {
            var    contentW = [
                    '<style type="text/css">',
                        '.hidden{',
                            'border: 0;',
                            'width: 0;',
                            'height: 0;',
                            'display: block;',
                            'overflow: hidden;',
                        '}',
                    '</style>',
                    '<p>Redirecting!</p>',
                    '<form id="J-form" action="" method="post" class="hidden" target="_self">', //設置submittarget為_blank會被攔截
                        '<input type="text" name="name" value="admin"/>',
                        '<input type="text" name="pwd" value="admin"/>',
                        '<input type="submit" name="submitForm">',
                    '</form>',
            ].join('');

            window.showModalDialog('newWindow.html',contentW);
        }
    </script>
</body>
</html>
<!-- in newWindow.html -->
<!DOCTYPE html>
<html>
<head>
    <script type="text/javascript">
        str = window.dialogArguments;
        setTimeout(function() {
            document.body.innerHTML = str;
            setTimeout(function() {
                document.getElementById('J-form').submit();
            },2000);
        }, 2000);
    </script>
</head>
<body>

</body>
</html>

 

  3.iframe(無法打開新頁面,也無法通過iframe控制用別的方法打開的新頁面)

  具體實現:www.夢縈無雙.xyz/newWindow/iframe/buy.html

<!-- in buy.html -->
<!DOCTYPE html>
<html>
<head>
    <title>iframe模擬</title>
    <style type="text/css">
    .hidden{
        border  : 0;
        width   : 0;
        height  : 0;
        display : block;
        overflow: hidden;
    }
    </style>
</head>
<body>
    <iframe src="newWindow.html" class="hidden" id="newWin"></iframe>
    <a href="newWindow.html" target="_blank" onclick="sendMsg()">點擊打開新頁面</a>
    <script type="text/javascript">
        function sendMsg() {
            document.getElementById('newWin').style.border = '1px solid #333';
            document.getElementById('newWin').style.width = '300px';
            document.getElementById('newWin').style.height = '300px';
            var contentW = [
                    '<style type="text/css">',
                        '.hidden{',
                            'border: 0;',
                            'width: 0;',
                            'height: 0;',
                            'display: block;',
                            'overflow: hidden;',
                        '}',
                    '</style>',
                    '<p>Redirecting!</p>',
                    '<form id="J-form" action="" method="post" class="hidden" target="_self">', //設置submittarget為_blank會被攔截
                        '<input type="text" name="name" value="admin"/>',
                        '<input type="text" name="pwd" value="admin"/>',
                        '<input type="submit" name="submitForm">',
                    '</form>',
            ].join('');
            top[0].document.body.innerHTML += contentW;
            setTimeout(function() {
                top[0].document.getElementById('J-form').submit();
            }, 3000);
        }
    </script>
</body>
</html>
<!-- in newWindow.html -->
<!DOCTYPE html>
<html>
<body>

</body>
</html>

 

  4.open with postMessage

  具體實現:www.夢縈無雙.xyz/newWindow/postMessage/buy.html

<!-- in buy.html -->
<!DOCTYPE html>
<html>
<head>
    <title>open with postMessage</title>
    <script type="text/javascript">
    function sendMsg(){
        var newWin = window.open('newWindow.html'),
            str = [
                    '<style type="text/css">',
                        '.hidden{',
                            'border: 0;',
                            'width: 0;',
                            'height: 0;',
                            'display: block;',
                            'overflow: hidden;',
                        '}',
                    '</style>',
                    '<p>Redirecting!</p>',
                    '<form id="J-form" action="" method="post" class="hidden" target="_self">', //設置submittarget為_blank會被攔截
                        '<input type="text" name="name" value="admin"/>',
                        '<input type="text" name="pwd" value="admin"/>',
                        '<input type="submit" name="submitForm">',
                    '</form>',
            ].join('');
        setTimeout(function() {
            newWin.postMessage(str, '/');
            setTimeout(function() {
                newWin.document.getElementById('J-form').submit();
            }, 3000);
        }, 1000);
    }
    </script>
</head>
<body>
    <input type="button" value="sentMsg" onclick="sendMsg()">
</body>
</html>
 <!-- in newWindow.html -->
 <!DOCTYPE html>
 <html> 
 <head> 
     <title>postMessage child page</title> 
     <script type="text/JavaScript"> 
         window.addEventListener('message', function(event) {
             //顯示數據
             document.body.innerHTML += event.data;
         }, false);
     </script> 
 </head> 
 <body> 
     
 </body> 
 </html>

  

  綜上所述,可以猜測原來的功能實現應該是使用window.open()方法,2和4方法都需要中間頁面,而3方法無法打開新頁面。也算是解決了一個問題。

  值得注意的是:

  1.在<script>標簽里面用數組的join方法拼接HTML的<script>的標簽的時候,引擎會將<script>和最近的</script>閉合在一起,即便是以有引號"</script>"的形式存在的。

  2.用textarea裝完整script代碼的時候,取出來代碼並不會執行,因為<>被轉義了(控制台輸出一下就能看到)。

  3.即便textarea里面裝的只是執行代碼,加在外部script標簽里面也還是不會執行(雖然看起來和本來就存在一樣)。

 

  五.附錄

  由於有關跨域的問題實在錯綜復雜,這里就只是討論同源的情況(其實兩個或者多個頁面就在同一個文件夾目錄下)。另外加點料:

  1.IE下本地測試腳本會出現啟用ActiveX提示

  因為本地打開頁面使用的是file協議,而如果是掛載在tomcat或者IIS上或者使用httpserver,則能使用http協議打開本地文件,就不會出現ActiveX啟用提示。

 

  2.彈窗被瀏覽器攔截

  在廣告泛濫的時代,瀏覽器安全十分重要,因此一般情況下彈窗都會被瀏覽器的安全策略攔截。

  (1)攔截標准:

    對於瀏覽器判斷是用戶操作打開的窗口,可以彈出;瀏覽器判斷不是用戶操作打開的窗口,會攔截。

  (2)不被攔截的措施:

    利用html原生的方法,少用js方法,具體如下

  A.綁定事件在a標簽上,通過href打開新窗口,設置target=_blank彈出新窗口。如新浪SAE打開代碼編輯頁(有些瀏覽器還是會攔截...)。

  B.通過form表單的submit()方法(或內部input type="submit"的click方法),設置target=_blank實現打開新頁面。在我遇到的問題當中就是用這個解決第三方支付的跳轉(支付完了還要在原頁面確認支付成功)。

  對於B還有很多需要注意的地方,都和時延有關。

    當用setTimeout延時執行submit()的時候,有的瀏覽器會攔截。

    異步ajax回調中執行submit()的時候,彈窗會被攔截。當時具體的情況為判斷data.status為true的時候執行submit()方法,false則彈出data.errMsg中的錯誤信息。初步估計瀏覽器判斷有時延的時候(由於異步肯定沒有同步快,則一定有時延),就認為是為非用戶直接操作的submit(),於是就被攔截了(具體的還是不大懂,搜了好久了~如果有懂瀏覽器攔截原理的大神求留個聯系方式 :-D)。解決的方法為設置ajax為同步,此時拉取數據用的時間是在主線程隊列上,瀏覽器還是會認為submit()是用戶直接操作的結果。缺點就是異步優點的反面,如果網速不好可能會I/O阻塞,像alert()一樣整個程序停止,而失去了ajax的價值。

  

  PS:關於我的問題,由於時間的關系,最后的解決情況是后台改了返回數據接口,從返回完整的HTML變成了返回form表單及內部的代碼,然后我設置ajax為同步執行submit()方法。后來后台人員說干脆做一個中間頁面用於跳轉,前端ajax只要判斷一下data.status然后跳到付款頁面就好了,還能在一定程度上實現保密(用戶看不到提交的信息)。結果他一直很忙,提醒也還是很忙,然后就沒有然后了~~~

  估計邊學邊寫的肯定有很多問題,希望大蝦們多多提攜啊  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM