【问题标题】:jquery - iframe access denied in IE on some pagesjquery - 在某些页面上的 IE 中拒绝 iframe 访问
【发布时间】:2013-01-30 12:18:07
【问题描述】:

我是 printThis 的作者,一个用于打印的 jquery 插件。

https://github.com/jasonday/printThis

我有一个用户提出了一个我无法破解的问题,很遗憾,我无法共享该页面(隐私问题)。

在用户的网站上,问题出现在 IE 的某些页面上,但在其他页面上没有。打印失败,因为 iframe 仍然为空。

IE中的错误在jQuery内部:

contents: function (a) {
            return f.nodeName(a,
                "iframe") ? a.contentDocument || a.contentWindow.document : f.makeArray(a.childNodes)
        }

使用日志记录,我能够确定它在这条线上失败了:

var $doc = $("#" + strFrameName).contents();

但同样,这只发生在某些页面上,我无法在此用户站点之外的任何实例中重新创建。

我的问题:这里有更好的方法吗?还是让$doc 对象更防弹的方法?


// -----------------------------------------------------------------------
// printThis v1.1
// Printing plug-in for jQuery
//
// Resources (based on) :
//              jPrintArea: http://plugins.jquery.com/project/jPrintArea
//              jqPrint: https://github.com/permanenttourist/jquery.jqprint
//              Ben Nadal: http://www.bennadel.com/blog/1591-Ask-Ben-Print-Part-Of-A-Web-Page-With-jQuery.htm
//
// Dual licensed under the MIT and GPL licenses:
//              http://www.opensource.org/licenses/mit-license.php
//              http://www.gnu.org/licenses/gpl.html
//
// (c) Jason Day 2012
//
// Usage:
//
// $("#mySelector").printThis({
//      debug: false, //show the iframe for debugging
//      importCSS: true, // import page CSS
//      printContainer: true, // grab outer container as well as the contents of the selector
//      loadCSS: "path/to/my.css" //path to additional css file
//  });
//
// Notes:
//  - the loadCSS option does not need @media print
//------------------------------------------------------------------------

(function($) {
    var opt;

    $.fn.printThis = function (options) {
        opt = $.extend({}, $.fn.printThis.defaults, options);

        var $element = (this instanceof jQuery) ? this : $(this);

    // if Opera, open a new tab
        if ($.browser.opera)
        {
            var tab = window.open("","Print Preview");
            tab.document.open();


        }
    // add dynamic iframe to DOM
        else
        {
        var strFrameName = ("printThis-" + (new Date()).getTime());

            var $iframe = $("<iframe id='" + strFrameName +"' src='about:blank'/>");

            if (!opt.debug) { $iframe.css({ position: "absolute", width: "0px", height: "0px", left: "-600px", top: "-600px" }); }

            $iframe.appendTo("body");

        }
    // allow iframe to fully render before action
    setTimeout ( function () {

        if ($.browser.opera)
            {
        var $doc = tab.document;
        } else
        {
        var $doc = $("#" + strFrameName).contents();
        }



        // import page css
        if (opt.importCSS)
        {
                $("link[rel=stylesheet]").each(function(){
                var href = $(this).attr('href');
                if(href){
                        var media = $(this).attr('media') || 'all';
                        $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + href + "' media='"+media+"'>");
                    }
        });
        }

        // add another stylesheet
        if (opt.loadCSS)
        {
        $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + opt.loadCSS + "'>");

        }

        //add title of the page
        if (opt.titlePage)
        {
        $doc.find("head").append('<title>'+opt.titlePage+'</title>');
        } 
        //grab outer container
        if (opt.printContainer) { $doc.find("body").append($element.outer()); }
        else { $element.each( function() { $doc.find("body").append($(this).html()); }); }

        //$doc.close();
        // print
        ($.browser.opera ? tab : $iframe[0].contentWindow).focus();
        setTimeout( function() { ($.browser.opera ? tab : $iframe[0].contentWindow).print(); if (tab) { tab.close(); } }, 1000);

        //removed iframe after 60 seconds
        setTimeout(
        function(){
        $iframe.remove();
        },
        (60 * 1000)
        );
    }, 333 );
    }


    $.fn.printThis.defaults = {
        debug: false, //show the iframe for debugging
        importCSS: true, // import page CSS
        printContainer: true, // grab outer container as well as the contents of the selector
        loadCSS: "", //path to additional css file
        titlePage: "" //add title to print page
    };


    jQuery.fn.outer = function() {
      return $($('<div></div>').html(this.clone())).html();
    }
})(jQuery);

更新

由于document.domain引起的问题

这种类型的页面设置了document.domain,IE 不会从父级继承document.domain

为了修复该部分,我将 iframe 创建更改为标准 javascript,并将源设置为在 iframe 创建时写入 document.domain

    var printI= document.createElement('iframe');

    printI.name = "printIframe";

    printI.id = strFrameName;

    document.body.appendChild(printI);

    printI.src = "javascript:document.write('<head><script>document.domain=\"mydomain.com\";</script></head><body></body>')";


   var $iframe = $("#" + strFrameName);

所以这修复了访问被拒绝的问题,但是现在框架不会打印。我已经尝试了很多不同的方法来访问该对象,但是它们都不起作用。

A)在这种情况下,您将如何访问框架(我已经尝试过 SO 中列出的大多数方法)以使 IE 识别和打印

B) 谁能想到一个更好的方法来让 document.domain 在使用 jQuery 创建时进入 iframe? (以后不能,因为会出现访问被拒绝的问题)

【问题讨论】:

  • 所有页面是否都在同一个域/子域中?
  • @Christophe - 是的。所有页面都在同一个域中。打印不是专门在一种类型的页面上工作,而是在所有其他页面上工作。
  • 还检查您的脚本用户是否使用相同版本的 jQuery,有时不同版本的 jQuery 会破坏,当我尝试将我的 jQuery 升级到最新版本时,我遇到过这种情况,并且很少有事情会崩溃,所以我只是恢复到旧的
  • @am05mhz - 不幸的是,它不是 jquery 版本。我已经测试了回到 1.4.2 的所有版本

标签: jquery iframe printing printthis


【解决方案1】:

这个答案已经在原始问题 UPDATE 中说明,但我想为与绕过 SCRIPT70 Permission denied 错误相关的原始问题添加一个更简洁的答案(我在 IE11/Win7 上使用 JQuery 3.2.1 遇到了这个问题)。

而不是$('&lt;iframe .../&gt;').appendTo($('body'))

这样做:

var $iframe = $('<iframe .../>');
document.body.appendChild($iframe[0]);

答案取自这里:https://bugs.jquery.com/ticket/13936#comment:28

【讨论】:

    【解决方案2】:

    IE 与所有其他浏览器一样与 iframe 一起使用(至少对于主要功能而言)。你只需要遵守一套规则:

    • 在 iframe(需要了解 iframe 父级的 js 部分)中加载任何 javascript 之前,请确保父级已更改 document.domain。
    • 加载所有 iframe 资源后,将 document.domain 更改为与 parent 中定义的相同。 (需要稍后再做,因为设置域会导致 iframe 资源请求失败)

    • 现在您可以对父窗口进行引用:var winn = window.parent

    • 现在您可以引用父 HTML,以便对其进行操作: var parentContent = $('html', winn.document)
    • 此时您应该可以访问 IE 父窗口/文档,并且可以随意更改它

    【讨论】:

    • 请不要将相同的answer 复制/粘贴到多个问题中。相反,每个答案都应该针对所提出的特定问题进行定制,而不是通用的复制和粘贴。
    【解决方案3】:

    问题是由于 IE 没有继承父 document.domain。

    不幸的是,一旦您进入这个阴暗的区域,就需要一些特定的技巧才能使其正常工作。

    基本上检查是否显式设置了 document.domain 并且浏览器是 IE。

    完全更新的插件:

    https://github.com/jasonday/printThis

    (function ($) {
        var opt;
        $.fn.printThis = function (options) {
            opt = $.extend({}, $.fn.printThis.defaults, options);
            var $element = this instanceof jQuery ? this : $(this);
    
                var strFrameName = "printThis-" + (new Date()).getTime();
    
                if(window.location.hostname !== document.domain && navigator.userAgent.match(/msie/i)){
                    // Ugly IE hacks due to IE not inheriting document.domain from parent
                    // checks if document.domain is set by comparing the host name against document.domain
                    var iframeSrc = "javascript:document.write(\"<head><script>document.domain=\\\"" + document.domain + "\\\";</script></head><body></body>\")";
                    var printI= document.createElement('iframe');
                    printI.name = "printIframe";
                    printI.id = strFrameName;
                    printI.className = "MSIE";
                    document.body.appendChild(printI);
                    printI.src = iframeSrc;
    
                } else {
                     // other browsers inherit document.domain, and IE works if document.domain is not explicitly set
                    var $frame = $("<iframe id='" + strFrameName +"' name='printIframe' />");
                    $frame.appendTo("body");
                }
    
    
                var $iframe = $("#" + strFrameName);
    
                // show frame if in debug mode
                if (!opt.debug) $iframe.css({
                    position: "absolute",
                    width: "0px",
                    height: "0px",
                    left: "-600px",
                    top: "-600px"
                });
    
    
            // $iframe.ready() and $iframe.load were inconsistent between browsers    
            setTimeout ( function () {
    
                var $doc = $iframe.contents();
    
                // import page stylesheets
                if (opt.importCSS) $("link[rel=stylesheet]").each(function () {
                    var href = $(this).attr("href");
                    if (href) {
                        var media = $(this).attr("media") || "all";
                        $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + href + "' media='" + media + "'>")
                    }
                });
    
                //add title to iframe
                if (opt.pageTitle) $doc.find("head").append("<title>" + opt.pageTitle + "</title>");
    
                // import additional stylesheet
                if (opt.loadCSS) $doc.find("head").append("<link type='text/css' rel='stylesheet' href='" + opt.loadCSS + "'>");
    
                // grab $.selector as container
                if (opt.printContainer) $doc.find("body").append($element.outer());
    
                // otherwise just print interior elements of container
                else $element.each(function () {
                    $doc.find("body").append($(this).html())
                });
    
                if($iframe.hasClass("MSIE")){
                    // check if the iframe was created with the ugly hack
                    // and perform another ugly hack out of neccessity
                    window.frames["printIframe"].focus();
                    setTimeout(function () {
                       $doc.find("head").append("<script>  window.print(); </script>");
                    }, 500 );
                } else {
                    // proper method
                    $iframe[0].contentWindow.focus();
                    $iframe[0].contentWindow.print();  
                }
    
                 //remove iframe after print
                if (!opt.debug) {
                    setTimeout(function () {
                        $iframe.remove();
                    }, 1000);
                }
    
    
            }, 333 );
    
        };
    
        // defaults
        $.fn.printThis.defaults = {
            debug: false,           // show the iframe for debugging
            importCSS: true,        // import parent page css
            printContainer: true,   // print outer container/$.selector
            loadCSS: "",            // load an additional css file
            pageTitle: ""           // add title to print page
        };
    
        // $.selector container
        jQuery.fn.outer = function () {
            return $($("<div></div>").html(this.clone())).html()
        }
    })(jQuery);
    

    【讨论】:

      【解决方案4】:

      在您的代码中,您使用 setTimeout 在 iframe 加载后执行您的函数。

      // allow iframe to fully render before action
      setTimeout ( function () {
      ...
      }, 333 );  //333ms
      

      但这是一个错误,因为您不知道给定的时间是否足以加载 iframe。 Javascript 执行是异步的,因此无法保证setTimeout 会在 iframe 加载之前抵消函数的执行。由于不同页面的加载时间不同。有些无法正确执行代码,指向您发现导致错误的行。

      var $doc = $("#" + strFrameName).contents();  //only after loading
      

      正确的方法是使用事件loadonload来了解DOM对象是否加载正确。

      <script>
      document.getElementById("myframe").onload = function() {
        alert("myframe is loaded");
      };
      </script>
      //or
      <iframe id="myFrame" onload="myFunction();"></iframe>
      

      【讨论】:

      【解决方案5】:

      只要您设置 iframe src,就必须针对父元素验证相同的来源,即使您将其设置为 'about:blank'。我猜 IE 无法正确检查,或者一些 javascript 运行并将 document.location 设置为与创建 iframe 不同的位置。

      像下面这样不设置 src 怎么样?它仍然应该工作。

      var $iframe = $("<iframe id='" + strFrameName +"'/>");
      $iframe.appendTo("body");
      var $iframeDoc = $iframe[0].contentWindow.document;
      
      $iframeDoc.open();
      $iframeDoc.write("foo");
      $iframeDoc.close();
      

      【讨论】:

      • 这帮助我解决了一个类似的问题 - 在我执行 $('iframe')[0].contentWindow.document.open() 之前,我无法获得 iframe 正文元素。跨度>
      猜你喜欢
      • 1970-01-01
      • 2016-02-23
      • 1970-01-01
      • 1970-01-01
      • 2011-07-02
      • 1970-01-01
      • 2012-05-15
      • 1970-01-01
      • 2013-08-07
      相关资源
      最近更新 更多