zoukankan      html  css  js  c++  java
  • javascript 跨文档调用技术

    这是一种很有前途的技术,可惜生不逢时,IE刚打垮网景,火狐又冒出来了。这种技术是我在设计超级数组对象时发现的。由于直接继承原生数组问题多,我只有用Object与Array的原型方法构建一个新类。不用说,效率不太理想。直到我已把超级数组臻至完美的今天,我还在不断寻找新的替代方案,于是我就想到用另一个文档的数组对象来构建。经测试,IE下是完美的。后来我又发现早在2006年全知全能的DE大神早已做了这样的探索,在《How To Subclass The JavaScript Array Object》中应用这种技术,我只不过是重新把它发挖出来而已。但是,以后就没有下文了,DE大神在他的Base2类库构建Array2对象时也没有应用这种技术。不明真相的我一头扎下去,哎呀!DE大神你真坏,有陷阱也不通报声!

    下面就拿DE大神的例子改一下,演示究竟出了什么状况?!

     window.onload = function(){
            //创建一个iframe
            var iframe = document.createElement("iframe");
            iframe.style.display = "none";
            document.body.appendChild(iframe);
            // 取得iframe文档的数组对象
            frames[frames.length - 1].document.write(
            "<script>parent.Array2 = Array;<\/script>");
            var a = Array2(1,2,3,4),
            b = a.slice(2)
            alert(b instanceof Array)//万恶的safari与firefox总是试图把沙箱文档的数组实例转化为本地文档的数组实例
            a.push(5);
            alert(a instanceof Array)//同上,safari与firefox受影响的方法是所有返回数组的方法……晕
          }
    

    为了方便,我把iframe中的文档对象的javascript环境中的数组对象称之为沙箱数组,意即,它不受本地数组对象影响。对,本地数组的原型进行扩展,不会殃及沙箱数组。俗话说,龙生龙,凤生风,老鼠的儿子会打洞!事实也应该如此!看,firefox与safari做了什么好事!沙箱数组变成原生数组了,我们在沙箱数组上做的扩展成了废物了!而且现在是linux大多数系统捆绑firefox,Mac捆绑safari,真不好办……

    别以为跨文档调用技术只能干这事,只是它的潜力还有待发掘而已。现在再解释一下上面那段用到的长长的定语。什么叫做“iframe中的文档对象的javascript环境……”?由于框架技术的发展,一个页面并不只有一个文档对象(在HTML5中,有关框架的标签只死剩iframe了)。最顶层的我们称之为本地文档,iframe与frame的文档我称之为沙箱文档。由于它们的相对独立性,人们最喜欢用iframe做富文本编辑器,省得调用document.execCommand( "BackColor", "", "red" )命令,整个页面都红了。不过,本地文档也好,iframe文档也好,它们都属于HTML文档。另,创建HTML文档也不单止iframe标签(frame标签我向来无视),还有ActiveXObject与createDocument,DOMParser等方法。

    ActiveXObject是指ActiveXObject("htmlfile"),谷歌的gtalk就是用它结合其他技术实现push的技术。ActiveXObject("htmlfile")创建的文档是一个完美的HTML文档,它拥有document.title,document.body等HTML DOM专有的属性,还能运行javascript。全局变量this就是我们要找的全局对象,它还拥有我们想要的一切,Array,Boolean,String,Date,Object等等。我们需要调用它们为我们做事,问题是标准浏览器能有像ctiveXObject("htmlfile")这样便捷的方法创建另一个文档吗?这些文档能拥有独立的javascript运行环境吗?这正是我们下面要讲的。

    w3c还真是实现了一些创建文档的方法,不过都比较偏~~~

    利用createHTMLDocument(title)创建文档

    title参数为tilte元素的innerText。

          window.onload = function(){
            try{    
              var doc = document.implementation.createHTMLDocument('跨文本调用技术 by 司徒正美');
              var html = doc.documentElement
              alert(html)        //测试是否存在HTML元素
              alert(html.tagName)//注意大小写,HTML文档会把元素节点的tagName与nodeName大写化,按理应该会返回“HTML”
              var body = doc.body
              alert(body);      //测试document.body
              if("title" in doc){
                alert(doc.title)//测试document.title
              }
              var head = doc.getElementsByTagName("head")[0]
              alert(head);
              if(head && "innerHTML" in head){
                alert("head.innerHTML = "+head.innerHTML)//测试innerHTML,HTML文档的元素节点都会支持这属性,及head与title是否存在套嵌关系
                alert("html.innerHTML = "+html.innerHTML)
              }
              if("outerHTML" in html){  //outerHTML也是HTML5的标准API
                alert("html.outerHTML = "+html.outerHTML)
              }
              var script = doc.createElement("script");
              html.insertBefore(script,null);
              script.appendChild(doc.createTextNode("alert('能调用javascript')"))//测试是否能动态解析脚本,注意这里是否弹出
              var xpath = doc.evaluate && doc.evaluate('//title', doc, null, 7, null);
              alert("测试xpath")
              alert(xpath && xpath.snapshotItem(0) )
            }catch(e){
              alert("不支持createHTMLDocument方法")
            }
          }
    

    IE6IE8FF3.6safari4.0 opera10.50chrome5.0
    能否创建文档 × × ×
    doc.documentElement为HTML标签 × × ×
    是否支持title属性 × × ×
    是否支持body属性 × × ×
    元素节点是否支持innerHTML × × ×
    元素节点是否支持outerHTML × × ×
    是否支持动态解析脚本 × × × × × ×
    是否支持xpath × × ×

    有关这方法更多的资料可点这里这里,很可能成为HTML5的标准API。

    利用createDocument(namespaceURI , qualified , doctype )创建文档

    第一个参数为字符串,指定命名空间,第二个参数为字符串,指定第一个元素的标签类型(tagName),第三个参数为对象,这个不用说吧,见字明义。

          window.onload = function(){
            try{
              var doc = document.implementation.createDocument(null, 'html', null);
              var html = doc.documentElement
              alert("doc.documentElement = "+html)        //测试是否存在HTML元素
              alert("html.tagName = "+html.tagName)//注意大小写,HTML文档会把元素节点的tagName与nodeName大写化,按理应该会返回“HTML”
              var body = doc.body
              alert("doc.body = "+body);      //测试document.body
              if("title" in doc){
                doc.title = "跨文档调用 by 司徒正美 "
                var title = doc.getElementsByTagName("title")[0]
                alert("title元素是否存在 = "+ title)
                alert("doc.title = "+doc.title)//测试document.titl
              }
              var head = doc.getElementsByTagName("head")[0]
              alert("是否有head元素 = " + head);
             if(head && "innerHTML" in head){
                alert("head.innerHTML = "+head.innerHTML)//测试innerHTML,HTML文档的元素节点都会支持这属性,及head与title是否存在套嵌关系
                alert("html.innerHTML = "+html.innerHTML)
              }
              if("outerHTML" in html){  //outerHTML也是HTML5的标准API
                alert("html.outerHTML = "+html.outerHTML)
              }
              var script = doc.createElement("script");
              html.insertBefore(script,null);
              script.appendChild(doc.createTextNode("alert('能调用javascript')"))//测试是否能动态解析脚本,注意这里是否弹出
              alert("测试xpath = "+ doc.evaluate )
            }catch(e){
              alert("不支持createDocument方法")
            }
          }
    

    IE6IE8firefox3.6safari4.0.4 opera10.50chrome5.0
    能否创建文档 × ×
    doc.documentElement为HTML标签 × ×
    是否支持title属性 × × × × × ×
    是否支持body属性 × × × × × ×
    元素节点是否支持innerHTML × × × × × ×
    元素节点是否支持outerHTML × × × × × ×
    是否支持动态解析脚本 × × × × × ×
    是否支持xpath × ×

    ▲是因为标准浏览器返回的是 [object Element],而非正确的[object HTMLHtmlElement],证明它生成的是XML文档。

    有关这方法更多的资料可点这里

    利用命名空间与createDocument创建文档

          window.onload = function(){
            try{
              var namespace = 'http://www.w3.org/1999/xhtml';
              var doc = document.implementation.createDocument(namespace, 'html', null);
              var html = doc.documentElement
              alert("doc.documentElement = "+html)        //测试是否存在HTML元素
              alert("html.tagName = "+html.tagName)//注意大小写,HTML文档会把元素节点的tagName与nodeName大写化,按理应该会返回“HTML”
              if("title" in doc){
                doc.title = "跨文档调用 by 司徒正美 "
                var title = doc.getElementsByTagName("title")[0]
                if(!!title){
                  alert("本来就存在title元素 "+ +doc.title)//测试document.title
                }else{
                  alert("开始创建元素")
                  try{
                    html.innerHTML = "<head><title>由innerHTML创建的title</title></head><body></body>"
                  }catch(e){
                    var head = doc.createElement("head");
                    html.appendChild(head);
                    var title = doc.createElement("title");
                    head.appendChild(title);
                    title.appendChild(doc.createTextNode("由DOM API创建的title"));
                    var body = doc.createElement("body");
                    html.appendChild(body);
                  }
                  alert("doc.title = "+ doc.title)
                }
              }
              if("innerHTML" in html){
                alert("html.innerHTML = "+html.innerHTML)
              }
              if("outerHTML" in html){
                alert("html.outerHTML = "+html.outerHTML)
              }
              alert("doc.body = "+doc.body)
              var script = doc.createElement("script");
              html.insertBefore(script,null);
              script.appendChild(doc.createTextNode("alert('能调用javascript')"))//测试是否能动态解析脚本,注意这里是否弹出
              alert("测试xpath = "+ doc.evaluate )
            }catch(e){
              alert("不支持createHTMLDocument方法")
            }
          }
    

    IE6IE8firefox3.6safari4.0.4 opera10.50chrome5.0
    能否创建文档 × ×
    doc.documentElement为HTML标签 × ×
    是否支持title属性 × × ×
    是否支持body属性 × × × × ×
    元素节点是否支持innerHTML × ×
    元素节点是否支持outerHTML × × ×
    是否支持动态解析脚本 × × × × × ×
    是否支持xpath × ×

    有几点需要注意的:

    • 上述方法创建的是只有一个元素节点的HTML文档,由于是使用XHTML的命名空间,因此准确来说是XTHML文档。
    • XHTML的元素节点的tagName与nodeName是区分大小写的。
    • XHTML文档是没有实现body属性,opera的情况是特殊。

    利用文档类型与createDocument创建文档

     window.onload = function(){
            try{
              var doctype = document.implementation.createDocumentType('html',
              '-//W3C//DTD HTML 4.01//EN', 'http://www.w3.org/TR/html4/strict.dtd');
              var doc = document.implementation.createDocument(null, 'html', doctype);
              var html = doc.documentElement
              alert("doc.documentElement = "+html)        //测试是否存在HTML元素
              alert("html.tagName = "+html.tagName)//注意大小写,HTML文档会把元素节点的tagName与nodeName大写化,按理应该会返回“HTML”
              if("title" in doc){
                doc.title = "跨文档调用 by 司徒正美 "
                var title = doc.getElementsByTagName("title")[0]
                if(!!title){
                  alert("本来就存在title元素 "+ +doc.title)//测试document.title
                }else{
                  alert("开始创建元素")
                  try{
                    if("innerHTML" in html)
                      html.innerHTML = "<head><title>由innerHTML创建的title</title></head><body></body>"
                  }catch(e){
                    var head = doc.createElement("head");
                    html.appendChild(head);
                    var title = doc.createElement("title");
                    head.appendChild(title);
                    title.appendChild(doc.createTextNode("由DOM API创建的title"));
                    var body = doc.createElement("body");
                    html.appendChild(body);
                  }
                  alert("doc.title = "+ doc.title)
                }
              }
              var head = document.getElementsByName("head")[0];
              alert("head = "+head)
              if("innerHTML" in html){
                alert("html.innerHTML = "+html.innerHTML)
              }
              if("outerHTML" in html){
                alert("html.outerHTML = "+html.outerHTML)
              }
              alert("doc.body = "+doc.body)
              var script = doc.createElement("script");
              html.insertBefore(script,null);
              script.appendChild(doc.createTextNode("alert('能调用javascript')"))//测试是否能动态解析脚本,注意这里是否弹出
              alert("测试xpath = "+ doc.evaluate )
            }catch(e){
              alert("不支持createHTMLDocument方法")
            }
          }
    

    IE6IE8firefox3.6safari4.0.4 opera10.50chrome5.0
    能否创建文档 × ×
    doc.documentElement为HTML标签 × ×
    是否支持title属性 × × × × × ×
    是否支持body属性 × × × × × ×
    元素节点是否支持innerHTML × × × × × ×
    元素节点是否支持outerHTML × × × × × ×
    是否支持动态解析脚本 × × × × × ×
    是否支持xpath × ×

    利用document.cloneNode(true)创建文档

          window.onload = function(){
            try{
              var doc = document.cloneNode(true);
              var html = doc.documentElement
              alert("doc.documentElement = "+html)        //测试是否存在HTML元素
              alert("html.tagName = "+html.tagName);
              alert("html.html = "+html.innerHTML)
              if(doc.title){
                alert("doc.title = "+doc.title);
              }else{
                doc.title = "司徒正美";
                var title = doc.getElementsByTagName("title")[0];
                alert("title.innerHTML = "+title.innerHTML);
              }
              alert("doc.body = "+doc.body)
              var script = doc.createElement("script");
              doc.body.appendChild(script);
              if(!+"\v1"){
                script.text = "alert('能调用javascript')"
              }else{
                script.appendChild(doc.createTextNode("alert('能调用javascript')"));
              }
              alert("测试xpath = "+ doc.evaluate )
            }catch(e){
              alert("不支持document.cloneNode(true)方法")
            }
          }
    

    IE6IE8firefox3.6safari4.0.4 opera10.50chrome5.0
    能否创建文档 × × ×
    doc.documentElement为HTML标签 × × ×
    是否支持title属性 × × ×
    是否支持body属性 × × ×
    元素节点是否支持innerHTML × × ×
    元素节点是否支持outerHTML × × × ×
    是否支持动态解析脚本 × × × ×
    是否支持xpath × × × × ×

    注意:IE下,document.cloneNode(true)不能复制title元素的innerHTML!

    到目前,我们发现w3c实现的那一堆方法实在太屎了,竟然不支持创建另一个javascript运行环境。因此想得到拥有javascript运行环境的文档对象,我们还是得靠iframe与ActiveXObject("htmlfile")!

    /*
    get another HTMLDocument which can run javascript!
    Copyright 2010
    Dual licensed under the MIT or GPL Version 2 licenses.
    author "司徒正美(cheng)"
    http://www.cnblogs.com/rubylouvre/
     */
          (function(global) {
            var expando = '__dom__' + (new Date - 0),
            mode = (function(){
              if (global.ActiveXObject && global.location && global.location.protocol !== 'file:') {
                try {
                  return new ActiveXObject('htmlfile') && 1;
                } catch (e) {}
              }
              return 2;
            })(),
            getSandboxDocument = (function(){
              // 获取另一个全局对象
              if (mode === 1){
                return function() {
                  var htmlfile = new ActiveXObject('htmlfile');
                  htmlfile.open();
                  htmlfile.write('<script>this.author ="司徒正美";document.global = this;<\/script>');
                  htmlfile.close();
                  return htmlfile.global;
                };
              }else  if (mode === 2){
                return function() {
                  var idoc, iframe, result,
                  doc = global.document,
                  parentNode = doc.body || doc.documentElement,
                  name  = '__iframe__' + expando  ;
                  try {
                    iframe = doc.createElement('<iframe name="' + name + '">');
                  } catch (e) {
                    (iframe = doc.createElement('iframe')).name = name;
                  }
                  iframe.style.display = 'none';
                  parentNode.insertBefore(iframe, parentNode.firstChild);
                  try {
                    (idoc = global.frames[name].document).open();
                    var str = '<html><head><title>iframe</title><script>this.author ="司徒正美";parent.' +
                      expando + ' = this;<\/script></head><body></body></html>'
                    idoc.write(str);
                    idoc.close();
                  } catch (e) {
                    //opera9不支持在document.documentElement中插入iframe
                    throw new Error('Creating a sandbox by iframe is fail.');
                  }
                  result = global[expando];
                  try{
                    delete global[expando]
                  }catch(e){//IE下失败!
                    global[expando] = undefined
                  }
                  return result;
                }
              }else{
                return function() {
                  throw new Error('Creating a sandbox is fail.');
                };
              }
            })();
            global.getSandboxDocument = getSandboxDocument
          })(this);
    

    IE6IE8firefox3.6safari4.0.4 opera10.50chrome5.0
    能否创建文档
    doc.documentElement为HTML标签
    是否支持title属性
    是否支持body属性
    元素节点是否支持innerHTML
    元素节点是否支持outerHTML ×
    是否支持动态解析脚本
    是否支持xpath × ×

    这种技术我算毫无保留地公开出来了,如果有谁知道怎样利用它创建一个不污染本地数组的数组类,也请不吝赐教!

    2ch

  • 相关阅读:
    基于SPA的网页授权流程(微信OAuth2)
    用CSS3制作尖角标签按钮样式
    关于WebAPI跨域踩到的一点坑
    .net webapi跨域方法整理
    使用 JavaScript 截屏
    关于anguar2微信支付问题
    sql操作语句
    mysql5.7初始化密码报错 ERROR 1820 (HY000): You must reset your password using ALTER USER statement before
    linux上安装mysql5.7
    git 常用命令
  • 原文地址:https://www.cnblogs.com/rubylouvre/p/1683400.html
Copyright © 2011-2022 走看看