js 页面间的通信

  看了一下公司原来的代码,原页面ajax post返回一个页面完整的HTML,然后再打开一个新页面并输出ajax返回的所有代码到新页面上,在新页面上以表单提交的形式实现重定向。

  任凭我想了半天也没想出来,怎么样不借助node就直接用js生成新页面并输入数据到新页面上以初始化。然后百度,必应搜索(公司电脑安全设置不能用greenshadowFQ,郁闷!),关键词不对,没搜到想要的结果。趁着面试,问了一下考官,关键词是“页面之间的通信”,而且说是有三种方法!这不,折腾了几天,算是小有成果,且把相关的东西从头到尾理一下,一起分享。

  目录

  一.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然后跳到付款页面就好了,还能在一定程度上实现保密(用户看不到提交的信息)。结果他一直很忙,提醒也还是很忙,然后就没有然后了~~~

  估计边学边写的肯定有很多问题,希望大虾们多多提携啊  

posted @ 2015-07-29 19:12  codetker  阅读(24782)  评论(3编辑  收藏  举报